galaxy-commits
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
December 2013
- 1 participants
- 207 discussions
commit/galaxy-central: greg: Add a tool shed container to display successful repository and tool dependency installations and improve statistics and reporting in the tool shed's install and test runs.
by commits-noreply@bitbucket.org 19 Dec '13
by commits-noreply@bitbucket.org 19 Dec '13
19 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/a0e80a712c37/
Changeset: a0e80a712c37
User: greg
Date: 2013-12-19 18:49:50
Summary: Add a tool shed container to display successful repository and tool dependency installations and improve statistics and reporting in the tool shed's install and test runs.
Affected #: 8 files
diff -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b -r a0e80a712c37754d2d33260919469a7dccb2f1b1 lib/galaxy/model/tool_shed_install/__init__.py
--- a/lib/galaxy/model/tool_shed_install/__init__.py
+++ b/lib/galaxy/model/tool_shed_install/__init__.py
@@ -265,8 +265,7 @@
"""Return the repository's tool dependencies that are currently installed, but possibly in an error state."""
installed_dependencies = []
for tool_dependency in self.tool_dependencies:
- if tool_dependency.status in [ ToolDependency.installation_status.INSTALLED,
- ToolDependency.installation_status.ERROR ]:
+ if tool_dependency.status in [ ToolDependency.installation_status.INSTALLED ]:
installed_dependencies.append( tool_dependency )
return installed_dependencies
@@ -442,6 +441,16 @@
return dependencies_being_installed
@property
+ def tool_dependencies_installed_or_in_error( self ):
+ """Return the repository's tool dependencies that are currently installed, but possibly in an error state."""
+ installed_dependencies = []
+ for tool_dependency in self.tool_dependencies:
+ if tool_dependency.status in [ ToolDependency.installation_status.INSTALLED,
+ ToolDependency.installation_status.ERROR ]:
+ installed_dependencies.append( tool_dependency )
+ return installed_dependencies
+
+ @property
def tool_dependencies_missing_or_being_installed( self ):
dependencies_missing_or_being_installed = []
for tool_dependency in self.tool_dependencies:
diff -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b -r a0e80a712c37754d2d33260919469a7dccb2f1b1 lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -2733,7 +2733,7 @@
def build_dependency_shell_commands( self ):
"""Return a list of commands to be run to populate the current environment to include this tools requirements."""
if self.tool_shed_repository:
- installed_tool_dependencies = self.tool_shed_repository.installed_tool_dependencies
+ installed_tool_dependencies = self.tool_shed_repository.tool_dependencies_installed_or_in_error
else:
installed_tool_dependencies = None
return self.app.toolbox.dependency_manager.dependency_shell_commands( self.requirements,
diff -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b -r a0e80a712c37754d2d33260919469a7dccb2f1b1 lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
--- a/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
+++ b/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
@@ -255,7 +255,7 @@
tool_shed_repository.uninstalled = True
# Remove all installed tool dependencies and tool dependencies stuck in the INSTALLING state, but don't touch any
# repository dependencies.
- tool_dependencies_to_uninstall = tool_shed_repository.installed_tool_dependencies
+ tool_dependencies_to_uninstall = tool_shed_repository.tool_dependencies_installed_or_in_error
tool_dependencies_to_uninstall.extend( tool_shed_repository.tool_dependencies_being_installed )
for tool_dependency in tool_dependencies_to_uninstall:
uninstalled, error_message = tool_dependency_util.remove_tool_dependency( trans.app, tool_dependency )
diff -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b -r a0e80a712c37754d2d33260919469a7dccb2f1b1 lib/tool_shed/util/container_util.py
--- a/lib/tool_shed/util/container_util.py
+++ b/lib/tool_shed/util/container_util.py
@@ -21,26 +21,29 @@
self.key = key
self.label = label
self.parent = parent
+ self.current_repository_installation_errors = []
+ self.current_repository_successful_installations = []
self.description = None
self.datatypes = []
+ self.failed_tests = []
self.folders = []
+ self.invalid_data_managers = []
self.invalid_repository_dependencies = []
self.invalid_tool_dependencies = []
self.invalid_tools = []
- self.current_repository_installation_errors = []
- self.repository_installation_errors = []
- self.tool_dependency_installation_errors = []
- self.valid_tools = []
- self.valid_data_managers = []
- self.invalid_data_managers = []
- self.tool_dependencies = []
- self.failed_tests = []
self.missing_test_components = []
self.not_tested = []
self.passed_tests = []
+ self.readme_files = []
+ self.repository_dependencies = []
+ self.repository_installation_errors = []
+ self.repository_successful_installations = []
self.test_environments = []
- self.repository_dependencies = []
- self.readme_files = []
+ self.tool_dependencies = []
+ self.tool_dependency_installation_errors = []
+ self.tool_dependency_successful_installations = []
+ self.valid_tools = []
+ self.valid_data_managers = []
self.workflows = []
def contains_folder( self, folder ):
@@ -230,6 +233,17 @@
self.error_message = error_message
+class RepositorySuccessfulInstallation( object ):
+ """Repository installation object"""
+
+ def __init__( self, id=None, tool_shed=None, name=None, owner=None, changeset_revision=None ):
+ self.id = id
+ self.tool_shed = tool_shed
+ self.name = name
+ self.owner = owner
+ self.changeset_revision = changeset_revision
+
+
class TestEnvironment( object ):
"""Tool test environment object"""
@@ -294,6 +308,16 @@
self.error_message = error_message
+class ToolDependencySuccessfulInstallation( object ):
+ """Tool dependency installation object"""
+
+ def __init__( self, id=None, type=None, name=None, version=None, installation_directory=None ):
+ self.id = id
+ self.type = type
+ self.name = name
+ self.version = version
+ self.installation_directory = installation_directory
+
class Workflow( object ):
"""Workflow object."""
@@ -1097,7 +1121,8 @@
# {'python_version': '2.7.4', 'tool_shed_mercurial_version': '2.2.3', 'system': 'Linux 3.8.0-30-generic',
# 'tool_shed_database_version': 21, 'architecture': 'x86_64', 'galaxy_revision': '11573:a62c54ddbe2a',
# 'galaxy_database_version': 117, 'time_tested': '2013-12-03 09:11:48', 'tool_shed_revision': '11556:228156daa575'},
- # 'installation_errors': {'current_repository': [], 'repository_dependencies': [], 'tool_dependencies': []}
+ # 'installation_errors': {'current_repository': [], 'repository_dependencies': [], 'tool_dependencies': []},
+ # 'successful_installations': {'current_repository': [], 'repository_dependencies': [], 'tool_dependencies': []}
# }
test_environment_dict = tool_test_results_dict.get( 'test_environment', None )
if test_environment_dict is None:
@@ -1335,6 +1360,82 @@
version=td_version,
error_message=td_error_message )
tool_dependencies_folder.tool_dependency_installation_errors.append( tool_dependency_installation_error )
+ successful_installation_dict = tool_test_results_dict.get( 'successful_installations', {} )
+ if len( successful_installation_dict ) > 0:
+ # 'successful_installation':
+ # {'current_repository': [],
+ # 'repository_dependencies': [],
+ # 'tool_dependencies':
+ # [{'installation_directory': 'some path' 'type': 'package', 'name': 'MIRA', 'version': '4.0'}]
+ # }
+ current_repository_successful_installation_dicts = successful_installation_dict.get( 'current_repository', [] )
+ repository_dependency_successful_installation_dicts = successful_installation_dict.get( 'repository_dependencies', [] )
+ tool_dependency_successful_installation_dicts = successful_installation_dict.get( 'tool_dependencies', [] )
+ if len( current_repository_successful_installation_dicts ) > 0 or \
+ len( repository_dependency_successful_installation_dicts ) > 0 or \
+ len( tool_dependency_successful_installation_dicts ) > 0:
+ repository_installation_success_id = 0
+ folder_id += 1
+ successful_installation_base_folder = Folder( id=folder_id,
+ key='successful_installations',
+ label='Successful installations',
+ parent=containing_folder )
+ containing_folder.folders.append( successful_installation_base_folder )
+ # Displaying the successful installation of the current repository is not really necessary, so we'll skip it.
+ if len( repository_dependency_successful_installation_dicts ) > 0:
+ folder_id += 1
+ repository_dependencies_folder = Folder( id=folder_id,
+ key='repository_dependency_successful_installations',
+ label='Repository dependencies',
+ parent=successful_installation_base_folder )
+ successful_installation_base_folder.folders.append( repository_dependencies_folder )
+ for repository_dependency_successful_installation_dict in repository_dependency_successful_installation_dicts:
+ repository_installation_success_id += 1
+ try:
+ rd_tool_shed = str( repository_dependency_successful_installation_dict.get( 'tool_shed', '' ) )
+ rd_name = str( repository_dependency_successful_installation_dict.get( 'name', '' ) )
+ rd_owner = str( repository_dependency_successful_installation_dict.get( 'owner', '' ) )
+ rd_changeset_revision = str( repository_dependency_successful_installation_dict.get( 'changeset_revision', '' ) )
+ except Exception, e:
+ rd_tool_shed = 'unknown'
+ rd_name = 'unknown'
+ rd_owner = 'unknown'
+ rd_changeset_revision = 'unknown'
+ repository_installation_success = \
+ RepositoryInstallationSuccess( id=repository_installation_success_id,
+ tool_shed=rd_tool_shed,
+ name=rd_name,
+ owner=rd_owner,
+ changeset_revision=rd_changeset_revision )
+ repository_dependencies_folder.repository_successful_installations.append( repository_installation_success )
+ if len( tool_dependency_successful_installation_dicts ) > 0:
+ # [{'installation_directory': 'some path' 'type': 'package', 'name': 'MIRA', 'version': '4.0'}]
+ folder_id += 1
+ tool_dependencies_folder = Folder( id=folder_id,
+ key='tool_dependency_successful_installations',
+ label='Tool dependencies',
+ parent=successful_installation_base_folder )
+ successful_installation_base_folder.folders.append( tool_dependencies_folder )
+ tool_dependency_error_id = 0
+ for tool_dependency_successful_installation_dict in tool_dependency_successful_installation_dicts:
+ tool_dependency_error_id += 1
+ try:
+ td_type = str( tool_dependency_successful_installation_dict.get( 'type', '' ) )
+ td_name = str( tool_dependency_successful_installation_dict.get( 'name', '' ) )
+ td_version = str( tool_dependency_successful_installation_dict.get( 'version', '' ) )
+ td_installation_directory = tool_dependency_successful_installation_dict.get( 'installation_directory', '' )
+ except Exception, e:
+ td_type = 'unknown'
+ td_name = 'unknown'
+ td_version = 'unknown'
+ td_installation_directory = str( e )
+ tool_dependency_successful_installation = \
+ ToolDependencySuccessfulInstallation( id=tool_dependency_error_id,
+ type=td_type,
+ name=td_name,
+ version=td_version,
+ installation_directory=td_installation_directory )
+ tool_dependencies_folder.tool_dependency_successful_installations.append( tool_dependency_successful_installation )
else:
tool_test_results_root_folder = None
return folder_id, tool_test_results_root_folder
diff -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b -r a0e80a712c37754d2d33260919469a7dccb2f1b1 templates/webapps/tool_shed/repository/common.mako
--- a/templates/webapps/tool_shed/repository/common.mako
+++ b/templates/webapps/tool_shed/repository/common.mako
@@ -396,17 +396,27 @@
%for tool_dependency_installation_error in folder.tool_dependency_installation_errors:
${render_tool_dependency_installation_error( tool_dependency_installation_error, pad, my_row, row_counter )}
%endfor
- %endif
+ %endif
+ %if folder.tool_dependency_successful_installations:
+ %for tool_dependency_successful_installation in folder.tool_dependency_successful_installations:
+ ${render_tool_dependency_successful_installation( tool_dependency_successful_installation, pad, my_row, row_counter )}
+ %endfor
+ %endif
%if folder.repository_installation_errors:
%for repository_installation_error in folder.repository_installation_errors:
${render_repository_installation_error( repository_installation_error, pad, my_row, row_counter, is_current_repository=False )}
%endfor
- %endif
+ %endif
%if folder.current_repository_installation_errors:
%for repository_installation_error in folder.current_repository_installation_errors:
${render_repository_installation_error( repository_installation_error, pad, my_row, row_counter, is_current_repository=True )}
%endfor
- %endif
+ %endif
+ %if folder.repository_successful_installations:
+ %for repository_successful_installation in folder.repository_successful_installations:
+ ${render_repository_successful_installation( repository_successful_installation, pad, my_row, row_counter )}
+ %endfor
+ %endif
</%def><%def name="render_datatype( datatype, pad, parent, row_counter, row_is_header=False )">
@@ -714,6 +724,36 @@
%></%def>
+<%def name="render_tool_dependency_successful_installation( successful_installation, pad, parent, row_counter, row_is_header=False )">
+ <%
+ encoded_id = trans.security.encode_id( successful_installation.id )
+ %>
+ <tr class="datasetRow"
+ %if parent is not None:
+ parent="${parent}"
+ %endif
+ id="libraryItem-rtdsi-${encoded_id}">
+ <td style="padding-left: ${pad+20}px;">
+ <table id="test_environment">
+ <tr bgcolor="#FFFFCC">
+ <th>Type</th><th>Name</th><th>Version</th>
+ </tr>
+ <tr>
+ <td>${successful_installation.name | h}</td>
+ <td>${successful_installation.type | h}</td>
+ <td>${successful_installation.version | h}</td>
+ </tr>
+ <tr><th>Installation directory</th></tr>
+ <tr><td colspan="3">${successful_installation.installation_directory | h}</td></tr>
+ </table>
+ </td>
+ </tr>
+ <%
+ my_row = row_counter.count
+ row_counter.increment()
+ %>
+</%def>
+
<%def name="render_repository_installation_error( installation_error, pad, parent, row_counter, row_is_header=False, is_current_repository=False )"><%
from galaxy.util import unicodify
@@ -748,6 +788,37 @@
%></%def>
+<%def name="render_repository_successful_installation( successful_installation, pad, parent, row_counter, row_is_header=False, is_current_repository=False )">
+ <%
+ encoded_id = trans.security.encode_id( successful_installation.id )
+ %>
+ <tr class="datasetRow"
+ %if parent is not None:
+ parent="${parent}"
+ %endif
+ id="libraryItem-rrsi-${encoded_id}">
+ <td style="padding-left: ${pad+20}px;">
+ <table id="test_environment">
+ %if not is_current_repository:
+ <tr bgcolor="#FFFFCC">
+ <th>Tool shed</th><th>Name</th><th>Owner</th><th>Changeset revision</th>
+ </tr>
+ <tr>
+ <td>${successful_installation.tool_shed | h}</td>
+ <td>${successful_installation.name | h}</td>
+ <td>${successful_installation.owner | h}</td>
+ <td>${successful_installation.changeset_revision | h}</td>
+ </tr>
+ %endif
+ </table>
+ </td>
+ </tr>
+ <%
+ my_row = row_counter.count
+ row_counter.increment()
+ %>
+</%def>
+
<%def name="render_not_tested( not_tested, pad, parent, row_counter, row_is_header=False )"><%
encoded_id = trans.security.encode_id( not_tested.id )
diff -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b -r a0e80a712c37754d2d33260919469a7dccb2f1b1 test/install_and_test_tool_shed_repositories/base/util.py
--- a/test/install_and_test_tool_shed_repositories/base/util.py
+++ b/test/install_and_test_tool_shed_repositories/base/util.py
@@ -119,7 +119,6 @@
# a definition for that tool shed.
galaxy_tool_shed_url = os.environ.get( 'GALAXY_INSTALL_TEST_TOOL_SHED_URL', None )
tool_shed_api_key = os.environ.get( 'GALAXY_INSTALL_TEST_TOOL_SHED_API_KEY', None )
-exclude_list_file = os.environ.get( 'GALAXY_INSTALL_TEST_EXCLUDE_REPOSITORIES', 'install_test_exclude.xml' )
if 'GALAXY_INSTALL_TEST_SECRET' not in os.environ:
galaxy_encode_secret = 'changethisinproductiontoo'
@@ -182,6 +181,46 @@
return passed_tests
return []
+def display_repositories_by_owner( repository_dicts ):
+ # Group summary display by repository owner.
+ repository_dicts_by_owner = {}
+ for repository_dict in repository_dicts:
+ name = str( repository_dict[ 'name' ] )
+ owner = str( repository_dict[ 'owner' ] )
+ changeset_revision = str( repository_dict[ 'changeset_revision' ] )
+ if owner in repository_dicts_by_owner:
+ repository_dicts_by_owner[ owner ].append( repository_dict )
+ else:
+ repository_dicts_by_owner[ owner ] = [ repository_dict ]
+ # Display grouped summary.
+ for owner, grouped_repository_dicts in repository_dicts_by_owner.items():
+ print "# "
+ for repository_dict in grouped_repository_dicts:
+ name = str( repository_dict[ 'name' ] )
+ owner = str( repository_dict[ 'owner' ] )
+ changeset_revision = str( repository_dict[ 'changeset_revision' ] )
+ print "# Revision %s of repository %s owned by %s" % ( changeset_revision, name, owner )
+
+def display_tool_dependencies_by_name( tool_dependency_dicts ):
+ # Group summary display by repository owner.
+ tool_dependency_dicts_by_name = {}
+ for tool_dependency_dict in tool_dependency_dicts:
+ name = str( tool_dependency_dict[ 'name' ] )
+ type = str( tool_dependency_dict[ 'type' ] )
+ version = str( tool_dependency_dict[ 'version' ] )
+ if name in tool_dependency_dicts_by_name:
+ tool_dependency_dicts_by_name[ name ].append( tool_dependency_dict )
+ else:
+ tool_dependency_dicts_by_name[ name ] = [ tool_dependency_dict ]
+ # Display grouped summary.
+ for name, grouped_tool_dependency_dicts in tool_dependency_dicts_by_name.items():
+ print "# "
+ for tool_dependency_dict in grouped_tool_dependency_dicts:
+ type = str( tool_dependency_dict[ 'type' ] )
+ name = str( tool_dependency_dict[ 'name' ] )
+ version = str( tool_dependency_dict[ 'version' ] )
+ print "# %s %s version %s" % ( type, name, version )
+
def get_api_url( base, parts=[], params=None ):
if 'api' in parts and parts.index( 'api' ) != 0:
parts.pop( parts.index( 'api' ) )
@@ -474,18 +513,14 @@
def initialize_install_and_test_statistics_dict( test_framework ):
# Initialize a dictionary for the summary that will be printed to stdout.
install_and_test_statistics_dict = {}
+ install_and_test_statistics_dict[ 'total_repositories_processed' ] = 0
+ install_and_test_statistics_dict[ 'successful_repository_installations' ] = []
+ install_and_test_statistics_dict[ 'successful_tool_dependency_installations' ] = []
+ install_and_test_statistics_dict[ 'repositories_with_installation_error' ] = []
+ install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ] = []
if test_framework == REPOSITORIES_WITH_TOOLS:
- install_and_test_statistics_dict[ 'total_repositories_processed' ] = 0
install_and_test_statistics_dict[ 'all_tests_passed' ] = []
install_and_test_statistics_dict[ 'at_least_one_test_failed' ] = []
- install_and_test_statistics_dict[ 'successful_installations' ] = []
- install_and_test_statistics_dict[ 'repositories_with_installation_error' ] = []
- install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ] = []
- elif test_framework == TOOL_DEPENDENCY_DEFINITIONS:
- install_and_test_statistics_dict[ 'total_repositories_processed' ] = 0
- install_and_test_statistics_dict[ 'successful_installations' ] = []
- install_and_test_statistics_dict[ 'repositories_with_installation_error' ] = []
- install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ] = []
return install_and_test_statistics_dict
def initialize_tool_tests_results_dict( app, tool_test_results_dict ):
@@ -516,6 +551,9 @@
tool_test_results_dict[ 'installation_errors' ] = dict( current_repository=[],
repository_dependencies=[],
tool_dependencies=[] )
+ tool_test_results_dict[ 'successful_installations' ] = dict( current_repository=[],
+ repository_dependencies=[],
+ tool_dependencies=[] )
return tool_test_results_dict
def install_repository( app, repository_dict ):
@@ -563,7 +601,17 @@
def parse_exclude_list( xml_filename ):
"""Return a list of repositories to exclude from testing."""
- # This method should return a list with the following structure:
+ # This method expects an xml document that looks something like this:
+ # <?xml version="1.0"?>
+ # <blacklist>
+ # <repositories tool_shed="http://testtoolshed.g2.bx.psu.edu">
+ # <reason>
+ # <text>Some reason</text>
+ # <repository name="some_name" owner="some_owner" />
+ # </reason>
+ # </repositories>
+ # </blacklist>
+ # A list is returned with the following structure:
# [{ 'reason': The default reason or the reason specified in this section,
# 'repositories': [( name, owner, changeset revision if changeset revision else None ),
# ( name, owner, changeset revision if changeset revision else None )]}]
@@ -640,26 +688,6 @@
result = test_runner.run( tests )
return result, test_config.plugins._plugins
-def show_summary_output( repository_dicts ):
- # Group summary display by repository owner.
- repository_dicts_by_owner = {}
- for repository_dict in repository_dicts:
- name = str( repository_dict[ 'name' ] )
- owner = str( repository_dict[ 'owner' ] )
- changeset_revision = str( repository_dict[ 'changeset_revision' ] )
- if owner in repository_dicts_by_owner:
- repository_dicts_by_owner[ owner ].append( repository_dict )
- else:
- repository_dicts_by_owner[ owner ] = [ repository_dict ]
- # Display grouped summary.
- for owner, grouped_repository_dicts in repository_dicts_by_owner.items():
- print "# "
- for repository_dict in grouped_repository_dicts:
- name = str( repository_dict[ 'name' ] )
- owner = str( repository_dict[ 'owner' ] )
- changeset_revision = str( repository_dict[ 'changeset_revision' ] )
- print "# Revision %s of repository %s owned by %s" % ( changeset_revision, name, owner )
-
def uninstall_repository_and_repository_dependencies( app, repository_dict ):
"""Uninstall a repository and all of its repository dependencies."""
# This method assumes that the repositor defined by the received repository_dict is not a repository
@@ -740,7 +768,7 @@
tool_dependency_install_path = tool_dependency.installation_directory( app )
uninstalled, error_message = tool_dependency_util.remove_tool_dependency( app, tool_dependency )
if error_message:
- log.debug( 'There was an error attempting to remove directory: %s' % str( tool_dependency_install_path ) )
+ log.debug( 'Error attempting to remove directory: %s' % str( tool_dependency_install_path ) )
log.debug( error_message )
else:
log.debug( 'Successfully removed tool dependency installation directory: %s' % str( tool_dependency_install_path ) )
diff -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b -r a0e80a712c37754d2d33260919469a7dccb2f1b1 test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
@@ -66,6 +66,9 @@
default_galaxy_locales = 'en'
default_galaxy_test_file_dir = "test-data"
os.environ[ 'GALAXY_INSTALL_TEST_TMP_DIR' ] = galaxy_test_tmp_dir
+# This file is copied to the Galaxy root directory by buildbot.
+# It is managed by cfengine and is not locally available.
+exclude_list_file = os.environ.get( 'GALAXY_INSTALL_TEST_EXCLUDE_REPOSITORIES', 'repositories_with_tools_exclude.xml' )
# This script can be run in such a way that no Tool Shed database records should be changed.
if '-info_only' in sys.argv or 'GALAXY_INSTALL_TEST_INFO_ONLY' in os.environ:
@@ -144,7 +147,7 @@
if error_message:
return None, error_message
# Handle repositories not to be tested.
- if os.path.exists( install_and_test_base_util.exclude_list_file ):
+ if os.path.exists( exclude_list_file ):
# Entries in the exclude_list look something like this.
# { 'reason': The default reason or the reason specified in this section,
# 'repositories':
@@ -157,8 +160,8 @@
# undeleted, this script will then test them the next time it runs. We don't need to check if a repository has been deleted
# here because our call to the Tool Shed API filters by downloadable='true', in which case deleted will always be False.
log.debug( 'Loading the list of repositories excluded from testing from the file %s...' % \
- str( install_and_test_base_util.exclude_list_file ) )
- exclude_list = install_and_test_base_util.parse_exclude_list( install_and_test_base_util.exclude_list_file )
+ str( exclude_list_file ) )
+ exclude_list = install_and_test_base_util.parse_exclude_list( exclude_list_file )
else:
exclude_list = []
# Generate a test method that will use Twill to install each repository into the embedded Galaxy application that was
@@ -299,7 +302,7 @@
test_toolbox.toolbox = app.toolbox
else:
# This repository and all of its dependencies were successfully installed.
- install_and_test_statistics_dict[ 'successful_installations' ].append( repository_identifier_dict )
+ install_and_test_statistics_dict[ 'successful_repository_installations' ].append( repository_identifier_dict )
# Configure and run functional tests for this repository. This is equivalent to sh run_functional_tests.sh -installed
remove_install_tests()
log.debug( 'Installation of %s succeeded, running all defined functional tests.' % str( repository.name ) )
@@ -560,33 +563,33 @@
total_repositories_processed = install_and_test_statistics_dict[ 'total_repositories_processed' ]
all_tests_passed = install_and_test_statistics_dict[ 'all_tests_passed' ]
at_least_one_test_failed = install_and_test_statistics_dict[ 'at_least_one_test_failed' ]
- successful_installations = install_and_test_statistics_dict[ 'successful_installations' ]
+ successful_repository_installations = install_and_test_statistics_dict[ 'successful_repository_installations' ]
repositories_with_installation_error = install_and_test_statistics_dict[ 'repositories_with_installation_error' ]
tool_dependencies_with_installation_error = install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ]
now = time.strftime( "%Y-%m-%d %H:%M:%S" )
print "####################################################################################"
print "# %s - installation script for repositories containing tools completed." % now
print "# Repository revisions processed: %s" % str( total_repositories_processed )
- if successful_installations:
+ if successful_repository_installations:
print "# ----------------------------------------------------------------------------------"
- print "# The following %d revisions with all dependencies were successfully installed:" % len( successful_installations )
- install_and_test_base_util.show_summary_output( successful_installations )
+ print "# The following %d revisions were successfully installed:" % len( successful_repository_installations )
+ install_and_test_base_util.display_repositories_by_owner( successful_repository_installations )
if all_tests_passed:
print '# ----------------------------------------------------------------------------------'
- print "# %d repositories successfully passed all functional tests:" % len( all_tests_passed )
- install_and_test_base_util.show_summary_output( all_tests_passed )
+ print "# The following %d revisions successfully passed all functional tests:" % len( all_tests_passed )
+ install_and_test_base_util.display_repositories_by_owner( all_tests_passed )
if at_least_one_test_failed:
print '# ----------------------------------------------------------------------------------'
- print "# %d repositories failed at least 1 functional test:" % len( at_least_one_test_failed )
- install_and_test_base_util.show_summary_output( at_least_one_test_failed )
+ print "# The following %d revisions failed at least 1 functional test:" % len( at_least_one_test_failed )
+ install_and_test_base_util.display_repositories_by_owner( at_least_one_test_failed )
if repositories_with_installation_error:
print '# ----------------------------------------------------------------------------------'
- print "# %d repositories have installation errors:" % len( repositories_with_installation_error )
- install_and_test_base_util.show_summary_output( repositories_with_installation_error )
+ print "# The following %d revisions have installation errors:" % len( repositories_with_installation_error )
+ install_and_test_base_util.display_repositories_by_owner( repositories_with_installation_error )
if tool_dependencies_with_installation_error:
print "# ----------------------------------------------------------------------------------"
print "# The following %d tool dependencies have installation errors:" % len( tool_dependencies_with_installation_error )
- install_and_test_base_util.show_summary_output( tool_dependencies_with_installation_error )
+ install_and_test_base_util.display_tool_dependencies_by_name( tool_dependencies_with_installation_error )
print "####################################################################################"
log.debug( "Shutting down..." )
# Gracefully shut down the embedded web server and UniverseApplication.
diff -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b -r a0e80a712c37754d2d33260919469a7dccb2f1b1 test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
@@ -64,6 +64,9 @@
default_galaxy_locales = 'en'
default_galaxy_test_file_dir = "test-data"
os.environ[ 'GALAXY_INSTALL_TEST_TMP_DIR' ] = galaxy_test_tmp_dir
+# This file is copied to the Galaxy root directory by buildbot.
+# It is managed by cfengine and is not locally available.
+exclude_list_file = os.environ.get( 'GALAXY_INSTALL_TEST_EXCLUDE_REPOSITORIES', 'tool_dependency_definition_exclude.xml' )
# This script can be run in such a way that no Tool Shed database records should be changed.
if '-info_only' in sys.argv or 'GALAXY_INSTALL_TEST_INFO_ONLY' in os.environ:
@@ -83,7 +86,7 @@
if error_message:
return None, error_message
# Handle repositories not to be tested.
- if os.path.exists( install_and_test_base_util.exclude_list_file ):
+ if os.path.exists( exclude_list_file ):
# Entries in the exclude_list look something like this.
# { 'reason': The default reason or the reason specified in this section,
# 'repositories':
@@ -96,8 +99,8 @@
# undeleted, this script will then test them the next time it runs. We don't need to check if a repository has been deleted
# here because our call to the Tool Shed API filters by downloadable='true', in which case deleted will always be False.
log.debug( 'Loading the list of repositories excluded from testing from the file %s...' % \
- str( install_and_test_base_util.exclude_list_file ) )
- exclude_list = install_and_test_base_util.parse_exclude_list( install_and_test_base_util.exclude_list_file )
+ str( exclude_list_file ) )
+ exclude_list = install_and_test_base_util.parse_exclude_list( exclude_list_file )
else:
exclude_list = []
# Generate a test method that will use Twill to install each repository into the embedded Galaxy application that was
@@ -176,7 +179,8 @@
# Even if the repository failed to install, execute the uninstall method, in case a dependency did succeed.
log.debug( 'Attempting to uninstall revision %s of repository %s owned by %s.' % ( changeset_revision, name, owner ) )
try:
- repository = test_db_util.get_installed_repository_by_name_owner_changeset_revision( name, owner, changeset_revision )
+ repository = \
+ test_db_util.get_installed_repository_by_name_owner_changeset_revision( name, owner, changeset_revision )
except Exception, e:
error_message = 'Unable to find revision %s of repository %s owned by %s: %s.' % \
( changeset_revision, name, owner, str( e ) )
@@ -199,9 +203,34 @@
log.debug( 'Installation failed for revision %s of repository %s owned by %s.' % \
( changeset_revision, name, owner ) )
else:
+ # The repository was successfully installed.
log.debug( 'Installation succeeded for revision %s of repository %s owned by %s.' % \
( changeset_revision, name, owner ) )
+ install_and_test_statistics_dict[ 'successful_repository_installations' ].append( repository_identifier_dict )
+ tool_test_results_dict[ 'successful_installations' ][ 'current_repository' ]\
+ .append( repository_identifier_dict )
+ params = dict( test_install_error=False,
+ do_not_test=False )
+ if repository.missing_repository_dependencies:
+ params[ 'test_install_error' ] = True
+ # Keep statistics for this repository's repository dependencies that resulted in installation errors.
+ for missing_repository_dependency in repository.missing_repository_dependencies:
+ tool_shed = str( missing_repository_dependency.tool_shed )
+ name = str( missing_repository_dependency.name )
+ owner = str( missing_repository_dependency.owner )
+ changset_revision = str( missing_repository_dependency.changeset_revision )
+ error_message = unicodify( missing_repository_dependency.error_message )
+ missing_repository_dependency_info_dict = dict( tool_shed=tool_shed,
+ name=name,
+ owner=owner,
+ changset_revision=changset_revision,
+ error_message=error_message )
+ install_and_test_statistics_dict[ 'repositories_with_installation_error' ]\
+ .append( missing_repository_dependency_dict )
+ tool_test_results_dict[ 'installation_errors' ][ 'repository_dependencies' ]\
+ .append( missing_repository_dependency_info_dict )
if repository.missing_tool_dependencies:
+ params[ 'test_install_error' ] = True
# Keep statistics for this repository's tool dependencies that resulted in installation errors.
for missing_tool_dependency in repository.missing_tool_dependencies:
name = str( missing_tool_dependency.name )
@@ -212,22 +241,47 @@
name=name,
version=version,
error_message=error_message )
- install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ].append( missing_tool_dependency_info_dict )
- else:
- # This repository and all of its dependencies were successfully installed.
- install_and_test_statistics_dict[ 'successful_installations' ].append( repository_identifier_dict )
- tool_test_results_dict[ 'passed_tests' ].append( repository_identifier_dict )
- params = dict( test_install_error=False,
- do_not_test=False )
- # TODO: do something useful with response_dict
- response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
- tool_test_results_dicts,
- tool_test_results_dict,
- repository_dict,
- params,
- can_update_tool_shed )
-
- install_and_test_statistics_dict[ 'total_repositories_processed' ] = total_repositories_processed
+ install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ]\
+ .append( missing_tool_dependency_info_dict )
+ tool_test_results_dict[ 'installation_errors' ][ 'tool_dependencies' ]\
+ .append( missing_tool_dependency_info_dict )
+ if repository.installed_repository_dependencies:
+ # Keep statistics for this repository's tool dependencies that resulted in successful installations.
+ for repository_dependency in repository.installed_repository_dependencies:
+ tool_shed = str( repository_dependency.tool_shed )
+ name = str( repository_dependency.name )
+ owner = str( repository_dependency.owner )
+ changeset_revision = str( repository_dependency.changeset_revision )
+ repository_dependency_info_dict = dict( tool_shed=tool_shed,
+ name=name,
+ owner=owner,
+ changeset_revision=changeset_revision )
+ install_and_test_statistics_dict[ 'successful_repository_installations' ]\
+ .append( repository_dependency_info_dict )
+ tool_test_results_dict[ 'successful_installations' ][ 'repository_dependencies' ]\
+ .append( repository_dependency_info_dict )
+ if repository.installed_tool_dependencies:
+ # Keep statistics for this repository's tool dependencies that resulted in successful installations.
+ for tool_dependency in repository.installed_tool_dependencies:
+ name = str( tool_dependency.name )
+ type = str( tool_dependency.type )
+ version = str( tool_dependency.version )
+ installation_directory = tool_dependency.installation_directory( app )
+ tool_dependency_info_dict = dict( type=type,
+ name=name,
+ version=version,
+ installation_directory=installation_directory )
+ install_and_test_statistics_dict[ 'successful_tool_dependency_installations' ]\
+ .append( tool_dependency_info_dict )
+ tool_test_results_dict[ 'successful_installations' ][ 'tool_dependencies' ]\
+ .append( tool_dependency_info_dict )
+ # TODO: do something useful with response_dict
+ response_dict = install_and_test_base_util.register_test_result( install_and_test_base_util.galaxy_tool_shed_url,
+ tool_test_results_dicts,
+ tool_test_results_dict,
+ repository_dict,
+ params,
+ can_update_tool_shed )
return install_and_test_statistics_dict, error_message
def main():
@@ -446,25 +500,30 @@
log.debug( error_message )
else:
total_repositories_processed = install_and_test_statistics_dict[ 'total_repositories_processed' ]
- successful_installations = install_and_test_statistics_dict[ 'successful_installations' ]
+ successful_repository_installations = install_and_test_statistics_dict[ 'successful_repository_installations' ]
+ successful_tool_dependency_installations = install_and_test_statistics_dict[ 'successful_tool_dependency_installations' ]
repositories_with_installation_error = install_and_test_statistics_dict[ 'repositories_with_installation_error' ]
tool_dependencies_with_installation_error = install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ]
now = time.strftime( "%Y-%m-%d %H:%M:%S" )
print "####################################################################################"
print "# %s - installation script for repositories of type tool_dependency_definition completed." % now
print "# Repository revisions processed: %s" % str( total_repositories_processed )
- if successful_installations:
+ if successful_repository_installations:
print "# ----------------------------------------------------------------------------------"
- print "# The following %d revisions with all dependencies were successfully installed:" % len( successful_installations )
- install_and_test_base_util.show_summary_output( successful_installations )
+ print "# The following %d revisions were successfully installed:" % len( successful_repository_installations )
+ install_and_test_base_util.display_repositories_by_owner( successful_repository_installations )
if repositories_with_installation_error:
print "# ----------------------------------------------------------------------------------"
print "# The following %d revisions have installation errors:" % len( repositories_with_installation_error )
- install_and_test_base_util.show_summary_output( repositories_with_installation_error )
+ install_and_test_base_util.successful_repository_installations( repositories_with_installation_error )
+ if successful_tool_dependency_installations:
+ print "# ----------------------------------------------------------------------------------"
+ print "# The following %d tool dependencies were successfully installed:" % len( successful_tool_dependency_installations )
+ install_and_test_base_util.display_tool_dependencies_by_name( successful_tool_dependency_installations )
if tool_dependencies_with_installation_error:
print "# ----------------------------------------------------------------------------------"
print "# The following %d tool dependencies have installation errors:" % len( tool_dependencies_with_installation_error )
- install_and_test_base_util.show_summary_output( tool_dependencies_with_installation_error )
+ install_and_test_base_util.display_tool_dependencies_by_name( tool_dependencies_with_installation_error )
print "####################################################################################"
log.debug( "Shutting down..." )
# Gracefully shut down the embedded web server and UniverseApplication.
@@ -506,6 +565,36 @@
# "architecture": "x86_64",
# "system": "Darwin 12.2.0"
# },
+ # "successful_installation":
+ # {
+ # 'tool_dependencies':
+ # [
+ # {
+ # 'type': 'Type of tool dependency, e.g. package, set_environment, etc.',
+ # 'name': 'Name of the tool dependency.',
+ # 'version': 'Version if this is a package, otherwise blank.',
+ # 'installation_directory': 'The installation directory path.'
+ # },
+ # ],
+ # 'repository_dependencies':
+ # [
+ # {
+ # 'tool_shed': 'The tool shed that this repository was installed from.',
+ # 'name': 'The name of the repository that failed to install.',
+ # 'owner': 'Owner of the failed repository.',
+ # 'changeset_revision': 'Changeset revision of the failed repository.'
+ # },
+ # ],
+ # 'current_repository':
+ # [
+ # {
+ # 'tool_shed': 'The tool shed that this repository was installed from.',
+ # 'name': 'The name of the repository that failed to install.',
+ # 'owner': 'Owner of the failed repository.',
+ # 'changeset_revision': 'Changeset revision of the failed repository.'
+ # },
+ # ],
+ # }
# "installation_errors":
# {
# 'tool_dependencies':
@@ -537,12 +626,6 @@
# 'error_message': 'The error message that was returned when the repository failed to install.',
# },
# ],
- # {
- # "name": "The name of the repository.",
- # "owner": "The owner of the repository.",
- # "changeset_revision": "The changeset revision of the repository.",
- # "error_message": "The message stored in tool_dependency.error_message."
- # },
# }
# }
sys.exit( main() )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
38 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/57d618633209/
Changeset: 57d618633209
Branch: data_library
User: martenson
Date: 2013-10-08 22:41:08
Summary: start data_library branch
Affected #: 3 files
diff -r b61927f340f30a636204ec560522acf9b76fd262 -r 57d618633209a35342f25a13a220fedce71f7a3f .hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -115,3 +115,6 @@
*.rej
*~
+
+syntax: regexp
+^static/AAA_scratch$
\ No newline at end of file
diff -r b61927f340f30a636204ec560522acf9b76fd262 -r 57d618633209a35342f25a13a220fedce71f7a3f lib/galaxy/webapps/galaxy/api/folder_contents.py
--- a/lib/galaxy/webapps/galaxy/api/folder_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/folder_contents.py
@@ -1,5 +1,5 @@
"""
-API operations on the contents of a library.
+API operations on the contents of a folder.
"""
import logging, os, string, shutil, urllib, re, socket
from cgi import escape, FieldStorage
@@ -11,12 +11,17 @@
log = logging.getLogger( __name__ )
class FolderContentsController( BaseAPIController, UsesLibraryMixin, UsesLibraryMixinItems ):
+ """
+ Class controls retrieval, creation and updating of folder contents.
+ """
@web.expose_api
def index( self, trans, folder_id, **kwd ):
"""
GET /api/folders/{encoded_folder_id}/contents
+
Displays a collection (list) of a folder's contents (files and folders).
+
The /api/library_contents/{encoded_library_id}/contents
lists everything in a library recursively, which is not what
we want here. We could add a parameter to use the recursive
@@ -25,7 +30,11 @@
rval = []
current_user_roles = trans.get_current_user_roles()
+
def traverse( folder ):
+ """
+ Load contents of the folder (folders and datasets).
+ """
admin = trans.user_is_admin()
rval = []
for subfolder in folder.active_folders:
@@ -50,6 +59,8 @@
try:
folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( decoded_folder_id )
+# log.debug("XXXXXXXXXXXXXXXXXXXXXXXXXXX folder.parent_library" + str(folder.parent_library.id))
+# log.debug("XXXXXXXXXXXXXXXXXXXXXXXXXXX folder.parent_id" + str(folder.parent_id))
parent_library = folder.parent_library
except:
folder = None
@@ -62,15 +73,43 @@
if not folder or not ( trans.user_is_admin() or trans.app.security_agent.can_access_library_item( current_user_roles, folder, trans.user ) ):
trans.response.status = 400
return "Invalid folder id ( %s ) specified." % str( folder_id )
+ # TODO MARTEN Can it be that predecessors of current folder have different access rights? aka user shouldn't see them?
+
+ # Search the path upwards and load the whole route of names and ids for breadcrumb purposes.
+ path_to_library = []
+
+ def build_path ( folder ):
+# log.debug("XXXXXXXXXXXXXXXXXXXXXXX folder.parent_id BEFORE " + str(folder.parent_id))
+# log.debug("XXXXXXXXXXXXXXXXXXXXXXX folder.parent_library.id BEFORE " + str(folder.parent_library.id))
+ if ( folder.parent_id != folder.parent_library.id ) and ( folder.parent_id is not None ):
+ log.debug("XXXXXXXXXXXXXXXXXXXXXXX LOADING UPPER FOLDER WITH ID: " + str(folder.parent_id))
+ upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_id )
+ log.debug("XXXXXXXXXXXXXXXXXXXXXXX upper_folder.id " + str(upper_folder.id))
+ log.debug("XXXXXXXXXXXXXXXXXXXXXXX upper_folder.name " + str(upper_folder.name))
+ path_to_library.append( ( upper_folder.id, upper_folder.name ) )
+ path_to_library.extend( build_path( upper_folder ) )
+ else:
+ pass
+ return path_to_library
+
+ full_path = build_path( folder )
for content in traverse( folder ):
+ return_item = {}
encoded_id = trans.security.encode_id( content.id )
if content.api_type == 'folder':
+ encoded_parent_library_id = trans.security.encode_id( content.parent_library.id )
encoded_id = 'F' + encoded_id
- rval.append( dict( id = encoded_id,
+ if content.parent_id is not None: # For folder return its parent's id for browsing back.
+ encoded_parent_id = 'F' + trans.security.encode_id( content.parent_id )
+ return_item.update ( dict ( parent_id = encoded_parent_id ) )
+ return_item.update( dict( id = encoded_id,
type = content.api_type,
name = content.name,
+ library_id = encoded_parent_library_id,
+ full_path = full_path,
url = url_for( 'folder_contents', folder_id=encoded_id ) ) )
+ rval.append( return_item )
return rval
@web.expose_api
diff -r b61927f340f30a636204ec560522acf9b76fd262 -r 57d618633209a35342f25a13a220fedce71f7a3f lib/galaxy/webapps/galaxy/api/libraries.py
--- a/lib/galaxy/webapps/galaxy/api/libraries.py
+++ b/lib/galaxy/webapps/galaxy/api/libraries.py
@@ -49,7 +49,7 @@
trans.model.Library.table.c.id.in_( accessible_restricted_library_ids ) ) )
rval = []
for library in query:
- item = library.to_dict()
+ item = library.to_dict( view='element' )
item['url'] = url_for( route, id=trans.security.encode_id( library.id ) )
item['id'] = trans.security.encode_id( item['id'] )
rval.append( item )
@@ -131,6 +131,9 @@
rval['name'] = name
rval['id'] = encoded_id
return rval
+
+ def edit( self, trans, payload, **kwd ):
+ return "Not implemented yet"
@web.expose_api
def delete( self, trans, id, **kwd ):
https://bitbucket.org/galaxy/galaxy-central/commits/db8fd059cf4c/
Changeset: db8fd059cf4c
Branch: data_library
User: martenson
Date: 2013-10-17 18:56:34
Summary: working reverse traversing towards root library for every folder = good for path building
Affected #: 1 file
diff -r 57d618633209a35342f25a13a220fedce71f7a3f -r db8fd059cf4cae9a6b7dc8bdbe7be5d071325d09 lib/galaxy/webapps/galaxy/api/folder_contents.py
--- a/lib/galaxy/webapps/galaxy/api/folder_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/folder_contents.py
@@ -27,11 +27,11 @@
we want here. We could add a parameter to use the recursive
style, but this is meant to act similar to an "ls" directory listing.
"""
- rval = []
+ folder_contents = []
current_user_roles = trans.get_current_user_roles()
- def traverse( folder ):
+ def load_folder_contents( folder ):
"""
Load contents of the folder (folders and datasets).
"""
@@ -76,41 +76,55 @@
# TODO MARTEN Can it be that predecessors of current folder have different access rights? aka user shouldn't see them?
# Search the path upwards and load the whole route of names and ids for breadcrumb purposes.
- path_to_library = []
+ path_to_root = []
def build_path ( folder ):
-# log.debug("XXXXXXXXXXXXXXXXXXXXXXX folder.parent_id BEFORE " + str(folder.parent_id))
-# log.debug("XXXXXXXXXXXXXXXXXXXXXXX folder.parent_library.id BEFORE " + str(folder.parent_library.id))
- if ( folder.parent_id != folder.parent_library.id ) and ( folder.parent_id is not None ):
- log.debug("XXXXXXXXXXXXXXXXXXXXXXX LOADING UPPER FOLDER WITH ID: " + str(folder.parent_id))
+ path_to_root = []
+ # We are almost in root
+ log.debug( "XXXXXXXXXXXXXXXXXXXXXXX folder.parent_id: " + str( folder.parent_id ) )
+ log.debug( "XXXXXXXXXXXXXXXXXXXXXXX folder.parent_library.id: " + str( folder.parent_library.id ) )
+ if folder.parent_id is None:
+ log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ALMOST ROOT FOLDER! ADDING: " + str( folder.name ) )
+ path_to_root.append( ( folder.id, folder.name ) )
+# upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_library.id )
+# path_to_root.append( ( upper_folder.id, upper_folder.name ) )
+ else:
+ # We add the current folder and traverse up one folder.
+ log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ADDING THIS FOLDER AND TRAVERSING UP: " + str( folder.name ) )
+ path_to_root.append( ( folder.id, folder.name ) )
upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_id )
- log.debug("XXXXXXXXXXXXXXXXXXXXXXX upper_folder.id " + str(upper_folder.id))
- log.debug("XXXXXXXXXXXXXXXXXXXXXXX upper_folder.name " + str(upper_folder.name))
- path_to_library.append( ( upper_folder.id, upper_folder.name ) )
- path_to_library.extend( build_path( upper_folder ) )
- else:
- pass
- return path_to_library
+ path_to_root.extend( build_path( upper_folder ) )
+# path_to_root = path_to_root.reverse()
+# return path_to_root[::-1]
+ return path_to_root
- full_path = build_path( folder )
+ # Return the reversed path so it starts with the library node.
+ full_path = build_path( folder )[::-1]
- for content in traverse( folder ):
+ # Go through every item in the folder and include its meta-data.
+ for content_item in load_folder_contents( folder ):
return_item = {}
- encoded_id = trans.security.encode_id( content.id )
- if content.api_type == 'folder':
- encoded_parent_library_id = trans.security.encode_id( content.parent_library.id )
+ encoded_id = trans.security.encode_id( content_item.id )
+
+ # For folder return also hierarchy values
+ if content_item.api_type == 'folder':
+ encoded_parent_library_id = trans.security.encode_id( content_item.parent_library.id )
encoded_id = 'F' + encoded_id
- if content.parent_id is not None: # For folder return its parent's id for browsing back.
- encoded_parent_id = 'F' + trans.security.encode_id( content.parent_id )
+ if content_item.parent_id is not None: # Return folder's parent id for browsing back.
+ encoded_parent_id = 'F' + trans.security.encode_id( content_item.parent_id )
return_item.update ( dict ( parent_id = encoded_parent_id ) )
+
+ # For every item return also the default meta-data
return_item.update( dict( id = encoded_id,
- type = content.api_type,
- name = content.name,
+ type = content_item.api_type,
+ name = content_item.name,
library_id = encoded_parent_library_id,
full_path = full_path,
url = url_for( 'folder_contents', folder_id=encoded_id ) ) )
- rval.append( return_item )
- return rval
+ folder_contents.append( return_item )
+ if len( folder_contents ) == 0:
+ folder_contents.append( dict( full_path = full_path ) )
+ return folder_contents
@web.expose_api
def show( self, trans, id, library_id, **kwd ):
https://bitbucket.org/galaxy/galaxy-central/commits/1eda20e058eb/
Changeset: 1eda20e058eb
Branch: data_library
User: martenson
Date: 2013-10-21 18:31:38
Summary: initial frontend commit
Affected #: 3 files
diff -r db8fd059cf4cae9a6b7dc8bdbe7be5d071325d09 -r 1eda20e058ebb08a4f4b71ed6988f6f106b62fdb lib/galaxy/webapps/galaxy/api/folder_contents.py
--- a/lib/galaxy/webapps/galaxy/api/folder_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/folder_contents.py
@@ -27,7 +27,7 @@
we want here. We could add a parameter to use the recursive
style, but this is meant to act similar to an "ls" directory listing.
"""
- folder_contents = []
+ folder_container = []
current_user_roles = trans.get_current_user_roles()
@@ -100,7 +100,8 @@
# Return the reversed path so it starts with the library node.
full_path = build_path( folder )[::-1]
-
+ folder_container.append( dict( full_path = full_path ) )
+ folder_contents = []
# Go through every item in the folder and include its meta-data.
for content_item in load_folder_contents( folder ):
return_item = {}
@@ -118,13 +119,11 @@
return_item.update( dict( id = encoded_id,
type = content_item.api_type,
name = content_item.name,
- library_id = encoded_parent_library_id,
- full_path = full_path,
- url = url_for( 'folder_contents', folder_id=encoded_id ) ) )
+ library_id = encoded_parent_library_id
+ ) )
folder_contents.append( return_item )
- if len( folder_contents ) == 0:
- folder_contents.append( dict( full_path = full_path ) )
- return folder_contents
+ folder_container.append( dict( folder_contents = folder_contents ) )
+ return folder_container
@web.expose_api
def show( self, trans, id, library_id, **kwd ):
diff -r db8fd059cf4cae9a6b7dc8bdbe7be5d071325d09 -r 1eda20e058ebb08a4f4b71ed6988f6f106b62fdb static/AAA_scratch/data_library/index.html
--- /dev/null
+++ b/static/AAA_scratch/data_library/index.html
@@ -0,0 +1,265 @@
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>Data Library</title>
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.1.1/css/bootstrap…">
+</head>
+<body>
+
+
+ <div class="container">
+ <h1><a onclick="router.navigate('', {trigger: true})">Data Library</a></h1>
+ <hr />
+ <div class="page"></div>
+ </div>
+
+<!-- LIBRARY LIST TEMPLATE -->
+ <script type="text/template" id="library-list-template">
+ <table class="table striped">
+ <thead>
+ <th>name</th>
+ <th>description</th>
+ <th>synopsis</th>
+ <th>model type</th>
+ <th>id</th>
+
+ </thead>
+ <tbody>
+ <% _.each(libraries, function(library) { %>
+ <tr>
+ <td><a href="#/folders/<%- library.id %>"><%- library.get('name') %></a></td>
+ <td><%= htmlEncode(library.get('description')) %></td>
+ <td><%= htmlEncode(library.get('synopsis')) %></td>
+ <td><%= htmlEncode(library.get('model_class')) %></td>
+ <td><a href="#/folders/<%- library.id %>"><%= htmlEncode(library.get('id')) %></a></td>
+ </tr>
+ <% }); %>
+ </tbody>
+ </table>
+ </script>
+
+
+<!-- FOLDERS LIST TEMPLATE -->
+ <script type="text/template" id="folder-content-template">
+ <table class="table striped">
+ <thead>
+ <th>name</th>
+ <th>type</th>
+ <th>parent id</th>
+ <th>id</th>
+ <th>library id</th>
+ </thead>
+ <tbody>
+ <% _.each(items, function(content_item) { %>
+ <tr>
+ <% if (content_item.get('type') === 'folder') { %>
+ <td><a href="#/folders/<%- content_item.id %>?library_id=<%- content_item.get('library_id') %>"><%- content_item.get('name') %></a></td>
+ <% } else { %>
+ <td><%- content_item.get('name') %></td>
+ <% } %>
+ <td><%= htmlEncode(content_item.get('type')) %></td>
+ <td><%= htmlEncode(content_item.get('parent_id')) %></td>
+ <td><a href="#/folders/<%- content_item.id %>"><%= htmlEncode(content_item.get('id')) %></a></td>
+ <td><a href="#/folders/<%- content_item.get('library_id') %>"><%= htmlEncode(content_item.get('library_id')) %></a></td>
+ </tr>
+ <% }); %>
+ </tbody>
+ </table>
+
+ <hr/>
+ <div>
+ <% if ( typeof libraries !== undefined ) { %>
+ <% _.each(libraries.models, function(library) { %>
+ <%- library.get('name') %><br/>
+ <% }); %>
+ <% } else { %>
+ libraries are undefined
+ <% } %>
+ </div>
+ </script>
+
+
+ <script src="jquery-1.10.2.js" type="text/javascript"></script>
+ <script src="underscore.js" type="text/javascript"></script>
+ <script src="backbone.js" type="text/javascript"></script>
+ <script src="backbone.queryparams.js" type="text/javascript"></script>
+
+
+ <script>
+
+ // ENCODE HTML
+ function htmlEncode(value){
+ return $('<div/>').text(value).html();
+ }
+
+ // NESTED COLLECTIONS
+ function nestCollection(model, attributeName, nestedCollection) {
+ //setup nested references
+ for (var i = 0; i < nestedCollection.length; i++) {
+ model.attributes[attributeName][i] = nestedCollection.at(i).attributes;
+ }
+
+ //create empty arrays if none
+ nestedCollection.bind('add', function (initiative) {
+ if (!model.get(attributeName)) {
+ model.attributes[attributeName] = [];
+ }
+ model.get(attributeName).push(initiative.attributes);
+ });
+
+ nestedCollection.bind('remove', function (initiative) {
+ var updateObj = {};
+ updateObj[attributeName] = _.without(model.get(attributeName), initiative.attributes);
+ model.set(updateObj);
+ });
+ return nestedCollection;
+ }
+
+ var api_key = 'e48b2b2589b242a50538f8f9fa8e5ee9';
+
+ // MODIFICATIONS DONE JUST BEFORE THE REQUEST ITSELF
+ $.ajaxPrefilter( function( options, originalOptions, jqXHR ) {
+ options.url = 'http://martenson.bx.psu.edu:8080' + options.url + '?key=' + api_key;
+ });
+
+ // SERIALIZE JSON
+ $.fn.serializeObject = function() {
+ var o = {};
+ var a = this.serializeArray();
+ $.each(a, function() {
+ if (o[this.name] !== undefined) {
+ if (!o[this.name].push) {
+ o[this.name] = [o[this.name]];
+ }
+ o[this.name].push(this.value || '');
+ } else {
+ o[this.name] = this.value || '';
+ }
+ });
+ return o;
+ };
+
+ // LIBRARY
+ var Library = Backbone.Model.extend({
+ urlRoot: '/api/libraries',
+ });
+ var Libraries = Backbone.Collection.extend({
+ url: '/api/libraries',
+ model: Library
+ });
+
+ // ITEM
+ var Item = Backbone.Model.extend({
+ })
+
+ // FOLDER
+ var Folder = Backbone.Collection.extend({
+ model: Item
+ })
+
+ // Container including metadata
+ var FolderContainer = Backbone.Model.extend({
+ defaults: {
+ folder: new Folder(),
+ full_path: "unknown",
+ urlRoot: "/api/folders/",
+ id: "unknown"
+ },
+ parse: function(obj) {
+ full_path = obj[0].full_path;
+ // update the inner collection
+ this.get("folder").reset(obj[1].folder_contents);
+
+ // this mightn't be necessary
+ // delete obj.folder;
+
+ return obj;
+ }
+ })
+
+ var LibraryListView = Backbone.View.extend({
+ el: '.page',
+ render: function () {
+ var that = this;
+ if (typeof libraries === "undefined") {
+ libraries = new Libraries();
+ }
+ libraries.fetch({
+ success: function (libraries) {
+ var template = _.template($('#library-list-template').html(), {libraries: libraries.models});
+ that.$el.html(template);
+ }
+ })
+ }
+ });
+
+
+ var FolderContentView = Backbone.View.extend({
+ el: '.page',
+ render: function (options, params) {
+ var that = this;
+
+ var folderContainer = new FolderContainer({id: options.id});
+ folderContainer.url = folderContainer.attributes.urlRoot + options.id + '/contents';
+ // var folder = new Folder({id: options.id});
+
+ folderContainer.fetch({
+ success: function (container) {
+ folderContainer.attributes.folder = container.attributes.folder;
+ var template = _.template($('#folder-content-template').html(), {items: folderContainer.attributes.folder.models, id: options.id});
+ that.$el.html(template);
+ }
+ })
+
+ // folder.fetch({
+ // success: function (folder) {
+ // folderContainer.folder = folder;
+ // var template = _.template($('#folder-content-template').html(), {items: folderContainer.folder.models, id: options.id});
+ // that.$el.html(template);
+ // }
+ // })
+
+ // var folder = new Folder({id: options.id});
+ // folder.url = folder.urlRoot + options.id + '/contents';
+ // folder.fetch({
+ // success: function (folder) {
+ // // console.log(JSON.stringify(folder))
+ // //create the template
+ // var template = _.template($('#folder-content-template').html(), {items: folder.models, id: options.id});
+ // that.$el.html(template);
+ // }
+ // })
+
+ },
+ });
+
+
+ var libraryListView = new LibraryListView();
+ var folderContentView = new FolderContentView();
+
+ var Router = Backbone.Router.extend({
+ routes: {
+ "" : "libraries",
+ "folders/:id" : "folder_content"
+ }
+ });
+
+ var router = new Router;
+
+ router.on('route:libraries', function() {
+ // render libraries list
+ libraryListView.render();
+ })
+
+ router.on('route:folder_content', function(id, params) {
+ // render folder's content
+ folderContentView.render({id: id, params: params});
+ })
+
+ Backbone.history.start();
+ </script>
+
+
+</body>
+</html>
diff -r db8fd059cf4cae9a6b7dc8bdbe7be5d071325d09 -r 1eda20e058ebb08a4f4b71ed6988f6f106b62fdb static/AAA_scratch/data_library/index_working.html
--- /dev/null
+++ b/static/AAA_scratch/data_library/index_working.html
@@ -0,0 +1,261 @@
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>Data Library</title>
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.1.1/css/bootstrap…">
+</head>
+<body>
+
+
+ <div class="container">
+ <h1>Data Library</h1>
+ <hr />
+ <div class="page"></div>
+ </div>
+
+<!-- LIBRARY LIST TEMPLATE -->
+ <script type="text/template" id="library-list-template">
+ <a href="#/new" class="btn btn-primary">New Library</a>
+ <hr />
+ <table class="table striped">
+ <thead>
+ <th>name</th>
+ <th>description</th>
+ <th>synopsis</th>
+ <th></th>
+ <th></th>
+ </thead>
+ <tbody>
+ <% _.each(libraries, function(library) { %>
+ <tr>
+ <td><a href="#/folders/<%- library.id %>"><%- library.get('name') %></a></td>
+ <td><%= htmlEncode(library.get('description')) %></td>
+ <td><%= htmlEncode(library.get('synopsis')) %></td>
+ <td><a href="#/edit/<%= library.id %>" class="btn">Edit</td>
+ <td><button data-library-id="<%- library.id %>" class="btn btn-danger delete">Delete</button></td>
+ </tr>
+ <% }); %>
+ </tbody>
+ </table>
+ </script>
+
+<!-- LIBRARY EDIT/CREATE TEMPLATE -->
+ <script type="text/template" id="library-edit-template">
+ <form class="library-edit-form">
+ <legend>Create Library</legend>
+ <label>Name of the Library</label>
+ <input type="text" name="name" />
+ <label>Description</label>
+ <input type="text" name="description" />
+ <label>Synopsis</label>
+ <input type="text" name="synopsis" />
+ <hr />
+ <button type="submit" class="btn">Create Library</button>
+ </form>
+ </script>
+
+
+<!-- FOLDERS LIST TEMPLATE -->
+ <script type="text/template" id="folders-list-template">
+ <table class="table striped">
+ <thead>
+ <th>name</th>
+ <th>type</th>
+ </thead>
+ <tbody>
+ <% _.each(folders, function(folder) { %>
+ <tr>
+ <% if (folder.get('type') === 'folder') { %>
+ <td><a href="#/folders/<%- folder.id %>"><%- folder.get('name') %></a></td>
+ <% } else { %>
+ <td><%- folder.get('name') %></td>
+ <% } %>
+ <td><%= htmlEncode(folder.get('type')) %></td>
+ </tr>
+ <% }); %>
+ </tbody>
+ </table>
+ </script>
+
+
+ <script src="jquery-1.10.2.js" type="text/javascript"></script>
+ <script src="underscore.js" type="text/javascript"></script>
+ <script src="backbone.js" type="text/javascript"></script>
+
+
+ <script>
+
+ // ENCODE HTML
+ function htmlEncode(value){
+ return $('<div/>').text(value).html();
+ }
+
+ var api_key = 'e48b2b2589b242a50538f8f9fa8e5ee9';
+
+ // MODIFICATIONS DONE JUST BEFORE THE REQUEST ITSELF
+ $.ajaxPrefilter( function( options, originalOptions, jqXHR ) {
+ options.url = 'http://martenson.bx.psu.edu:8080' + options.url + '?key=' + api_key;
+ });
+
+ // SERIALIZE JSON
+ $.fn.serializeObject = function() {
+ var o = {};
+ var a = this.serializeArray();
+ $.each(a, function() {
+ if (o[this.name] !== undefined) {
+ if (!o[this.name].push) {
+ o[this.name] = [o[this.name]];
+ }
+ o[this.name].push(this.value || '');
+ } else {
+ o[this.name] = this.value || '';
+ }
+ });
+ return o;
+ };
+
+// LIBRARY
+ var Library = Backbone.Model.extend({
+ urlRoot: '/api/libraries'
+ });
+ var Libraries = Backbone.Collection.extend({
+ url: '/api/libraries'
+ });
+// FOLDER
+ var Folder = Backbone.Model.extend({
+ urlRoot: '/api/folders/'
+ })
+ var Folders = Backbone.Collection.extend({
+ model: Folder,
+ urlRoot: '/api/folders/'
+
+ })
+
+ var LibraryEditView = Backbone.View.extend({
+ el: '.page',
+ events: {
+ 'submit .library-edit-form': 'saveLibrary'
+ },
+ saveLibrary: function (ev) {
+ var libraryDetails = $(ev.currentTarget).serializeObject();
+ var library = new Library();
+ library.save(libraryDetails, {
+ success: function (library) {
+ router.navigate('', {trigger: true})
+ }
+ });
+ console.log(libraryDetails);
+ return false;
+ },
+ render: function (options) {
+ var that = this;
+ if (options.id) {
+ this.library = new Library({id: options.id});
+ this.library.fetch({
+ success: function(library){
+ var template = _.template($('#library-edit-template').html(), {library: library});
+ that.$el.html(template);
+ }
+ });
+ } else {
+ var template = _.template($('#library-edit-template').html(), {user: null});
+ this.$el.html(template);
+ }
+ }
+ });
+
+
+ var LibraryListView = Backbone.View.extend({
+ el: '.page',
+ events: {
+ 'click .delete' : 'deleteLibrary'
+ },
+ render: function () {
+ var that = this;
+ var libraries = new Libraries();
+ libraries.fetch({
+ success: function (libraries) {
+ var template = _.template($('#library-list-template').html(), {libraries: libraries.models});
+ that.$el.html(template);
+ }
+ })
+ },
+ deleteLibrary : function (ev) {
+ // DELETE /libraries/{id}
+ var id_to_delete = $(ev.target).attr('data-library-id');
+ var libraryToDelete = new Library({id: id_to_delete})
+ libraryToDelete.destroy({
+ success: function () {
+ libraryListView.render();
+ }
+ })
+ return false
+ },
+ createLibrary : function (ev) {
+ // PUT /libraries
+
+ }
+ });
+
+
+ var FoldersListView = Backbone.View.extend({
+ el: '.page',
+ render: function (options) {
+ var that = this;
+ var folders = new Folders({id: options.id});
+ folders.url = folders.urlRoot + options.id + '/contents';
+ folders.fetch({
+ success: function (folders) {
+ var template = _.template($('#folders-list-template').html(), {folders: folders.models, id: options.id});
+ that.$el.html(template);
+ }
+ })
+ },
+ });
+
+
+ var libraryListView = new LibraryListView();
+ var libraryEditView = new LibraryEditView();
+ var foldersListView = new FoldersListView();
+
+
+ var Router = Backbone.Router.extend({
+ routes: {
+ "" : "home",
+ "new" : "edit_library",
+ "edit/:id" : "edit_library",
+ "delete/:id" : "delete_library",
+ "folders/:id" : "folders"
+ }
+ });
+
+ var router = new Router;
+
+ router.on('route:home', function() {
+ // render libraries list
+ libraryListView.render();
+ })
+ router.on('route:edit_library', function(id) {
+ // render library edit form
+ libraryEditView.render({id: id});
+ })
+ router.on('route:delete_library', function() {
+ // render delete library form
+ libraryDeleteView.render();
+ })
+ // router.on('route:folders', function(parent_id, top_level) {
+ // // render folders list
+ // foldersListView.render({id: parent_id, top_level: true});
+ // })
+ router.on('route:folders', function(id) {
+ // render folders list
+ foldersListView.render({id: id});
+ })
+
+ Backbone.history.start();
+ </script>
+
+
+</body>
+</html>
https://bitbucket.org/galaxy/galaxy-central/commits/dc735a38cf76/
Changeset: dc735a38cf76
Branch: data_library
User: martenson
Date: 2013-10-24 20:22:26
Summary: refactoring, modals start
Affected #: 4 files
diff -r 1eda20e058ebb08a4f4b71ed6988f6f106b62fdb -r dc735a38cf76f93772027b34e489db1ef9d2d0a5 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -1835,7 +1835,7 @@
return name
class LibraryFolder( object, Dictifiable ):
- dict_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build' )
+ dict_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build', 'update_time' )
def __init__( self, name=None, description=None, item_count=0, order_id=None ):
self.name = name or "Unnamed folder"
self.description = description
diff -r 1eda20e058ebb08a4f4b71ed6988f6f106b62fdb -r dc735a38cf76f93772027b34e489db1ef9d2d0a5 lib/galaxy/webapps/galaxy/api/folder_contents.py
--- a/lib/galaxy/webapps/galaxy/api/folder_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/folder_contents.py
@@ -15,42 +15,37 @@
Class controls retrieval, creation and updating of folder contents.
"""
+ def load_folder_contents( self, trans, folder ):
+ """
+ Loads all contents of the folder (folders and data sets) but only in the first level.
+ """
+ current_user_roles = trans.get_current_user_roles()
+ is_admin = trans.user_is_admin()
+ content_items = []
+ for subfolder in folder.active_folders:
+ if not is_admin:
+ can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder )
+ if (is_admin or can_access) and not subfolder.deleted:
+ subfolder.api_type = 'folder'
+ content_items.append( subfolder )
+ for dataset in folder.datasets:
+ if not is_admin:
+ can_access = trans.app.security_agent.can_access_dataset( current_user_roles, dataset.library_dataset_dataset_association.dataset )
+ if (is_admin or can_access) and not dataset.deleted:
+ dataset.api_type = 'file'
+ content_items.append( dataset )
+ return content_items
+
@web.expose_api
def index( self, trans, folder_id, **kwd ):
"""
GET /api/folders/{encoded_folder_id}/contents
-
Displays a collection (list) of a folder's contents (files and folders).
-
- The /api/library_contents/{encoded_library_id}/contents
- lists everything in a library recursively, which is not what
- we want here. We could add a parameter to use the recursive
- style, but this is meant to act similar to an "ls" directory listing.
+ Encoded folder ID is prepended with 'F' if it is a folder as opposed to a data set which does not have it.
"""
folder_container = []
current_user_roles = trans.get_current_user_roles()
-
- def load_folder_contents( folder ):
- """
- Load contents of the folder (folders and datasets).
- """
- admin = trans.user_is_admin()
- rval = []
- for subfolder in folder.active_folders:
- if not admin:
- can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder )
- if (admin or can_access) and not subfolder.deleted:
- subfolder.api_type = 'folder'
- rval.append( subfolder )
- for ld in folder.datasets:
- if not admin:
- can_access = trans.app.security_agent.can_access_dataset( current_user_roles, ld.library_dataset_dataset_association.dataset )
- if (admin or can_access) and not ld.deleted:
- ld.api_type = 'file'
- rval.append( ld )
- return rval
-
try:
decoded_folder_id = trans.security.decode_id( folder_id[-16:] )
except TypeError:
@@ -59,69 +54,68 @@
try:
folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( decoded_folder_id )
-# log.debug("XXXXXXXXXXXXXXXXXXXXXXXXXXX folder.parent_library" + str(folder.parent_library.id))
-# log.debug("XXXXXXXXXXXXXXXXXXXXXXXXXXX folder.parent_id" + str(folder.parent_id))
parent_library = folder.parent_library
except:
folder = None
- log.error( "FolderContentsController.index: Unable to retrieve folder %s"
- % folder_id )
+ log.error( "FolderContentsController.index: Unable to retrieve folder with ID: %s" % folder_id )
- # TODO: Find the API's path to this folder if necessary.
- # This was needed in recursive descent, but it's not needed
- # for "ls"-style content checking:
- if not folder or not ( trans.user_is_admin() or trans.app.security_agent.can_access_library_item( current_user_roles, folder, trans.user ) ):
+ # We didn't find the folder or user does not have an access to it.
+ if not folder:
trans.response.status = 400
return "Invalid folder id ( %s ) specified." % str( folder_id )
- # TODO MARTEN Can it be that predecessors of current folder have different access rights? aka user shouldn't see them?
- # Search the path upwards and load the whole route of names and ids for breadcrumb purposes.
+ if not ( trans.user_is_admin() or trans.app.security_agent.can_access_library_item( current_user_roles, folder, trans.user ) ):
+ log.warning( "SECURITY: User (id: %s) without proper access rights is trying to load folder with ID of %s" % ( trans.user.id, folder.id ) )
+ trans.response.status = 400
+ return "Invalid folder id ( %s ) specified." % str( folder_id )
+
path_to_root = []
-
def build_path ( folder ):
+ """
+ Search the path upwards recursively and load the whole route of names and ids for breadcrumb purposes.
+ """
path_to_root = []
# We are almost in root
- log.debug( "XXXXXXXXXXXXXXXXXXXXXXX folder.parent_id: " + str( folder.parent_id ) )
- log.debug( "XXXXXXXXXXXXXXXXXXXXXXX folder.parent_library.id: " + str( folder.parent_library.id ) )
if folder.parent_id is None:
log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ALMOST ROOT FOLDER! ADDING: " + str( folder.name ) )
- path_to_root.append( ( folder.id, folder.name ) )
+ path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
# upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_library.id )
# path_to_root.append( ( upper_folder.id, upper_folder.name ) )
else:
# We add the current folder and traverse up one folder.
log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ADDING THIS FOLDER AND TRAVERSING UP: " + str( folder.name ) )
- path_to_root.append( ( folder.id, folder.name ) )
+ path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_id )
path_to_root.extend( build_path( upper_folder ) )
-# path_to_root = path_to_root.reverse()
-# return path_to_root[::-1]
return path_to_root
# Return the reversed path so it starts with the library node.
full_path = build_path( folder )[::-1]
folder_container.append( dict( full_path = full_path ) )
+
folder_contents = []
# Go through every item in the folder and include its meta-data.
- for content_item in load_folder_contents( folder ):
+ for content_item in self.load_folder_contents( trans, folder ):
return_item = {}
encoded_id = trans.security.encode_id( content_item.id )
# For folder return also hierarchy values
if content_item.api_type == 'folder':
- encoded_parent_library_id = trans.security.encode_id( content_item.parent_library.id )
+# encoded_parent_library_id = trans.security.encode_id( content_item.parent_library.id )
encoded_id = 'F' + encoded_id
- if content_item.parent_id is not None: # Return folder's parent id for browsing back.
- encoded_parent_id = 'F' + trans.security.encode_id( content_item.parent_id )
- return_item.update ( dict ( parent_id = encoded_parent_id ) )
+# if content_item.parent_id is not None: # Return folder's parent id for browsing back.
+# encoded_parent_id = 'F' + trans.security.encode_id( content_item.parent_id )
+ last_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
+ return_item.update ( dict ( item_count = content_item.item_count, last_updated = last_updated ) )
# For every item return also the default meta-data
return_item.update( dict( id = encoded_id,
type = content_item.api_type,
- name = content_item.name,
- library_id = encoded_parent_library_id
+ name = content_item.name
+
) )
folder_contents.append( return_item )
+ # Put the data in the container
folder_container.append( dict( folder_contents = folder_contents ) )
return folder_container
diff -r 1eda20e058ebb08a4f4b71ed6988f6f106b62fdb -r dc735a38cf76f93772027b34e489db1ef9d2d0a5 lib/galaxy/webapps/galaxy/api/libraries.py
--- a/lib/galaxy/webapps/galaxy/api/libraries.py
+++ b/lib/galaxy/webapps/galaxy/api/libraries.py
@@ -51,7 +51,7 @@
for library in query:
item = library.to_dict( view='element' )
item['url'] = url_for( route, id=trans.security.encode_id( library.id ) )
- item['id'] = trans.security.encode_id( item['id'] )
+ item['id'] = 'F' + trans.security.encode_id( item['id'] )
rval.append( item )
return rval
diff -r 1eda20e058ebb08a4f4b71ed6988f6f106b62fdb -r dc735a38cf76f93772027b34e489db1ef9d2d0a5 static/AAA_scratch/data_library/index.html
--- a/static/AAA_scratch/data_library/index.html
+++ b/static/AAA_scratch/data_library/index.html
@@ -1,10 +1,13 @@
-<!doctype html>
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>Data Library</title>
- <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.1.1/css/bootstrap…">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <!-- Bootstrap -->
+ <link href="css/bootstrap.min.css" rel="stylesheet" media="screen"></head>
+
<body>
@@ -16,7 +19,7 @@
<!-- LIBRARY LIST TEMPLATE --><script type="text/template" id="library-list-template">
- <table class="table striped">
+ <table class="table table-striped"><thead><th>name</th><th>description</th>
@@ -42,45 +45,47 @@
<!-- FOLDERS LIST TEMPLATE --><script type="text/template" id="folder-content-template">
- <table class="table striped">
+
+ <% _.each(path, function(path_item) { %>
+ <% if (path_item[0] != id) { %>
+ <a href='#/folders/<%- path_item[0] %>'><%- path_item[1] %></a> |
+ <% } else { %>
+ <%- path_item[1] %>
+ <% } %>
+ <% }); %>
+
+ <table class="table table-hover table-condensed"><thead>
+ <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th><th>name</th><th>type</th>
- <th>parent id</th>
- <th>id</th>
- <th>library id</th></thead><tbody><% _.each(items, function(content_item) { %>
- <tr>
+ <tr class="folder_row" id="<%- content_item.id %>">
+ <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td><% if (content_item.get('type') === 'folder') { %>
- <td><a href="#/folders/<%- content_item.id %>?library_id=<%- content_item.get('library_id') %>"><%- content_item.get('name') %></a></td>
+ <td><a href="#/folders/<%- content_item.id %>"><%- content_item.get('name') %></a>
+ <% if (content_item.get('item_count') === 0) { %>
+ <span class="muted">(empty folder)</span>
+ <% } %>
+ </td><% } else { %><td><%- content_item.get('name') %></td><% } %><td><%= htmlEncode(content_item.get('type')) %></td>
- <td><%= htmlEncode(content_item.get('parent_id')) %></td>
- <td><a href="#/folders/<%- content_item.id %>"><%= htmlEncode(content_item.get('id')) %></a></td>
- <td><a href="#/folders/<%- content_item.get('library_id') %>"><%= htmlEncode(content_item.get('library_id')) %></a></td></tr><% }); %></tbody></table>
- <hr/>
- <div>
- <% if ( typeof libraries !== undefined ) { %>
- <% _.each(libraries.models, function(library) { %>
- <%- library.get('name') %><br/>
- <% }); %>
- <% } else { %>
- libraries are undefined
- <% } %>
- </div>
+
</script>
+ <script src="require.js"></script><script src="jquery-1.10.2.js" type="text/javascript"></script>
+ <script src="js/bootstrap.min.js"></script><script src="underscore.js" type="text/javascript"></script><script src="backbone.js" type="text/javascript"></script><script src="backbone.queryparams.js" type="text/javascript"></script>
@@ -88,34 +93,27 @@
<script>
+ // configure require
+ // require.config({
+ // baseUrl: "/",
+ // shim: {
+ // "libs/underscore": { exports: "_" },
+ // "libs/backbone/backbone": { exports: "Backbone" },
+ // "libs/backbone/backbone-relational": ["libs/backbone/backbone"]
+ // }
+ // });
+
+ // require(['galaxy.modal'], function(modal)
+ // {
+ // Galaxy.modal = new modal.GalaxyModal();
+ // });
+
+
// ENCODE HTML
function htmlEncode(value){
return $('<div/>').text(value).html();
}
- // NESTED COLLECTIONS
- function nestCollection(model, attributeName, nestedCollection) {
- //setup nested references
- for (var i = 0; i < nestedCollection.length; i++) {
- model.attributes[attributeName][i] = nestedCollection.at(i).attributes;
- }
-
- //create empty arrays if none
- nestedCollection.bind('add', function (initiative) {
- if (!model.get(attributeName)) {
- model.attributes[attributeName] = [];
- }
- model.get(attributeName).push(initiative.attributes);
- });
-
- nestedCollection.bind('remove', function (initiative) {
- var updateObj = {};
- updateObj[attributeName] = _.without(model.get(attributeName), initiative.attributes);
- model.set(updateObj);
- });
- return nestedCollection;
- }
-
var api_key = 'e48b2b2589b242a50538f8f9fa8e5ee9';
// MODIFICATIONS DONE JUST BEFORE THE REQUEST ITSELF
@@ -167,13 +165,10 @@
id: "unknown"
},
parse: function(obj) {
- full_path = obj[0].full_path;
+ this.full_path = obj[0].full_path;
+ // this.folder.reset(obj[1].folder_contents);
// update the inner collection
this.get("folder").reset(obj[1].folder_contents);
-
- // this mightn't be necessary
- // delete obj.folder;
-
return obj;
}
})
@@ -197,41 +192,39 @@
var FolderContentView = Backbone.View.extend({
el: '.page',
+ events: {
+ 'click #select-all-checkboxes' : 'selectAll',
+ 'click .folder_row' : 'selectClicked'
+ },
render: function (options, params) {
var that = this;
var folderContainer = new FolderContainer({id: options.id});
folderContainer.url = folderContainer.attributes.urlRoot + options.id + '/contents';
- // var folder = new Folder({id: options.id});
folderContainer.fetch({
success: function (container) {
- folderContainer.attributes.folder = container.attributes.folder;
- var template = _.template($('#folder-content-template').html(), {items: folderContainer.attributes.folder.models, id: options.id});
+ // folderContainer.attributes.folder = container.attributes.folder;
+ var template = _.template($('#folder-content-template').html(), {path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id});
that.$el.html(template);
}
})
-
- // folder.fetch({
- // success: function (folder) {
- // folderContainer.folder = folder;
- // var template = _.template($('#folder-content-template').html(), {items: folderContainer.folder.models, id: options.id});
- // that.$el.html(template);
- // }
- // })
-
- // var folder = new Folder({id: options.id});
- // folder.url = folder.urlRoot + options.id + '/contents';
- // folder.fetch({
- // success: function (folder) {
- // // console.log(JSON.stringify(folder))
- // //create the template
- // var template = _.template($('#folder-content-template').html(), {items: folder.models, id: options.id});
- // that.$el.html(template);
- // }
- // })
-
},
+ selectAll : function (ev) {
+ var selected = ev.target.checked;
+ // Iterate each checkbox
+ $(':checkbox').each(function () { this.checked = selected; });
+ },
+ selectClicked : function (ev) {
+ var checkbox = $("#" + ev.target.parentElement.id).find(':checkbox')
+ if (checkbox[0] != undefined) {
+ if (checkbox[0].checked){
+ checkbox[0].checked = '';
+ } else {
+ checkbox[0].checked = 'selected';
+ }
+ }
+ }
});
@@ -258,8 +251,7 @@
})
Backbone.history.start();
+
</script>
-
-
</body></html>
https://bitbucket.org/galaxy/galaxy-central/commits/518ded2c3f30/
Changeset: 518ded2c3f30
Branch: data_library
User: martenson
Date: 2013-11-06 23:15:41
Summary: work in progress on data libraries, integrated within galaxy framework with require, modularized
Affected #: 7 files
diff -r dc735a38cf76f93772027b34e489db1ef9d2d0a5 -r 518ded2c3f30cca803a29f76da2e80505b166647 lib/galaxy/webapps/galaxy/controllers/library.py
--- a/lib/galaxy/webapps/galaxy/controllers/library.py
+++ b/lib/galaxy/webapps/galaxy/controllers/library.py
@@ -76,6 +76,22 @@
library_list_grid = LibraryListGrid()
+
+ @web.expose
+ def list( self, trans, **kwd ):
+ params = util.Params( kwd )
+
+ # define app configuration for generic mako template
+ app = {
+ 'jscript' : "galaxy.library"
+ }
+
+ # fill template
+ return trans.fill_template('galaxy.panels.mako', config = {'app' : app})
+
+
+# return trans.fill_template( "/library/list.mako")
+
@web.expose
def index( self, trans, **kwd ):
params = util.Params( kwd )
diff -r dc735a38cf76f93772027b34e489db1ef9d2d0a5 -r 518ded2c3f30cca803a29f76da2e80505b166647 static/AAA_scratch/data_library/index.html
--- a/static/AAA_scratch/data_library/index.html
+++ b/static/AAA_scratch/data_library/index.html
@@ -83,7 +83,7 @@
</script>
- <script src="require.js"></script>
+
<script src="jquery-1.10.2.js" type="text/javascript"></script><script src="js/bootstrap.min.js"></script><script src="underscore.js" type="text/javascript"></script>
@@ -93,22 +93,6 @@
<script>
- // configure require
- // require.config({
- // baseUrl: "/",
- // shim: {
- // "libs/underscore": { exports: "_" },
- // "libs/backbone/backbone": { exports: "Backbone" },
- // "libs/backbone/backbone-relational": ["libs/backbone/backbone"]
- // }
- // });
-
- // require(['galaxy.modal'], function(modal)
- // {
- // Galaxy.modal = new modal.GalaxyModal();
- // });
-
-
// ENCODE HTML
function htmlEncode(value){
return $('<div/>').text(value).html();
@@ -245,9 +229,9 @@
libraryListView.render();
})
- router.on('route:folder_content', function(id, params) {
+ router.on('route:folder_content', function(id) {
// render folder's content
- folderContentView.render({id: id, params: params});
+ folderContentView.render({id: id});
})
Backbone.history.start();
diff -r dc735a38cf76f93772027b34e489db1ef9d2d0a5 -r 518ded2c3f30cca803a29f76da2e80505b166647 static/scripts/galaxy.library.js
--- /dev/null
+++ b/static/scripts/galaxy.library.js
@@ -0,0 +1,333 @@
+// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+// === GALAXY LIBRARY MODULE ====
+// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+
+// global variables
+var view = null;
+var library_router = null;
+
+// dependencies
+define(["galaxy.modal", "galaxy.master", "galaxy.library.router"], function(mod_modal, mod_master, router) {
+
+// MMMMMMMMMMMMMMM
+// === Models ====
+// MMMMMMMMMMMMMMM
+
+ // LIBRARY
+ var Library = Backbone.Model.extend({
+ urlRoot: '/api/libraries',
+ });
+
+ // LIBRARIES
+ var Libraries = Backbone.Collection.extend({
+ url: '/api/libraries',
+ model: Library
+ });
+
+ // ITEM
+ var Item = Backbone.Model.extend({
+ })
+
+ // FOLDER
+ var Folder = Backbone.Collection.extend({
+ model: Item
+ })
+
+ // Container for folder contents (folders, items and metadata).
+ var FolderContainer = Backbone.Model.extend({
+ defaults: {
+ folder: new Folder(),
+ full_path: "unknown",
+ urlRoot: "/api/folders/",
+ id: "unknown"
+ },
+ parse: function(obj) {
+ this.full_path = obj[0].full_path;
+ // this.folder.reset(obj[1].folder_contents);
+ // update the inner collection
+ this.get("folder").reset(obj[1].folder_contents);
+ return obj;
+ }
+ })
+
+// MMMMMMMMMMMMMM
+// === Views ====
+// MMMMMMMMMMMMMM
+
+// galaxy folder
+var FolderContentView = Backbone.View.extend({
+ el : '#center',
+ // initialize
+ initialize : function(){
+ // view = this;
+ //set up the library router
+ // this.set_up_router();
+
+ //render
+ // this.render();
+ },
+ // set up router
+ set_up_router : function(){
+ if (library_router === null){
+ library_router = new router.LibraryRouter();
+ Backbone.history.start();
+ }
+ },
+ template_folder : function (){
+ tmpl_array = [];
+
+ tmpl_array.push('<% _.each(path, function(path_item) { %>');
+ tmpl_array.push('<% if (path_item[0] != id) { %>');
+ tmpl_array.push('<a href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a> |');
+ tmpl_array.push('<% } else { %>');
+ tmpl_array.push('<%- path_item[1] %>');
+ tmpl_array.push('<% } %>');
+ tmpl_array.push('<% }); %>');
+ tmpl_array.push('<table class="table table-hover table-condensed">');
+ tmpl_array.push(' <thead>');
+ tmpl_array.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');
+ tmpl_array.push(' <th>name</th>');
+ tmpl_array.push(' <th>type</th>');
+ tmpl_array.push(' </thead>');
+ tmpl_array.push(' <tbody>');
+ tmpl_array.push(' <% _.each(items, function(content_item) { %>');
+ tmpl_array.push(' <tr class="folder_row" id="<%- content_item.id %>">');
+ tmpl_array.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');
+ tmpl_array.push(' <% if (content_item.get("type") === "folder") { %>');
+ tmpl_array.push(' <td><a href="#/folders/<%- content_item.id %>"><%- content_item.get("name") %></a>');
+ tmpl_array.push(' <% if (content_item.get("item_count") === 0) { %>');
+ tmpl_array.push(' <span class="muted">(empty folder)</span>');
+ tmpl_array.push(' <% } %>');
+ tmpl_array.push(' </td>');
+ tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td><%- content_item.get("name") %></td>');
+ tmpl_array.push(' <% } %> ');
+ tmpl_array.push(' <td><%= _.escape(content_item.get("type")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push(' ');
+ tmpl_array.push(' ');
+ tmpl_array.push(' ');
+ tmpl_array.push(' </tbody>');
+ tmpl_array.push('</table>');
+
+ return tmpl_array.join('');
+ },
+ events: {
+ 'click #select-all-checkboxes' : 'selectAll',
+ 'click .folder_row' : 'selectClicked'
+ },
+ render: function (options) {
+ var that = this;
+
+ var folderContainer = new FolderContainer({id: options.id});
+ folderContainer.url = folderContainer.attributes.urlRoot + options.id + '/contents';
+
+ folderContainer.fetch({
+ success: function (container) {
+ // folderContainer.attributes.folder = container.attributes.folder;
+ var template = _.template(that.template_folder(), {path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id});
+ that.$el.html(template);
+ }
+ })
+ },
+ selectAll : function (ev) {
+ var selected = ev.target.checked;
+ // Iterate each checkbox
+ $(':checkbox').each(function () { this.checked = selected; });
+ },
+ selectClicked : function (ev) {
+ var checkbox = $("#" + ev.target.parentElement.id).find(':checkbox')
+ if (checkbox[0] != undefined) {
+ if (checkbox[0].checked){
+ checkbox[0].checked = '';
+ } else {
+ checkbox[0].checked = 'selected';
+ }
+ }
+ }
+});
+
+
+// galaxy library
+var GalaxyLibraryview = Backbone.View.extend({
+ el: '#center',
+
+ events: {
+ 'click #create_new_library_btn' : 'show_library_modal'
+ },
+
+
+ // initialize
+ initialize : function(){
+ // view = this;
+ //set up the libray router
+ // this.set_up_router();
+
+ //render
+ // this.render();
+ },
+
+ // set up router
+ set_up_router : function(){
+ if (library_router === null){
+ library_router = new router.LibraryRouter();
+ Backbone.history.start();
+ }
+ },
+
+ // template
+ template_library_list : function (){
+ tmpl_array = [];
+
+ tmpl_array.push('');
+ tmpl_array.push('<h1>Welcome to the data libraries</h1>');
+ tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary">New Library</a>');
+ tmpl_array.push('<table class="table table-striped">');
+ tmpl_array.push(' <thead>');
+ tmpl_array.push(' <th>name</th>');
+ tmpl_array.push(' <th>description</th>');
+ tmpl_array.push(' <th>synopsis</th> ');
+ tmpl_array.push(' <th>model type</th> ');
+ tmpl_array.push(' <th>id</th> ');
+ tmpl_array.push(' </thead>');
+ tmpl_array.push(' <tbody>');
+ tmpl_array.push(' <% _.each(libraries, function(library) { %>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <td><a href="#/folders/<%- library.id %>"><%- library.get("name") %></a></td>');
+ tmpl_array.push(' <td><%= _.escape(library.get("description")) %></td>');
+ tmpl_array.push(' <td><%= _.escape(library.get("synopsis")) %></td>');
+ tmpl_array.push(' <td><%= _.escape(library.get("model_class")) %></td>');
+ tmpl_array.push(' <td><a href="#/folders/<%- library.id %>"><%= _.escape(library.get("id")) %></a></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push('');
+ tmpl_array.push('');
+ tmpl_array.push('');
+ tmpl_array.push('');
+ tmpl_array.push(' </tbody>');
+ tmpl_array.push('</table>');
+
+ return tmpl_array.join('');
+ },
+
+ // render
+ render: function () {
+ var that = this;
+ // if (typeof libraries === "undefined") {
+ libraries = new Libraries();
+ // }
+ libraries.fetch({
+ success: function (libraries) {
+ var template = _.template(that.template_library_list(), {libraries: libraries.models});
+ that.$el.html(template);
+ }
+ })
+ },
+
+ // own modal
+ modal : null,
+
+ // show/hide create library modal
+ show_library_modal : function (e)
+ {
+ // prevent default
+ e.preventDefault();
+
+ // create modal
+ if (!this.modal){
+ // make modal
+ var self = this;
+ this.modal = new mod_modal.GalaxyModal(
+ {
+ title : 'Create New Library',
+ body : this.template_new_library(),
+ buttons : {
+ 'Create' : function() {self.create_new_library_event()},
+ 'Close' : function() {self.modal.hide()}
+ }
+ });
+ }
+
+ // show modal
+ this.modal.show();
+ },
+ create_new_library_event: function(){
+ var libraryDetails = this.serialize_new_library();
+ var library = new Library();
+ library.save(libraryDetails, {
+ success: function (library) {
+
+ library_router.navigate('', {trigger: true})
+ }
+ });
+ // console.log(libraryDetails);
+ return false;
+ },
+ serialize_new_library : function(){
+ return {
+ name: $("input[name='Name']").val(),
+ description: $("input[name='Description']").val(),
+ synopsis: $("input[name='Synopsis']").val()
+ };
+ },
+ // load html template
+ template_new_library: function()
+ {
+ tmpl_array = [];
+ tmpl_array.push('<div id="new_library_modal">');
+ tmpl_array.push('<form>');
+ tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');
+ tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');
+ // tmpl_array.push('<label>Synopsis</label>');
+ tmpl_array.push('<input type="text" name="Synopsis" value="" placeholder="Synopsis">');
+ tmpl_array.push('');
+ tmpl_array.push('</form>');
+ tmpl_array.push('</div>');
+ return tmpl_array.join('');
+
+
+ // return '<div id="'+ id +'"></div>';
+ }
+});
+
+// galaxy library wrapper View
+var GalaxyLibrary = Backbone.View.extend({
+ folderContentView : null,
+ galaxyLibraryview : null,
+ initialize : function(){
+
+ folderContentView = new FolderContentView();
+ galaxyLibraryview = new GalaxyLibraryview();
+
+ library_router = new router.LibraryRouter();
+
+ library_router.on('route:libraries', function() {
+ // render libraries list
+ galaxyLibraryview.render();
+ })
+
+ library_router.on('route:folder_content', function(id) {
+ // render folder's contents
+ folderContentView.render({id: id});
+
+ })
+
+ // library_router.on('route:show_library_modal', function() {
+ // // render folder's contents
+ // galaxyLibraryview.show_library_modal();
+
+ // })
+
+ Backbone.history.start();
+
+ return this
+ }
+});
+
+// return
+return {
+ GalaxyApp: GalaxyLibrary
+};
+
+});
diff -r dc735a38cf76f93772027b34e489db1ef9d2d0a5 -r 518ded2c3f30cca803a29f76da2e80505b166647 static/scripts/galaxy.library.router.js
--- /dev/null
+++ b/static/scripts/galaxy.library.router.js
@@ -0,0 +1,21 @@
+define( [], function() {
+
+/**
+ * -- Routers --
+ */
+
+/**
+ * Router for library browser.
+ */
+var LibraryRouter = Backbone.Router.extend({
+ routes: {
+ "" : "libraries",
+ "folders/:id" : "folder_content"
+ }
+});
+
+return {
+ LibraryRouter: LibraryRouter,
+};
+
+})
\ No newline at end of file
diff -r dc735a38cf76f93772027b34e489db1ef9d2d0a5 -r 518ded2c3f30cca803a29f76da2e80505b166647 static/style/Gruntfile.js
--- a/static/style/Gruntfile.js
+++ b/static/style/Gruntfile.js
@@ -6,7 +6,7 @@
var theme = grunt.option( 'theme', 'blue' );
var out = 'blue'
- var lessFiles = [ 'base', 'autocomplete_tagging', 'embed_item', 'iphone', 'masthead', 'library', 'trackster' ];
+ var lessFiles = [ 'base', 'autocomplete_tagging', 'embed_item', 'iphone', 'masthead', 'library', 'new_library', 'trackster' ];
var _ = grunt.util._;
var fmt = _.sprintf;
diff -r dc735a38cf76f93772027b34e489db1ef9d2d0a5 -r 518ded2c3f30cca803a29f76da2e80505b166647 templates/base/base_panels.mako
--- a/templates/base/base_panels.mako
+++ b/templates/base/base_panels.mako
@@ -102,12 +102,13 @@
});
## load galaxy js-modules
- require(['galaxy.master', 'galaxy.frame', 'galaxy.modal', 'galaxy.upload'], function(master, frame, modal, upload)
+ require(['galaxy.master', 'galaxy.frame', 'galaxy.modal', 'galaxy.upload', 'galaxy.library'], function(master, frame, modal, upload, library)
{
Galaxy.master = new master.GalaxyMaster();
Galaxy.frame_manager = new frame.GalaxyFrameManager();
Galaxy.modal = new modal.GalaxyModal();
- ##Galaxy.upload = new upload.GalaxyUpload();
+ Galaxy.upload = new upload.GalaxyUpload();
+ ##Galaxy.library = new library.GalaxyLibrary();
});
</script></%def>
diff -r dc735a38cf76f93772027b34e489db1ef9d2d0a5 -r 518ded2c3f30cca803a29f76da2e80505b166647 templates/webapps/galaxy/base_panels.mako
--- a/templates/webapps/galaxy/base_panels.mako
+++ b/templates/webapps/galaxy/base_panels.mako
@@ -126,6 +126,7 @@
## 'Shared Items' or Libraries tab.
<%
menu_options = [
+ [ _('New Libraries'), h.url_for( controller='/library', action='list') ],
[ _('Data Libraries'), h.url_for( controller='/library', action='index') ],
None,
[ _('Published Histories'), h.url_for( controller='/history', action='list_published' ) ],
https://bitbucket.org/galaxy/galaxy-central/commits/3ea8be4161ab/
Changeset: 3ea8be4161ab
Branch: data_library
User: martenson
Date: 2013-11-12 22:48:56
Summary: transfer into modal window, requirejs, notifications, import to history, galaxy.modal changes, API for library datasets, start of API versioning
Affected #: 6 files
diff -r 518ded2c3f30cca803a29f76da2e80505b166647 -r 3ea8be4161ab0a9ed2e1c17160c9d3d52d41b555 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -2000,6 +2000,7 @@
genome_build = ldda.dbkey,
misc_info = ldda.info,
misc_blurb = ldda.blurb,
+ peek = ( lambda ldda: ldda.display_peek() if ldda.peek and ldda.peek != 'no peek' else None )( ldda ),
template_data = template_data )
if ldda.dataset.uuid is None:
rval['uuid'] = None
diff -r 518ded2c3f30cca803a29f76da2e80505b166647 -r 3ea8be4161ab0a9ed2e1c17160c9d3d52d41b555 lib/galaxy/webapps/galaxy/api/lda_datasets.py
--- /dev/null
+++ b/lib/galaxy/webapps/galaxy/api/lda_datasets.py
@@ -0,0 +1,38 @@
+"""
+API operations on the contents of a dataset from library.
+"""
+from galaxy import web
+from galaxy.web.base.controller import BaseAPIController, UsesLibraryMixinItems
+
+import logging
+log = logging.getLogger( __name__ )
+
+class DatasetsController( BaseAPIController, UsesLibraryMixinItems ):
+
+ @web.expose_api
+ def index( self, trans, **kwd ):
+ """
+ GET /api/libraries/datasets
+ """
+ trans.response.status = 501
+ return 'not implemented'
+
+ @web.expose_api
+ def show( self, trans, id, **kwd ):
+ """
+ GET /api/libraries/datasets/{encoded_dataset_id}
+ Displays information about and/or content of a dataset identified by the lda ID.
+ """
+ # Get dataset.
+ try:
+ dataset = self.get_library_dataset( trans, id = id )
+ except Exception, e:
+ return str( e )
+ try:
+ # Default: return dataset as dict.
+ rval = dataset.to_dict()
+ except Exception, e:
+ rval = "Error in dataset API at listing contents: " + str( e )
+ log.error( rval + ": %s" % str(e), exc_info=True )
+ trans.response.status = 500
+ return rval
\ No newline at end of file
diff -r 518ded2c3f30cca803a29f76da2e80505b166647 -r 3ea8be4161ab0a9ed2e1c17160c9d3d52d41b555 lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -46,18 +46,19 @@
atexit.register( app.shutdown )
# Create the universe WSGI application
webapp = GalaxyWebApplication( app, session_cookie='galaxysession', name='galaxy' )
- # Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
- webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
- controller='admin_toolshed',
- action='display_image_in_repository',
- repository_id=None,
- image_file=None )
+
+ # Add the controllers folder
webapp.add_ui_controllers( 'galaxy.webapps.galaxy.controllers', app )
+ # Add the api folder
+ webapp.add_api_controllers( 'galaxy.webapps.galaxy.api', app )
+ # Add the api folder VERSION 2
+ webapp.add_api_controllers( 'galaxy.webapps.galaxy.api.v2', app )
+
# Force /history to go to /root/history -- needed since the tests assume this
webapp.add_route( '/history', controller='root', action='history' )
# Force /activate to go to the controller
webapp.add_route( '/activate', controller='user', action='activate' )
- # These two routes handle our simple needs at the moment
+
webapp.add_route( '/async/:tool_id/:data_id/:data_secret', controller='async', action='index', tool_id=None, data_id=None, data_secret=None )
webapp.add_route( '/:controller/:action', action='index' )
webapp.add_route( '/:action', controller='root', action='index' )
@@ -66,8 +67,8 @@
webapp.add_route( '/datasets/:dataset_id/display/{filename:.+?}', controller='dataset', action='display', dataset_id=None, filename=None)
webapp.add_route( '/datasets/:dataset_id/:action/:filename', controller='dataset', action='index', dataset_id=None, filename=None)
webapp.add_route( '/display_application/:dataset_id/:app_name/:link_name/:user_id/:app_action/:action_param',
- controller='dataset', action='display_application', dataset_id=None, user_id=None,
- app_name = None, link_name = None, app_action = None, action_param = None )
+ controller='dataset', action='display_application', dataset_id=None, user_id=None,
+ app_name = None, link_name = None, app_action = None, action_param = None )
webapp.add_route( '/u/:username/d/:slug/:filename', controller='dataset', action='display_by_username_and_slug', filename=None )
webapp.add_route( '/u/:username/p/:slug', controller='page', action='display_by_username_and_slug' )
webapp.add_route( '/u/:username/h/:slug', controller='history', action='display_by_username_and_slug' )
@@ -75,22 +76,6 @@
webapp.add_route( '/u/:username/v/:slug', controller='visualization', action='display_by_username_and_slug' )
webapp.add_route( '/search', controller='search', action='index' )
- # Add the web API
- webapp.add_api_controllers( 'galaxy.webapps.galaxy.api', app )
- # The /folders section is experimental at this point:
- log.debug( "app.config.api_folders: %s" % app.config.api_folders )
- webapp.mapper.resource( 'folder', 'folders', path_prefix='/api' )
- webapp.mapper.resource( 'content', 'contents',
- controller='folder_contents',
- name_prefix='folder_',
- path_prefix='/api/folders/:folder_id',
- parent_resources=dict( member_name='folder', collection_name='folders' ) )
- webapp.mapper.resource( 'content',
- 'contents',
- controller='library_contents',
- name_prefix='library_',
- path_prefix='/api/libraries/:library_id',
- parent_resources=dict( member_name='library', collection_name='libraries' ) )
webapp.mapper.resource( 'content',
'contents',
controller='history_contents',
@@ -102,10 +87,6 @@
controller="datasets",
action="display",
conditions=dict(method=["GET"]))
- webapp.mapper.resource( 'permission',
- 'permissions',
- path_prefix='/api/libraries/:library_id',
- parent_resources=dict( member_name='library', collection_name='libraries' ) )
webapp.mapper.resource( 'user',
'users',
controller='group_users',
@@ -127,11 +108,6 @@
_add_item_tags_controller( webapp,
name_prefix="workflow_",
path_prefix='/api/workflows/:workflow_id' )
-
- _add_item_extended_metadata_controller( webapp,
- name_prefix="library_dataset_",
- path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
-
_add_item_annotation_controller( webapp,
name_prefix="history_content_",
path_prefix='/api/histories/:history_id/contents/:history_content_id' )
@@ -147,31 +123,58 @@
path_prefix='/api/histories/:history_id/contents/:history_content_id' )
webapp.mapper.resource( 'dataset', 'datasets', path_prefix='/api' )
- webapp.mapper.resource_with_deleted( 'library', 'libraries', path_prefix='/api' )
webapp.mapper.resource( 'sample', 'samples', path_prefix='/api' )
webapp.mapper.resource( 'request', 'requests', path_prefix='/api' )
webapp.mapper.resource( 'form', 'forms', path_prefix='/api' )
webapp.mapper.resource( 'request_type', 'request_types', path_prefix='/api' )
webapp.mapper.resource( 'role', 'roles', path_prefix='/api' )
webapp.mapper.resource( 'group', 'groups', path_prefix='/api' )
- webapp.mapper.resource_with_deleted( 'quota', 'quotas', path_prefix='/api' )
webapp.mapper.resource( 'tool', 'tools', path_prefix='/api' )
- webapp.mapper.resource_with_deleted( 'user', 'users', path_prefix='/api' )
webapp.mapper.resource( 'genome', 'genomes', path_prefix='/api' )
webapp.mapper.resource( 'visualization', 'visualizations', path_prefix='/api' )
webapp.mapper.resource( 'workflow', 'workflows', path_prefix='/api' )
- webapp.mapper.resource_with_deleted( 'history', 'histories', path_prefix='/api' )
webapp.mapper.resource( 'configuration', 'configuration', path_prefix='/api' )
#webapp.mapper.connect( 'run_workflow', '/api/workflow/{workflow_id}/library/{library_id}', controller='workflows', action='run', workflow_id=None, library_id=None, conditions=dict(method=["GET"]) )
webapp.mapper.resource( 'search', 'search', path_prefix='/api' )
+ webapp.mapper.resource_with_deleted( 'quota', 'quotas', path_prefix='/api' )
+ webapp.mapper.resource_with_deleted( 'history', 'histories', path_prefix='/api' )
+ webapp.mapper.resource_with_deleted( 'user', 'users', path_prefix='/api' )
+
+ # =======================
+ # ===== LIBRARY API =====
+ # =======================
+
+ webapp.mapper.connect( 'show_lda_item', '/api/libraries/datasets/:id', controller='lda_datasets', action='show', conditions=dict(method=["GET"]) )
+
+ webapp.mapper.resource_with_deleted( 'library', 'libraries', path_prefix='/api' )
+ webapp.mapper.resource( 'folder', 'folders', path_prefix='/api' )
+
+ webapp.mapper.resource( 'content', 'contents',
+ controller='folder_contents',
+ name_prefix='folder_',
+ path_prefix='/api/folders/:folder_id',
+ parent_resources=dict( member_name='folder', collection_name='folders' ) )
+ webapp.mapper.resource( 'content',
+ 'contents',
+ controller='library_contents',
+ name_prefix='library_',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
+ webapp.mapper.resource( 'permission',
+ 'permissions',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
+ _add_item_extended_metadata_controller( webapp,
+ name_prefix="library_dataset_",
+ path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
+
+
# add as a non-ATOM API call to support the notion of a 'current/working' history unique to the history resource
- webapp.mapper.connect( "set_as_current", "/api/histories/{id}/set_as_current",
- controller="histories", action="set_as_current", conditions=dict( method=["POST"] ) )
+ webapp.mapper.connect( "set_as_current", "/api/histories/{id}/set_as_current", controller="histories", action="set_as_current", conditions=dict( method=["POST"] ) )
# visualizations registry generic template renderer
- webapp.add_route( '/visualization/show/:visualization_name',
- controller='visualization', action='render', visualization_name=None )
+ webapp.add_route( '/visualization/show/:visualization_name', controller='visualization', action='render', visualization_name=None )
# "POST /api/workflows/import" => ``workflows.import_workflow()``.
# Defines a named route "import_workflow".
@@ -179,6 +182,15 @@
webapp.mapper.connect("workflow_dict", '/api/workflows/{workflow_id}/download', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
# Preserve the following download route for now for dependent applications -- deprecate at some point
webapp.mapper.connect("workflow_dict", '/api/workflows/download/{workflow_id}', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
+
+
+ # Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
+ webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
+ controller='admin_toolshed',
+ action='display_image_in_repository',
+ repository_id=None,
+ image_file=None )
+
# Galaxy API for tool shed features.
webapp.mapper.resource( 'tool_shed_repository',
'tool_shed_repositories',
@@ -191,6 +203,7 @@
path_prefix='/api',
new={ 'install_repository_revision' : 'POST' },
parent_resources=dict( member_name='tool_shed_repository', collection_name='tool_shed_repositories' ) )
+
# Connect logger from app
if app.trace_logger:
webapp.trace_logger = app.trace_logger
diff -r 518ded2c3f30cca803a29f76da2e80505b166647 -r 3ea8be4161ab0a9ed2e1c17160c9d3d52d41b555 lib/galaxy/webapps/galaxy/controllers/library.py
--- a/lib/galaxy/webapps/galaxy/controllers/library.py
+++ b/lib/galaxy/webapps/galaxy/controllers/library.py
@@ -80,18 +80,13 @@
@web.expose
def list( self, trans, **kwd ):
params = util.Params( kwd )
-
# define app configuration for generic mako template
app = {
'jscript' : "galaxy.library"
}
-
# fill template
return trans.fill_template('galaxy.panels.mako', config = {'app' : app})
-
-
-# return trans.fill_template( "/library/list.mako")
-
+
@web.expose
def index( self, trans, **kwd ):
params = util.Params( kwd )
diff -r 518ded2c3f30cca803a29f76da2e80505b166647 -r 3ea8be4161ab0a9ed2e1c17160c9d3d52d41b555 static/scripts/galaxy.library.js
--- a/static/scripts/galaxy.library.js
+++ b/static/scripts/galaxy.library.js
@@ -7,7 +7,7 @@
var library_router = null;
// dependencies
-define(["galaxy.modal", "galaxy.master", "galaxy.library.router"], function(mod_modal, mod_master, router) {
+define(["galaxy.modal", "galaxy.master"], function(mod_modal, mod_master) {
// MMMMMMMMMMMMMMM
// === Models ====
@@ -15,40 +15,65 @@
// LIBRARY
var Library = Backbone.Model.extend({
- urlRoot: '/api/libraries',
- });
+ urlRoot: '/api/libraries'
+ });
// LIBRARIES
var Libraries = Backbone.Collection.extend({
url: '/api/libraries',
model: Library
- });
+ });
// ITEM
var Item = Backbone.Model.extend({
+ urlRoot : '/api/libraries/datasets'
})
// FOLDER
var Folder = Backbone.Collection.extend({
model: Item
- })
+ })
- // Container for folder contents (folders, items and metadata).
+ // CONTAINER for folder contents (folders, items and metadata).
var FolderContainer = Backbone.Model.extend({
- defaults: {
- folder: new Folder(),
- full_path: "unknown",
- urlRoot: "/api/folders/",
- id: "unknown"
- },
- parse: function(obj) {
- this.full_path = obj[0].full_path;
- // this.folder.reset(obj[1].folder_contents);
+ defaults : {
+ folder : new Folder(),
+ full_path : "unknown",
+ urlRoot : "/api/folders/",
+ id : "unknown"
+ },
+ parse : function(obj) {
+ this.full_path = obj[0].full_path;
// update the inner collection
this.get("folder").reset(obj[1].folder_contents);
return obj;
+ }
+ })
+
+ // HISTORY ITEM
+ var HistoryItem = Backbone.Model.extend({
+ urlRoot : '/api/histories/'
+ });
+
+ // HISTORY
+ var GalaxyHistory = Backbone.Model.extend({
+ url : '/api/histories/'
+ });
+
+ // HISTORIES
+ var GalaxyHistories = Backbone.Collection.extend({
+ url : '/api/histories',
+ model : GalaxyHistory
+ });
+
+ //ROUTER
+ var LibraryRouter = Backbone.Router.extend({
+ routes: {
+ "" : "libraries",
+ "folders/:id" : "folder_content"
}
- })
+ });
+
// MMMMMMMMMMMMMM
// === Views ====
@@ -56,67 +81,155 @@
// galaxy folder
var FolderContentView = Backbone.View.extend({
- el : '#center',
+ //main element
+ el : '#center',
// initialize
initialize : function(){
- // view = this;
- //set up the library router
- // this.set_up_router();
+ this.folders = [];
+ },
+ // set up
+ templateFolder : function (){
+ var tmpl_array = [];
- //render
- // this.render();
- },
- // set up router
- set_up_router : function(){
- if (library_router === null){
- library_router = new router.LibraryRouter();
- Backbone.history.start();
- }
+ tmpl_array.push('<a href="#">Libraries</a> | ');
+ tmpl_array.push('<% _.each(path, function(path_item) { %>'); //breadcrumb
+ tmpl_array.push('<% if (path_item[0] != id) { %>');
+ tmpl_array.push('<a href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a> |');
+ tmpl_array.push('<% } else { %>');
+ tmpl_array.push('<%- path_item[1] %>');
+ tmpl_array.push('<% } %>');
+ tmpl_array.push('<% }); %>');
+ tmpl_array.push('<table class="table table-hover table-condensed">');
+ tmpl_array.push(' <thead>');
+ tmpl_array.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');
+ tmpl_array.push(' <th>name</th>');
+ tmpl_array.push(' <th>type</th>');
+ tmpl_array.push(' </thead>');
+ tmpl_array.push(' <tbody>');
+ tmpl_array.push(' <% _.each(items, function(content_item) { %>');
+ tmpl_array.push(' <tr class="folder_row" id="<%- content_item.id %>">');
+ tmpl_array.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');
+ tmpl_array.push(' <% if (content_item.get("type") === "folder") { %>'); //folder
+ tmpl_array.push(' <td><a href="#/folders/<%- content_item.id %>"><%- content_item.get("name") %></a>');
+ tmpl_array.push(' <% if (content_item.get("item_count") === 0) { %>'); //empty folder
+ tmpl_array.push(' <span class="muted">(empty folder)</span>');
+ tmpl_array.push(' <% } %>');
+ tmpl_array.push(' </td>');
+ tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td><a class="library-dataset" href="#"><%- content_item.get("name") %></a></td>'); //dataset
+ tmpl_array.push(' <% } %> ');
+ tmpl_array.push(' <td><%= _.escape(content_item.get("type")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push(' ');
+ tmpl_array.push(' ');
+ tmpl_array.push(' ');
+ tmpl_array.push(' </tbody>');
+ tmpl_array.push('</table>');
+
+ return tmpl_array.join('');
},
- template_folder : function (){
- tmpl_array = [];
+ templateDatasetModal : function(){
+ var tmpl_array = [];
- tmpl_array.push('<% _.each(path, function(path_item) { %>');
- tmpl_array.push('<% if (path_item[0] != id) { %>');
- tmpl_array.push('<a href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a> |');
- tmpl_array.push('<% } else { %>');
- tmpl_array.push('<%- path_item[1] %>');
- tmpl_array.push('<% } %>');
- tmpl_array.push('<% }); %>');
- tmpl_array.push('<table class="table table-hover table-condensed">');
- tmpl_array.push(' <thead>');
- tmpl_array.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');
- tmpl_array.push(' <th>name</th>');
- tmpl_array.push(' <th>type</th>');
- tmpl_array.push(' </thead>');
- tmpl_array.push(' <tbody>');
- tmpl_array.push(' <% _.each(items, function(content_item) { %>');
- tmpl_array.push(' <tr class="folder_row" id="<%- content_item.id %>">');
- tmpl_array.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');
- tmpl_array.push(' <% if (content_item.get("type") === "folder") { %>');
- tmpl_array.push(' <td><a href="#/folders/<%- content_item.id %>"><%- content_item.get("name") %></a>');
- tmpl_array.push(' <% if (content_item.get("item_count") === 0) { %>');
- tmpl_array.push(' <span class="muted">(empty folder)</span>');
- tmpl_array.push(' <% } %>');
- tmpl_array.push(' </td>');
- tmpl_array.push(' <% } else { %>');
- tmpl_array.push(' <td><%- content_item.get("name") %></td>');
- tmpl_array.push(' <% } %> ');
- tmpl_array.push(' <td><%= _.escape(content_item.get("type")) %></td>');
- tmpl_array.push(' </tr>');
- tmpl_array.push(' <% }); %>');
- tmpl_array.push(' ');
- tmpl_array.push(' ');
- tmpl_array.push(' ');
- tmpl_array.push(' </tbody>');
- tmpl_array.push('</table>');
+ tmpl_array.push('<div id="dataset_info_modal">');
+ tmpl_array.push(' <table class="table table-striped table-condensed">');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Name</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("name")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Data type</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("data_type")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Genome build</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("genome_build")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <th scope="row">Size</th>');
+ tmpl_array.push(' <td><%= _.escape(size) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Date uploaded</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("date_uploaded")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Uploaded by</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("uploaded_by")) %></td>');
+ tmpl_array.push(' </tr>');
+ // tmpl_array.push(' </table>');
+ // tmpl_array.push(' <hr/>');
+ // tmpl_array.push(' <table class="table table-striped">');
+ tmpl_array.push(' <tr scope="row">');
+ tmpl_array.push(' <th scope="row">Data Lines</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_data_lines")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <th scope="row">Comment Lines</th>');
+ tmpl_array.push(' <% if (item.get("metadata_comment_lines") === "") { %>'); //folder
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_comment_lines")) %></td>');
+ tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td scope="row">unknown</td>');
+ tmpl_array.push(' <% } %>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Number of Columns</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_columns")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Column Types</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_column_types")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Miscellaneous information</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("misc_blurb")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' ');
+ tmpl_array.push(' </table>');
+ // tmpl_array.push(' <hr/>');
+ tmpl_array.push(' <pre class="peek">');
+ tmpl_array.push(' </pre>');
+ tmpl_array.push('</div>');
- return tmpl_array.join('');
- },
- events: {
- 'click #select-all-checkboxes' : 'selectAll',
- 'click .folder_row' : 'selectClicked'
- },
+ return tmpl_array.join('');
+ },
+ templateHistorySelectInModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<span id="history_modal_footer" style="width:60%;">');
+ tmpl_array.push('<select name="history_import" style="width:60%; margin-left: 2em; "> ');
+ tmpl_array.push(' <% _.each(histories, function(history) { %>'); //history select box
+ tmpl_array.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push('</span>');
+ tmpl_array.push('</div>');
+ tmpl_array.push('');
+
+ return tmpl_array.join('');
+ },
+ // to string
+ size_to_string : function (size)
+ {
+ // identify unit
+ var unit = "";
+ if (size >= 100000000000) { size = size / 100000000000; unit = "TB"; } else
+ if (size >= 100000000) { size = size / 100000000; unit = "GB"; } else
+ if (size >= 100000) { size = size / 100000; unit = "MB"; } else
+ if (size >= 100) { size = size / 100; unit = "KB"; } else
+ { size = size * 10; unit = "b"; }
+ // return formatted string
+ return (Math.round(size) / 10) + unit;
+ },
+ events: {
+ 'click #select-all-checkboxes' : 'selectAll',
+ 'click .folder_row' : 'selectClicked',
+ 'click .library-dataset' : 'showDatasetDetails'
+ },
+ //self modal
+ modal : null,
+ //loaded folders
+ folders : null,
+ //render the view
render: function (options) {
var that = this;
@@ -125,80 +238,131 @@
folderContainer.fetch({
success: function (container) {
- // folderContainer.attributes.folder = container.attributes.folder;
- var template = _.template(that.template_folder(), {path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id});
- that.$el.html(template);
+ // folderContainer.attributes.folder = container.attributes.folder;
+ var template = _.template(that.templateFolder(), {path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id});
+ that.$el.html(template);
}
- })
- },
- selectAll : function (ev) {
- var selected = ev.target.checked;
- // Iterate each checkbox
- $(':checkbox').each(function () { this.checked = selected; });
- },
- selectClicked : function (ev) {
- var checkbox = $("#" + ev.target.parentElement.id).find(':checkbox')
- if (checkbox[0] != undefined) {
- if (checkbox[0].checked){
- checkbox[0].checked = '';
- } else {
- checkbox[0].checked = 'selected';
- }
+ })
+ },
+ //show modal with dataset info
+ showDatasetDetails : function(e){
+ // prevent default
+ e.preventDefault();
+
+ //TODO check whether we already have the data
+
+ //load the ID of the row
+ var id = $(e.target).parent().parent().attr('id');
+
+ //create new item
+ var item = new Item();
+ var histories = new GalaxyHistories();
+ item.id = id;
+ var self = this;
+ //fetch the dataset info
+ item.fetch({
+ success: function (item) {
+ //fetch user histories for import
+ histories.fetch({
+ success: function (histories){self.modalFetchSuccess(item, histories)}
+ });
+ }
+ });
+ },
+
+ modalFetchSuccess : function(item, histories){
+ var histories_modal = this.templateHistorySelectInModal();
+ var size = this.size_to_string(item.get('file_size'));
+ var template = _.template(this.templateDatasetModal(), { item : item, size : size });
+ // make modal
+ var self = this;
+ this.modal = new mod_modal.GalaxyModal({
+ title : 'Dataset Details',
+ body : template,
+ buttons : {
+ 'Import' : function() {self.importIntoHistory()},
+ // 'Notify' : function() {self.modal.showNotification("TEST")},
+ 'Close' : function() {self.modal.hide()}
+ }
+ });
+ $(".peek").html(item.get("peek"));
+ // this.modal.hideButton('Import');
+ // $(this.modal.elMain).find('.modal-footer').prepend("<div>BUBUBUBU" + "</div>");
+ var history_footer_tmpl = _.template(this.templateHistorySelectInModal(), {histories : histories.models});
+
+ $(this.modal.elMain).find('.buttons').prepend(history_footer_tmpl);
+
+ // show the prepared modal
+ this.modal.show();
+ },
+
+ importIntoHistory : function(){
+ var history_id = 'a0c15f4d91084599';
+ var library_dataset_id = '03501d7626bd192f';
+
+ var historyItem = new HistoryItem();
+ var self = this;
+ historyItem.url = historyItem.urlRoot + history_id + '/contents';
+ console.log(historyItem);
+ historyItem.save({ content : library_dataset_id, source : 'library' }, { success : function(){
+ self.modal.showNotification('Dataset imported', 3000, '#e1f4e0', '#32a427');
+ }
+ });
+ },
+
+ selectAll : function (ev) {
+ var selected = ev.target.checked;
+ // Iterate each checkbox
+ $(':checkbox').each(function () { this.checked = selected; });
+ },
+ selectClicked : function (ev) {
+ var checkbox = $("#" + ev.target.parentElement.id).find(':checkbox')
+ if (checkbox[0] != undefined) {
+ if (checkbox[0].checked){
+ checkbox[0].checked = '';
+ } else {
+ checkbox[0].checked = 'selected';
+ }
+ }
}
- }
-});
+ });
// galaxy library
var GalaxyLibraryview = Backbone.View.extend({
el: '#center',
- events: {
+ events: {
'click #create_new_library_btn' : 'show_library_modal'
- },
+ },
-
// initialize
initialize : function(){
- // view = this;
- //set up the libray router
- // this.set_up_router();
-
- //render
- // this.render();
},
- // set up router
- set_up_router : function(){
- if (library_router === null){
- library_router = new router.LibraryRouter();
- Backbone.history.start();
- }
- },
-
// template
template_library_list : function (){
tmpl_array = [];
tmpl_array.push('');
tmpl_array.push('<h1>Welcome to the data libraries</h1>');
- tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary">New Library</a>');
+ tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary icon-file ">New Library</a>');
tmpl_array.push('<table class="table table-striped">');
tmpl_array.push(' <thead>');
tmpl_array.push(' <th>name</th>');
tmpl_array.push(' <th>description</th>');
tmpl_array.push(' <th>synopsis</th> ');
tmpl_array.push(' <th>model type</th> ');
- tmpl_array.push(' <th>id</th> ');
+ // tmpl_array.push(' <th>id</th> ');
tmpl_array.push(' </thead>');
tmpl_array.push(' <tbody>');
tmpl_array.push(' <% _.each(libraries, function(library) { %>');
- tmpl_array.push(' <tr>');
- tmpl_array.push(' <td><a href="#/folders/<%- library.id %>"><%- library.get("name") %></a></td>');
- tmpl_array.push(' <td><%= _.escape(library.get("description")) %></td>');
- tmpl_array.push(' <td><%= _.escape(library.get("synopsis")) %></td>');
- tmpl_array.push(' <td><%= _.escape(library.get("model_class")) %></td>');
- tmpl_array.push(' <td><a href="#/folders/<%- library.id %>"><%= _.escape(library.get("id")) %></a></td>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <td><a href="#/folders/<%- library.id %>"><%- library.get("name") %></a></td>');
+ tmpl_array.push(' <td><%= _.escape(library.get("description")) %></td>');
+ tmpl_array.push(' <td><%= _.escape(library.get("synopsis")) %></td>');
+ tmpl_array.push(' <td><%= _.escape(library.get("model_class")) %></td>');
+ // tmpl_array.push(' <td><a href="#/folders/<%- library.id %>"><%= _.escape(library.get("id")) %></a></td>');
tmpl_array.push(' </tr>');
tmpl_array.push(' <% }); %>');
tmpl_array.push('');
@@ -219,18 +383,17 @@
// }
libraries.fetch({
success: function (libraries) {
- var template = _.template(that.template_library_list(), {libraries: libraries.models});
+ var template = _.template(that.template_library_list(), { libraries : libraries.models });
that.$el.html(template);
- }
- })
+ }
+ })
},
// own modal
modal : null,
// show/hide create library modal
- show_library_modal : function (e)
- {
+ show_library_modal : function (e){
// prevent default
e.preventDefault();
@@ -255,15 +418,21 @@
create_new_library_event: function(){
var libraryDetails = this.serialize_new_library();
var library = new Library();
+ var self = this;
library.save(libraryDetails, {
success: function (library) {
-
- library_router.navigate('', {trigger: true})
- }
- });
- // console.log(libraryDetails);
+ self.modal.hide();
+ self.clear_library_modal();
+ self.render();
+ }
+ });
return false;
},
+ clear_library_modal : function(){
+ $("input[name='Name']").val('');
+ $("input[name='Description']").val('');
+ $("input[name='Synopsis']").val('');
+ },
serialize_new_library : function(){
return {
name: $("input[name='Name']").val(),
@@ -279,7 +448,6 @@
tmpl_array.push('<form>');
tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');
tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');
- // tmpl_array.push('<label>Synopsis</label>');
tmpl_array.push('<input type="text" name="Synopsis" value="" placeholder="Synopsis">');
tmpl_array.push('');
tmpl_array.push('</form>');
@@ -300,18 +468,18 @@
folderContentView = new FolderContentView();
galaxyLibraryview = new GalaxyLibraryview();
- library_router = new router.LibraryRouter();
-
+ library_router = new LibraryRouter();
+
library_router.on('route:libraries', function() {
// render libraries list
galaxyLibraryview.render();
- })
-
+ })
+
library_router.on('route:folder_content', function(id) {
// render folder's contents
folderContentView.render({id: id});
-
- })
+
+ })
// library_router.on('route:show_library_modal', function() {
// // render folder's contents
@@ -319,10 +487,10 @@
// })
- Backbone.history.start();
+Backbone.history.start();
- return this
- }
+return this
+}
});
// return
diff -r 518ded2c3f30cca803a29f76da2e80505b166647 -r 3ea8be4161ab0a9ed2e1c17160c9d3d52d41b555 static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -82,6 +82,7 @@
this.$footer = (this.$el).find('.modal-footer');
this.$buttons = (this.$el).find('.buttons');
this.$backdrop = (this.$el).find('.modal-backdrop');
+ this.$notification = (this.$el).find('.notification-modal');
// append body
this.$body.html(options.body);
@@ -122,6 +123,35 @@
this.$buttons.find('#' + String(name).toLowerCase()).prop('disabled', true);
},
+ // hide buttons
+ hideButton: function(name) {
+ this.$buttons.find('#' + String(name).toLowerCase()).hide();
+ },
+ // show buttons
+ showButton: function(name) {
+ this.$buttons.find('#' + String(name).toLowerCase()).show();
+ },
+
+ // show notification
+ showNotification : function(message, duration, bgColor, txtColor) {
+ // defaults
+ var duration = typeof duration !== 'undefined' ? duration : 1500;
+ var bgColor = typeof bgColor !== 'undefined' ? bgColor : "#F4E0E1";
+ var txtColor = typeof txtColor !== 'undefined' ? txtColor : "#A42732";
+
+ var HTMLmessage = "<div class='notification-message' style='text-align:center; line-height:16px; '> " + message + " </div>";
+ this.$notification.html("<div id='notification-bar' style='display:none; float: right; height: 16px; width:100%; background-color: " + bgColor + "; z-index: 100; color: " + txtColor + ";border-bottom: 1px solid " + txtColor + ";'>" + HTMLmessage + "</div>");
+
+ var self = this;
+ /*animate the bar*/
+ $('#notification-bar').slideDown(function() {
+ setTimeout(function() {
+ $('#notification-bar').slideUp(function() {self.$notification.html('');});
+ }, duration);
+ });
+
+ },
+
// returns scroll top for body element
scrollTop: function()
{
@@ -137,10 +167,11 @@
return '<div class="modal">' +
'<div class="modal-backdrop fade in" style="z-index: -1;"></div>' +
'<div class="modal-dialog">' +
- '<div class="modal-content">' +
+ '<div class="modal-content"">' +
'<div class="modal-header">' +
'<button type="button" class="close" style="display: none;">×</button>' +
'<h4 class="title">' + title + '</h4>' +
+ '<span class="notification-modal"></span>' +
'</div>' +
'<div class="modal-body"></div>' +
'<div class="modal-footer">' +
https://bitbucket.org/galaxy/galaxy-central/commits/2bb7df095a5a/
Changeset: 2bb7df095a5a
Branch: data_library
User: martenson
Date: 2013-12-03 22:43:06
Summary: slight refactoring and bugfix of db_next_hid generator
Affected #: 1 file
diff -r 3ea8be4161ab0a9ed2e1c17160c9d3d52d41b555 -r 2bb7df095a5a23e3dce2e02fb963a30ff3b7d377 lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -1988,8 +1988,18 @@
table = self.table
trans = conn.begin()
try:
- next_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
- table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid + 1 ) )
+
+# log.debug("XXXXXXXXXXXXXXX NEW GENERATOOOOOR")
+# next_hid = table.update() \
+# .returning( table.c.hid_counter ) \
+# .where( table.c.id == self.id ) \
+# .values( hid_counter = hid_counter + 1 )
+# log.debug('XXXXXXXXXXXXXXXXXXXXXXXXXXX NEXT HID: ' + str(next_hid))
+
+
+ current_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
+ next_hid = current_hid + 1
+ table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid ) )
trans.commit()
return next_hid
except:
https://bitbucket.org/galaxy/galaxy-central/commits/4e53d7b759d1/
Changeset: 4e53d7b759d1
Branch: data_library
User: martenson
Date: 2013-12-03 22:43:25
Summary: added dropdown menu plugin to bootstrap
Affected #: 1 file
diff -r 2bb7df095a5a23e3dce2e02fb963a30ff3b7d377 -r 4e53d7b759d1999758a30611e5d897e3e1d20782 static/scripts/libs/bootstrap.js
--- a/static/scripts/libs/bootstrap.js
+++ b/static/scripts/libs/bootstrap.js
@@ -575,3 +575,158 @@
}
}(window.jQuery);
+
+/* ========================================================================
+ * Bootstrap: dropdown.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#dropdowns
+ * ========================================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // DROPDOWN CLASS DEFINITION
+ // =========================
+
+ var backdrop = '.dropdown-backdrop'
+ var toggle = '[data-toggle=dropdown]'
+ var Dropdown = function (element) {
+ var $el = $(element).on('click.bs.dropdown', this.toggle)
+ }
+
+ Dropdown.prototype.toggle = function (e) {
+ var $this = $(this)
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ clearMenus()
+
+ if (!isActive) {
+ if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
+ // if mobile we we use a backdrop because click events don't delegate
+ $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
+ }
+
+ $parent.trigger(e = $.Event('show.bs.dropdown'))
+
+ if (e.isDefaultPrevented()) return
+
+ $parent
+ .toggleClass('open')
+ .trigger('shown.bs.dropdown')
+
+ $this.focus()
+ }
+
+ return false
+ }
+
+ Dropdown.prototype.keydown = function (e) {
+ if (!/(38|40|27)/.test(e.keyCode)) return
+
+ var $this = $(this)
+
+ e.preventDefault()
+ e.stopPropagation()
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ if (!isActive || (isActive && e.keyCode == 27)) {
+ if (e.which == 27) $parent.find(toggle).focus()
+ return $this.click()
+ }
+
+ var $items = $('[role=menu] li:not(.divider):visible a', $parent)
+
+ if (!$items.length) return
+
+ var index = $items.index($items.filter(':focus'))
+
+ if (e.keyCode == 38 && index > 0) index-- // up
+ if (e.keyCode == 40 && index < $items.length - 1) index++ // down
+ if (!~index) index=0
+
+ $items.eq(index).focus()
+ }
+
+ function clearMenus() {
+ $(backdrop).remove()
+ $(toggle).each(function (e) {
+ var $parent = getParent($(this))
+ if (!$parent.hasClass('open')) return
+ $parent.trigger(e = $.Event('hide.bs.dropdown'))
+ if (e.isDefaultPrevented()) return
+ $parent.removeClass('open').trigger('hidden.bs.dropdown')
+ })
+ }
+
+ function getParent($this) {
+ var selector = $this.attr('data-target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ var $parent = selector && $(selector)
+
+ return $parent && $parent.length ? $parent : $this.parent()
+ }
+
+
+ // DROPDOWN PLUGIN DEFINITION
+ // ==========================
+
+ var old = $.fn.dropdown
+
+ $.fn.dropdown = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('dropdown')
+
+ if (!data) $this.data('dropdown', (data = new Dropdown(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ $.fn.dropdown.Constructor = Dropdown
+
+
+ // DROPDOWN NO CONFLICT
+ // ====================
+
+ $.fn.dropdown.noConflict = function () {
+ $.fn.dropdown = old
+ return this
+ }
+
+
+ // APPLY TO STANDARD DROPDOWN ELEMENTS
+ // ===================================
+
+ $(document)
+ .on('click.bs.dropdown.data-api', clearMenus)
+ .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+ .on('click.bs.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
+ .on('keydown.bs.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
+
+}(window.jQuery);
\ No newline at end of file
https://bitbucket.org/galaxy/galaxy-central/commits/44399512777b/
Changeset: 44399512777b
Branch: data_library
User: martenson
Date: 2013-12-03 22:44:22
Summary: library work, galaxy modal changes, api v2 preparation, API tweaks, API for dataset downloading implementation
Affected #: 9 files
diff -r 4e53d7b759d1999758a30611e5d897e3e1d20782 -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -1768,7 +1768,7 @@
class Library( object, Dictifiable ):
permitted_actions = get_permitted_actions( filter='LIBRARY' )
dict_collection_visible_keys = ( 'id', 'name' )
- dict_element_visible_keys = ( 'id', 'deleted', 'name', 'description', 'synopsis' )
+ dict_element_visible_keys = ( 'id', 'deleted', 'name', 'description', 'synopsis', 'root_folder_id' )
def __init__( self, name=None, description=None, synopsis=None, root_folder=None ):
self.name = name or "Unnamed library"
self.description = description
diff -r 4e53d7b759d1999758a30611e5d897e3e1d20782 -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 lib/galaxy/webapps/galaxy/api/datasets.py
--- a/lib/galaxy/webapps/galaxy/api/datasets.py
+++ b/lib/galaxy/webapps/galaxy/api/datasets.py
@@ -1,5 +1,5 @@
"""
-API operations on the contents of a dataset.
+API operations on the contents of a history dataset.
"""
from galaxy import web
from galaxy.visualization.data_providers.genome import FeatureLocationIndexDataProvider
diff -r 4e53d7b759d1999758a30611e5d897e3e1d20782 -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 lib/galaxy/webapps/galaxy/api/folder_contents.py
--- a/lib/galaxy/webapps/galaxy/api/folder_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/folder_contents.py
@@ -42,19 +42,34 @@
GET /api/folders/{encoded_folder_id}/contents
Displays a collection (list) of a folder's contents (files and folders).
Encoded folder ID is prepended with 'F' if it is a folder as opposed to a data set which does not have it.
+ Full path is provided as a separate object in response providing data for breadcrumb path building.
"""
folder_container = []
current_user_roles = trans.get_current_user_roles()
+
+# log.debug( 'XXXXXXXXXXXXXXXXXXXXX GOT id: ' + folder_id )
+# log.debug( 'XXXXXXXXXXXXXXXXXXXXX decode id: ' + folder_id[1:] )
+# log.debug( 'XXXXXXXXXXXXXXXXXXXXX call decode: ' + str(trans.security.decode_id( folder_id[1:] )) )
- try:
- decoded_folder_id = trans.security.decode_id( folder_id[-16:] )
- except TypeError:
- trans.response.status = 400
- return "Malformed folder id ( %s ) specified, unable to decode." % str( folder_id )
+# try:
+# decoded_folder_id = trans.security.decode_id( folder_id[-16:] )
+# except TypeError:
+# trans.response.status = 400
+# return "Malformed folder id ( %s ) specified, unable to decode." % str( folder_id )
+# log.debug( 'XXXXXXXXXXXXXXXXXXXXX decoded id: ' + decoded_folder_id )
+
+ if ( folder_id.startswith( 'F' ) ):
+ try:
+ decoded_folder_id = trans.security.decode_id( folder_id[1:] )
+ except TypeError:
+ trans.response.status = 400
+ return "Malformed folder id ( %s ) specified, unable to decode." % str( folder_id )
try:
folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( decoded_folder_id )
- parent_library = folder.parent_library
+# log.debug( 'XXXXXXXXXXXXXXXXXXXXX FOLDER id: ' + str(folder.id) )
+# log.debug( 'XXXXXXXXXXXXXXXXXXXXX FOLDER name: ' + str(folder.name) )
+# parent_library = folder.parent_library
except:
folder = None
log.error( "FolderContentsController.index: Unable to retrieve folder with ID: %s" % folder_id )
@@ -77,13 +92,17 @@
path_to_root = []
# We are almost in root
if folder.parent_id is None:
- log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ALMOST ROOT FOLDER! ADDING: " + str( folder.name ) )
+
+# log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ALMOST ROOT FOLDER! ADDING ID: " + str( folder.id ) )
+# log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ALMOST ROOT FOLDER! ADDING NAME: " + str( folder.name ) )
path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
# upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_library.id )
# path_to_root.append( ( upper_folder.id, upper_folder.name ) )
else:
# We add the current folder and traverse up one folder.
- log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ADDING THIS FOLDER AND TRAVERSING UP: " + str( folder.name ) )
+# log.debug( "XXXXXXXXXXXXXXXXXXXXXXX folder.parent_id " + str( folder.parent_id ) )
+# log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ALMOST ROOT FOLDER! ADDING ID: " + str( folder.id ) )
+# log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ADDING THIS FOLDER AND TRAVERSING UP: " + str( folder.name ) )
path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_id )
path_to_root.extend( build_path( upper_folder ) )
diff -r 4e53d7b759d1999758a30611e5d897e3e1d20782 -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 lib/galaxy/webapps/galaxy/api/lda_datasets.py
--- a/lib/galaxy/webapps/galaxy/api/lda_datasets.py
+++ b/lib/galaxy/webapps/galaxy/api/lda_datasets.py
@@ -1,27 +1,69 @@
"""
-API operations on the contents of a dataset from library.
+API operations on the dataset from library.
"""
-from galaxy import web
+import glob
+import logging
+import operator
+import os
+import os.path
+import string
+import sys
+import tarfile
+import tempfile
+import urllib
+import urllib2
+import zipfile
+from galaxy.security import Action
+from galaxy import util, web
+from galaxy.util.streamball import StreamBall
from galaxy.web.base.controller import BaseAPIController, UsesLibraryMixinItems
import logging
log = logging.getLogger( __name__ )
+# Test for available compression types
+# tmpd = tempfile.mkdtemp()
+# comptypes = []
+# for comptype in ( 'gz', 'bz2' ):
+# tmpf = os.path.join( tmpd, 'compression_test.tar.' + comptype )
+# try:
+# archive = tarfile.open( tmpf, 'w:' + comptype )
+# archive.close()
+# comptypes.append( comptype )
+# except tarfile.CompressionError:
+# log.exception( "Compression error when testing %s compression. This option will be disabled for library downloads." % comptype )
+# try:
+# os.unlink( tmpf )
+# except OSError:
+# pass
+ziptype = '32'
+# tmpf = os.path.join( tmpd, 'compression_test.zip' )
+# try:
+# archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True )
+# archive.close()
+# comptypes.append( 'zip' )
+# ziptype = '64'
+# except RuntimeError:
+# log.exception( "Compression error when testing zip compression. This option will be disabled for library downloads." )
+# except (TypeError, zipfile.LargeZipFile):
+# # ZIP64 is only in Python2.5+. Remove TypeError when 2.4 support is dropped
+# log.warning( 'Max zip file size is 2GB, ZIP64 not supported' )
+# comptypes.append( 'zip' )
+# try:
+# os.unlink( tmpf )
+# except OSError:
+# pass
+# os.rmdir( tmpd )
+
+
+
class DatasetsController( BaseAPIController, UsesLibraryMixinItems ):
@web.expose_api
- def index( self, trans, **kwd ):
- """
- GET /api/libraries/datasets
- """
- trans.response.status = 501
- return 'not implemented'
-
- @web.expose_api
def show( self, trans, id, **kwd ):
"""
GET /api/libraries/datasets/{encoded_dataset_id}
- Displays information about and/or content of a dataset identified by the lda ID.
+ Displays information about the dataset identified by the lda ID.
"""
# Get dataset.
try:
@@ -35,4 +77,165 @@
rval = "Error in dataset API at listing contents: " + str( e )
log.error( rval + ": %s" % str(e), exc_info=True )
trans.response.status = 500
- return rval
\ No newline at end of file
+
+ rval['id'] = trans.security.encode_id(rval['id']);
+ rval['ldda_id'] = trans.security.encode_id(rval['ldda_id']);
+ rval['folder_id'] = 'f' + trans.security.encode_id(rval['folder_id'])
+
+ return rval
+
+ @web.expose
+ def download( self, trans, format, **kwd ):
+ """
+ POST /api/libraries/datasets/download/{format}
+ POST data: ldda_ids = []
+ Downloads dataset(s) in the requested format.
+ """
+ lddas = []
+# is_admin = trans.user_is_admin()
+# current_user_roles = trans.get_current_user_roles()
+
+ datasets_to_download = kwd['ldda_ids%5B%5D']
+
+ if ( datasets_to_download != None ):
+ datasets_to_download = util.listify( datasets_to_download )
+ for dataset_id in datasets_to_download:
+ try:
+ ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( dataset_id ) )
+ assert not ldda.dataset.purged
+ lddas.append( ldda )
+ except:
+ ldda = None
+ message += "Invalid library dataset id (%s) specified. " % str( dataset_id )
+
+ if format in [ 'zip','tgz','tbz' ]:
+ error = False
+ killme = string.punctuation + string.whitespace
+ trantab = string.maketrans(killme,'_'*len(killme))
+ try:
+ outext = 'zip'
+ if format == 'zip':
+ # Can't use mkstemp - the file must not exist first
+ tmpd = tempfile.mkdtemp()
+ util.umask_fix_perms( tmpd, trans.app.config.umask, 0777, self.app.config.gid )
+ tmpf = os.path.join( tmpd, 'library_download.' + format )
+ if ziptype == '64' and trans.app.config.upstream_gzip:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED, True )
+ elif ziptype == '64':
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True )
+ elif trans.app.config.upstream_gzip:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED )
+ else:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED )
+ archive.add = lambda x, y: archive.write( x, y.encode('CP437') )
+ elif format == 'tgz':
+ if trans.app.config.upstream_gzip:
+ archive = StreamBall( 'w|' )
+ outext = 'tar'
+ else:
+ archive = StreamBall( 'w|gz' )
+ outext = 'tgz'
+ elif format == 'tbz':
+ archive = StreamBall( 'w|bz2' )
+ outext = 'tbz2'
+ except ( OSError, zipfile.BadZipfile ):
+ error = True
+ log.exception( "Unable to create archive for download" )
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ except:
+ error = True
+ log.exception( "Unexpected error %s in create archive for download" % sys.exc_info()[0] )
+ message = "Unable to create archive for download, please report - %s" % sys.exc_info()[0]
+ status = 'error'
+ if not error:
+ composite_extensions = trans.app.datatypes_registry.get_composite_extensions()
+ seen = []
+ for ldda in lddas:
+ if ldda.dataset.state in [ 'new', 'upload', 'queued', 'running', 'empty', 'discarded' ]:
+ continue
+ ext = ldda.extension
+ is_composite = ext in composite_extensions
+ path = ""
+ parent_folder = ldda.library_dataset.folder
+ while parent_folder is not None:
+ # Exclude the now-hidden "root folder"
+ if parent_folder.parent is None:
+ path = os.path.join( parent_folder.library_root[0].name, path )
+ break
+ path = os.path.join( parent_folder.name, path )
+ parent_folder = parent_folder.parent
+ path += ldda.name
+ while path in seen:
+ path += '_'
+ seen.append( path )
+ zpath = os.path.split(path)[-1] # comes as base_name/fname
+ outfname,zpathext = os.path.splitext(zpath)
+ if is_composite:
+ # need to add all the components from the extra_files_path to the zip
+ if zpathext == '':
+ zpath = '%s.html' % zpath # fake the real nature of the html file
+ try:
+ archive.add(ldda.dataset.file_name,zpath) # add the primary of a composite set
+ except IOError:
+ error = True
+ log.exception( "Unable to add composite parent %s to temporary library download archive" % ldda.dataset.file_name)
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ continue
+ flist = glob.glob(os.path.join(ldda.dataset.extra_files_path,'*.*')) # glob returns full paths
+ for fpath in flist:
+ efp,fname = os.path.split(fpath)
+ if fname > '':
+ fname = fname.translate(trantab)
+ try:
+ archive.add( fpath,fname )
+ except IOError:
+ error = True
+ log.exception( "Unable to add %s to temporary library download archive %s" % (fname,outfname))
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ continue
+ else: # simple case
+ try:
+ archive.add( ldda.dataset.file_name, path )
+ except IOError:
+ error = True
+ log.exception( "Unable to write %s to temporary library download archive" % ldda.dataset.file_name)
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ if not error:
+ lname = 'selected_dataset'
+ fname = lname.replace( ' ', '_' ) + '_files'
+ if format == 'zip':
+ archive.close()
+ trans.response.set_content_type( "application/octet-stream" )
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
+ archive = util.streamball.ZipBall(tmpf, tmpd)
+ archive.wsgi_status = trans.response.wsgi_status()
+ archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ return archive.stream
+ else:
+ trans.response.set_content_type( "application/x-tar" )
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
+ archive.wsgi_status = trans.response.wsgi_status()
+ archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ return archive.stream
+ elif format == 'uncompressed':
+ if len(lddas) != 1:
+ return 'Wrong request'
+ else:
+ single_dataset = lddas[0]
+ trans.response.set_content_type( single_dataset.get_mime() )
+ fStat = os.stat( ldda.file_name )
+ trans.response.headers[ 'Content-Length' ] = int( fStat.st_size )
+ valid_chars = '.,^_-()[]0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ fname = ldda.name
+ fname = ''.join( c in valid_chars and c or '_' for c in fname )[ 0:150 ]
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s"' % fname
+ try:
+ return open( single_dataset.file_name )
+ except:
+ return 'This dataset contains no content'
+ else:
+ return 'Wrong format';
\ No newline at end of file
diff -r 4e53d7b759d1999758a30611e5d897e3e1d20782 -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 lib/galaxy/webapps/galaxy/api/libraries.py
--- a/lib/galaxy/webapps/galaxy/api/libraries.py
+++ b/lib/galaxy/webapps/galaxy/api/libraries.py
@@ -52,6 +52,7 @@
item = library.to_dict( view='element' )
item['url'] = url_for( route, id=trans.security.encode_id( library.id ) )
item['id'] = 'F' + trans.security.encode_id( item['id'] )
+ item['root_folder_id'] = 'F' + trans.security.encode_id( item['root_folder_id'] )
rval.append( item )
return rval
diff -r 4e53d7b759d1999758a30611e5d897e3e1d20782 -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -146,6 +146,7 @@
# =======================
webapp.mapper.connect( 'show_lda_item', '/api/libraries/datasets/:id', controller='lda_datasets', action='show', conditions=dict(method=["GET"]) )
+ webapp.mapper.connect( 'download_lda_items', '/api/libraries/datasets/download/:format', controller='lda_datasets', action='download', conditions=dict(method=["POST"]) )
webapp.mapper.resource_with_deleted( 'library', 'libraries', path_prefix='/api' )
webapp.mapper.resource( 'folder', 'folders', path_prefix='/api' )
diff -r 4e53d7b759d1999758a30611e5d897e3e1d20782 -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 static/scripts/galaxy.library.js
--- a/static/scripts/galaxy.library.js
+++ b/static/scripts/galaxy.library.js
@@ -5,6 +5,9 @@
// global variables
var view = null;
var library_router = null;
+var responses = [];
+var KEYCODE_ENTER = 13;
+var KEYCODE_ESC = 27;
// dependencies
define(["galaxy.modal", "galaxy.master"], function(mod_modal, mod_master) {
@@ -70,52 +73,99 @@
var LibraryRouter = Backbone.Router.extend({
routes: {
"" : "libraries",
- "folders/:id" : "folder_content"
+ "folders/:id" : "folder_content",
+ "folders/:folder_id/download/:format" : "download"
}
});
// MMMMMMMMMMMMMM
-// === Views ====
+// === VIEWS ====
// MMMMMMMMMMMMMM
// galaxy folder
var FolderContentView = Backbone.View.extend({
- //main element
+ // main element definition
el : '#center',
+ // progress percentage
+ progress: 0,
+ // progress rate per one item
+ progressStep: 1,
+ // last selected history in modal for UX
+ lastSelectedHistory: '',
+ // self modal
+ modal : null,
+ // loaded folders
+ folders : null,
+
// initialize
initialize : function(){
this.folders = [];
+ this.queue = jQuery.Deferred();
+ this.queue.resolve();
},
+
+// MMMMMMMMMMMMMMMMMM
+// === TEMPLATES ====
+// MMMMMMMMMMMMMMMMMM
+
// set up
templateFolder : function (){
var tmpl_array = [];
- tmpl_array.push('<a href="#">Libraries</a> | ');
+ // CONTAINER
+ tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; ">');
+ tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to Martin.</h3>');
+
+ // TOOLBAR
+ tmpl_array.push('<div id="library_folder_toolbar" >');
+ tmpl_array.push(' <button id="toolbtn_create_folder" class="btn btn-primary" type="button">new folder</button>');
+ tmpl_array.push(' <button id="toolbtn_bulk_import" style="display: none; margin-left: 0.5em;" type="button">Import into History</button>');
+
+ tmpl_array.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; display: none; ">');
+ tmpl_array.push(' <button id="drop_toggle" type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">');
+ tmpl_array.push(' Download <span class="caret"></span>');
+ tmpl_array.push(' </button>');
+ tmpl_array.push(' <ul class="dropdown-menu" role="menu">');
+ // tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/uncompressed">uncompressed</a></li>');
+ // tmpl_array.push(' <li class="divider"></li>');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');
+ tmpl_array.push(' </ul>');
+ tmpl_array.push(' </div>');
+
+ tmpl_array.push('</div>');
+
+ // BREADCRUMBS
+ tmpl_array.push('<a href="#">Libraries</a><b>|</b> ');
tmpl_array.push('<% _.each(path, function(path_item) { %>'); //breadcrumb
- tmpl_array.push('<% if (path_item[0] != id) { %>');
- tmpl_array.push('<a href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a> |');
- tmpl_array.push('<% } else { %>');
- tmpl_array.push('<%- path_item[1] %>');
- tmpl_array.push('<% } %>');
- tmpl_array.push('<% }); %>');
- tmpl_array.push('<table class="table table-hover table-condensed">');
- tmpl_array.push(' <thead>');
- tmpl_array.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');
- tmpl_array.push(' <th>name</th>');
- tmpl_array.push(' <th>type</th>');
- tmpl_array.push(' </thead>');
- tmpl_array.push(' <tbody>');
- tmpl_array.push(' <% _.each(items, function(content_item) { %>');
- tmpl_array.push(' <tr class="folder_row" id="<%- content_item.id %>">');
- tmpl_array.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');
+ tmpl_array.push('<% if (path_item[0] != id) { %>');
+ tmpl_array.push('<a href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a><b>|</b> ');
+ tmpl_array.push('<% } else { %>');
+ tmpl_array.push('<%- path_item[1] %>');
+ tmpl_array.push('<% } %>');
+ tmpl_array.push('<% }); %>');
+
+ // FODLER CONTENT
+ tmpl_array.push('<table id="folder_table" class="table table-hover table-condensed">');
+ tmpl_array.push(' <thead>');
+ tmpl_array.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');
+ tmpl_array.push(' <th>name</th>');
+ tmpl_array.push(' <th>type</th>');
+ tmpl_array.push(' </thead>');
+ tmpl_array.push(' <tbody>');
+ tmpl_array.push(' <% _.each(items, function(content_item) { %>');
+ tmpl_array.push(' <tr class="folder_row" id="<%- content_item.id %>">');
tmpl_array.push(' <% if (content_item.get("type") === "folder") { %>'); //folder
- tmpl_array.push(' <td><a href="#/folders/<%- content_item.id %>"><%- content_item.get("name") %></a>');
+ tmpl_array.push(' <td style="text-align: center; "></td>');
+ tmpl_array.push(' <td><a href="#/folders/<%- content_item.id %>"><%- content_item.get("name") %></a>');
tmpl_array.push(' <% if (content_item.get("item_count") === 0) { %>'); //empty folder
tmpl_array.push(' <span class="muted">(empty folder)</span>');
tmpl_array.push(' <% } %>');
tmpl_array.push(' </td>');
tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');
tmpl_array.push(' <td><a class="library-dataset" href="#"><%- content_item.get("name") %></a></td>'); //dataset
tmpl_array.push(' <% } %> ');
tmpl_array.push(' <td><%= _.escape(content_item.get("type")) %></td>');
@@ -123,10 +173,10 @@
tmpl_array.push(' <% }); %>');
tmpl_array.push(' ');
tmpl_array.push(' ');
- tmpl_array.push(' ');
tmpl_array.push(' </tbody>');
tmpl_array.push('</table>');
+ tmpl_array.push('</div>');
return tmpl_array.join('');
},
templateDatasetModal : function(){
@@ -135,7 +185,7 @@
tmpl_array.push('<div id="dataset_info_modal">');
tmpl_array.push(' <table class="table table-striped table-condensed">');
tmpl_array.push(' <tr>');
- tmpl_array.push(' <th scope="row">Name</th>');
+ tmpl_array.push(' <th scope="row" id="id_row" data-id="<%= _.escape(item.get("ldda_id")) %>">Name</th>');
tmpl_array.push(' <td><%= _.escape(item.get("name")) %></td>');
tmpl_array.push(' </tr>');
tmpl_array.push(' <tr>');
@@ -157,9 +207,6 @@
tmpl_array.push(' <th scope="row">Uploaded by</th>');
tmpl_array.push(' <td><%= _.escape(item.get("uploaded_by")) %></td>');
tmpl_array.push(' </tr>');
- // tmpl_array.push(' </table>');
- // tmpl_array.push(' <hr/>');
- // tmpl_array.push(' <table class="table table-striped">');
tmpl_array.push(' <tr scope="row">');
tmpl_array.push(' <th scope="row">Data Lines</th>');
tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_data_lines")) %></td>');
@@ -183,31 +230,45 @@
tmpl_array.push(' <th scope="row">Miscellaneous information</th>');
tmpl_array.push(' <td scope="row"><%= _.escape(item.get("misc_blurb")) %></td>');
tmpl_array.push(' </tr>');
- tmpl_array.push(' <tr>');
- tmpl_array.push(' ');
tmpl_array.push(' </table>');
- // tmpl_array.push(' <hr/>');
tmpl_array.push(' <pre class="peek">');
tmpl_array.push(' </pre>');
tmpl_array.push('</div>');
return tmpl_array.join('');
},
+
templateHistorySelectInModal : function(){
var tmpl_array = [];
- tmpl_array.push('<span id="history_modal_footer" style="width:60%;">');
- tmpl_array.push('<select name="history_import" style="width:60%; margin-left: 2em; "> ');
+ tmpl_array.push('<span id="history_modal_combo" style="width:90%; margin-left: 1em; margin-right: 1em; ">');
+ tmpl_array.push('Select history: ');
+ tmpl_array.push('<select id="dataset_import_single" name="dataset_import_single" style="width:50%; margin-bottom: 1em; "> ');
tmpl_array.push(' <% _.each(histories, function(history) { %>'); //history select box
- tmpl_array.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');
- tmpl_array.push(' <% }); %>');
- tmpl_array.push('</span>');
- tmpl_array.push('</div>');
- tmpl_array.push('');
+ tmpl_array.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push('</select>');
+ tmpl_array.push('</span>');
- return tmpl_array.join('');
- },
- // to string
+ return tmpl_array.join('');
+ },
+
+ templateBulkImportInModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');
+ tmpl_array.push('Select history: ');
+ tmpl_array.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');
+ tmpl_array.push(' <% _.each(histories, function(history) { %>'); //history select box
+ tmpl_array.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push('</select>');
+ tmpl_array.push('</span>');
+
+ return tmpl_array.join('');
+ },
+
+ // convert size to nice string
size_to_string : function (size)
{
// identify unit
@@ -220,17 +281,26 @@
// return formatted string
return (Math.round(size) / 10) + unit;
},
- events: {
+
+// MMMMMMMMMMMMMMM
+// === EVENTS ====
+// MMMMMMMMMMMMMMM
+
+ // event binding
+ events: {
'click #select-all-checkboxes' : 'selectAll',
'click .folder_row' : 'selectClicked',
+ 'click #toolbtn_bulk_import' : 'modalBulkImport',
+ 'click #toolbtn_dl' : 'bulkDownload',
'click .library-dataset' : 'showDatasetDetails'
},
- //self modal
- modal : null,
- //loaded folders
- folders : null,
- //render the view
+
+ //render the folder view
render: function (options) {
+ //hack to show scrollbars
+ $("#center").css('overflow','auto');
+
+ view = this;
var that = this;
var folderContainer = new FolderContainer({id: options.id});
@@ -241,15 +311,16 @@
// folderContainer.attributes.folder = container.attributes.folder;
var template = _.template(that.templateFolder(), {path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id});
that.$el.html(template);
- }
- })
- },
- //show modal with dataset info
+ }
+ })
+ },
+
+ //show modal with current dataset info
showDatasetDetails : function(e){
// prevent default
e.preventDefault();
- //TODO check whether we already have the data
+//TODO check whether we already have the data
//load the ID of the row
var id = $(e.target).parent().parent().attr('id');
@@ -259,53 +330,90 @@
var histories = new GalaxyHistories();
item.id = id;
var self = this;
+
//fetch the dataset info
item.fetch({
success: function (item) {
- //fetch user histories for import
+// TODO can render here already
+ //fetch user histories for import purposes
histories.fetch({
- success: function (histories){self.modalFetchSuccess(item, histories)}
+ success: function (histories){self.renderModalAfterFetch(item, histories)}
});
}
});
},
- modalFetchSuccess : function(item, histories){
- var histories_modal = this.templateHistorySelectInModal();
+ // show the current dataset in a modal
+ renderModalAfterFetch : function(item, histories){
var size = this.size_to_string(item.get('file_size'));
var template = _.template(this.templateDatasetModal(), { item : item, size : size });
+ this.modal = null;
// make modal
var self = this;
this.modal = new mod_modal.GalaxyModal({
title : 'Dataset Details',
body : template,
buttons : {
- 'Import' : function() {self.importIntoHistory()},
- // 'Notify' : function() {self.modal.showNotification("TEST")},
- 'Close' : function() {self.modal.hide()}
+ 'Import' : function() { self.importCurrentIntoHistory() },
+ 'Download' : function() { self.downloadCurrent() },
+ 'Close' : function() { self.modal.hide(); $('.modal').html(''); self.modal = null; } // TODO refill nicely modal with data
}
});
$(".peek").html(item.get("peek"));
- // this.modal.hideButton('Import');
- // $(this.modal.elMain).find('.modal-footer').prepend("<div>BUBUBUBU" + "</div>");
var history_footer_tmpl = _.template(this.templateHistorySelectInModal(), {histories : histories.models});
+ $(this.modal.elMain).find('.buttons').prepend(history_footer_tmpl);
- $(this.modal.elMain).find('.buttons').prepend(history_footer_tmpl);
+ // preset last selected history if we know it
+ if (self.lastSelectedHistory.length > 0) {
+ $(this.modal.elMain).find('#dataset_import_single').val(self.lastSelectedHistory);
+ }
// show the prepared modal
this.modal.show();
},
- importIntoHistory : function(){
- var history_id = 'a0c15f4d91084599';
- var library_dataset_id = '03501d7626bd192f';
+ // download dataset shown currently in modal
+ downloadCurrent : function(){
+ //disable the buttons
+ this.modal.disableButton('Import');
+ this.modal.disableButton('Download');
+ var library_dataset_id = [];
+ library_dataset_id.push($('#id_row').attr('data-id'));
+ var url = '/api/libraries/datasets/download/uncompressed';
+ var data = {'ldda_ids' : library_dataset_id};
+
+ // we assume the view is existent
+ folderContentView.processDownload(url, data);
+ this.modal.enableButton('Import');
+ this.modal.enableButton('Download');
+ },
+
+ // import dataset shown currently in modal into selected history
+ importCurrentIntoHistory : function(){
+ //disable the buttons
+ this.modal.disableButton('Import');
+ this.modal.disableButton('Download');
+
+ var history_id = $(this.modal.elMain).find('select[name=dataset_import_single] option:selected').val();
+ this.lastSelectedHistory = history_id; //save selected history for further use
+
+ var library_dataset_id = $('#id_row').attr('data-id');
var historyItem = new HistoryItem();
var self = this;
historyItem.url = historyItem.urlRoot + history_id + '/contents';
- console.log(historyItem);
+
+ // save the dataset into selected history
historyItem.save({ content : library_dataset_id, source : 'library' }, { success : function(){
self.modal.showNotification('Dataset imported', 3000, '#e1f4e0', '#32a427');
+ //enable the buttons
+ self.modal.enableButton('Import');
+ self.modal.enableButton('Download');
+ }, error : function(){
+ self.modal.showNotification('An error occured! Dataset not imported. Please try again later.', 5000, '#f4e0e1', '#a42732');
+ //enable the buttons
+ self.modal.enableButton('Import');
+ self.modal.enableButton('Download');
}
});
},
@@ -314,9 +422,11 @@
var selected = ev.target.checked;
// Iterate each checkbox
$(':checkbox').each(function () { this.checked = selected; });
+ this.showTools();
},
- selectClicked : function (ev) {
- var checkbox = $("#" + ev.target.parentElement.id).find(':checkbox')
+
+ selectClicked : function (ev) {
+ var checkbox = $("#" + ev.target.parentElement.id).find(':checkbox')
if (checkbox[0] != undefined) {
if (checkbox[0].checked){
checkbox[0].checked = '';
@@ -324,11 +434,161 @@
checkbox[0].checked = 'selected';
}
}
- }
+ this.showTools();
+ },
+
+ // show toolbar in case something is selected
+ showTools : function(){
+ var checkedValues = $('#folder_table').find(':checked');
+ if(checkedValues.length > 0){
+ $('#toolbtn_bulk_import').show();
+ $('#toolbtn_dl').show();
+ } else {
+ $('#toolbtn_bulk_import').hide();
+ $('#toolbtn_dl').hide();
+ }
+
+ },
+
+ // show bulk import modal
+ modalBulkImport : function(){
+ var self = this;
+ // fetch histories
+ var histories = new GalaxyHistories();
+ histories.fetch({
+ success: function (histories){
+ // make modal
+ var history_modal_tmpl = _.template(self.templateBulkImportInModal(), {histories : histories.models});
+ self.modal = new mod_modal.GalaxyModal({
+ title : 'Import into History',
+ body : history_modal_tmpl,
+ buttons : {
+ 'Import' : function() {self.importAllIntoHistory()},
+ 'Close' : function() {self.modal.hide()}
+ }
+ });
+ // show the prepared modal
+ self.modal.show();
+ }
+ });
+ },
+
+ // import all selected datasets into history
+ importAllIntoHistory : function (){
+ //disable the button
+ this.modal.disableButton('Import');
+
+ var history_id = $("select[name=dataset_import_bulk] option:selected").val();
+ var history_name = $("select[name=dataset_import_bulk] option:selected").text();
+
+ var dataset_ids = [];
+ $('#folder_table').find(':checked').each(function(){
+ if (this.parentElement.parentElement.id != '') {
+ dataset_ids.push(this.parentElement.parentElement.id);
+ }
+ });
+ var progress_bar_tmpl = _.template(this.templateProgressBar(), { history_name : history_name });
+ $(this.modal.elMain).find('.modal-body').html(progress_bar_tmpl);
+
+ // init the progress bar
+ var progressStep = 100 / dataset_ids.length;
+ this.initProgress(progressStep);
+
+ // prepare the dataset objects to be imported
+ var datasets_to_import = [];
+ for (var i = dataset_ids.length - 1; i >= 0; i--) {
+ library_dataset_id = dataset_ids[i];
+ var historyItem = new HistoryItem();
+ var self = this;
+ historyItem.url = historyItem.urlRoot + history_id + '/contents';
+ historyItem.content = library_dataset_id;
+ historyItem.source = 'library';
+ datasets_to_import.push(historyItem);
+ };
+
+ // call the recursive function to call ajax one after each other
+ this.chainCall(datasets_to_import);
+ },
+
+ chainCall: function(history_item_set){
+ var self = this;
+ var popped_item = history_item_set.pop();
+ if (typeof popped_item === "undefined") {
+ self.modal.showNotification('All datasets imported', 3000, '#e1f4e0', '#32a427');
+ // enable button again
+ self.modal.enableButton('Import');
+ return
+ }
+ var promise = $.when(popped_item.save({content: popped_item.content, source: popped_item.source})).done(function(a1){
+ self.updateProgress();
+ responses.push(a1);
+ self.chainCall(history_item_set);
+ });
+ },
+
+ initProgress: function(progressStep){
+ this.progress = 0;
+ this.progressStep = progressStep;
+ },
+ updateProgress: function(){
+ this.progress += this.progressStep;
+ $('.progress-bar').width(Math.round(this.progress) + '%');
+ txt_representation = Math.round(this.progress) + '% Complete';
+ $('.completion_span').text(txt_representation);
+ },
+
+ // progress bar
+ templateProgressBar : function (){
+ var tmpl_array = [];
+
+ tmpl_array.push('<div class="import_text">');
+ tmpl_array.push('Importing selected datasets to history <b><%= _.escape(history_name) %></b>');
+ tmpl_array.push('</div>');
+ tmpl_array.push('<div class="progress">');
+ tmpl_array.push(' <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');
+ tmpl_array.push(' <span class="completion_span">0% Complete</span>');
+ tmpl_array.push(' </div>');
+ tmpl_array.push('</div>');
+ tmpl_array.push('');
+
+ return tmpl_array.join('');
+ },
+
+ // download selected datasets
+ download : function(folder_id, format){
+ var dataset_ids = [];
+ $('#folder_table').find(':checked').each(function(){
+ if (this.parentElement.parentElement.id != '') {
+ dataset_ids.push(this.parentElement.parentElement.id);
+ }
+ });
+
+ var url = '/api/libraries/datasets/download/' + format;
+ var data = {'ldda_ids' : dataset_ids};
+ this.processDownload(url, data);
+ },
+
+ // create hidden form and submit through POST to initialize download
+ processDownload: function(url, data, method){
+ //url and data options required
+ if( url && data ){
+ //data can be string of parameters or array/object
+ data = typeof data == 'string' ? data : $.param(data);
+ //split params into form inputs
+ var inputs = '';
+ $.each(data.split('&'), function(){
+ var pair = this.split('=');
+ inputs+='<input type="hidden" name="'+ pair[0] +'" value="'+ pair[1] +'" />';
+ });
+ //send request
+ $('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
+ .appendTo('body').submit().remove();
+ };
+ }
+
});
-
-// galaxy library
+// galaxy library view
var GalaxyLibraryview = Backbone.View.extend({
el: '#center',
@@ -343,9 +603,10 @@
// template
template_library_list : function (){
tmpl_array = [];
+ tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; overflow: auto !important; ">');
tmpl_array.push('');
- tmpl_array.push('<h1>Welcome to the data libraries</h1>');
+ tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to Martin.</h3>');
tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary icon-file ">New Library</a>');
tmpl_array.push('<table class="table table-striped">');
tmpl_array.push(' <thead>');
@@ -353,30 +614,27 @@
tmpl_array.push(' <th>description</th>');
tmpl_array.push(' <th>synopsis</th> ');
tmpl_array.push(' <th>model type</th> ');
- // tmpl_array.push(' <th>id</th> ');
tmpl_array.push(' </thead>');
tmpl_array.push(' <tbody>');
tmpl_array.push(' <% _.each(libraries, function(library) { %>');
- tmpl_array.push(' <tr>');
- tmpl_array.push(' <td><a href="#/folders/<%- library.id %>"><%- library.get("name") %></a></td>');
- tmpl_array.push(' <td><%= _.escape(library.get("description")) %></td>');
- tmpl_array.push(' <td><%= _.escape(library.get("synopsis")) %></td>');
- tmpl_array.push(' <td><%= _.escape(library.get("model_class")) %></td>');
- // tmpl_array.push(' <td><a href="#/folders/<%- library.id %>"><%= _.escape(library.get("id")) %></a></td>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <td><a href="#/folders/<%- library.get("root_folder_id") %>"><%- library.get("name") %></a></td>');
+ tmpl_array.push(' <td><%= _.escape(library.get("description")) %></td>');
+ tmpl_array.push(' <td><%= _.escape(library.get("synopsis")) %></td>');
+ tmpl_array.push(' <td><%= _.escape(library.get("model_class")) %></td>');
tmpl_array.push(' </tr>');
tmpl_array.push(' <% }); %>');
- tmpl_array.push('');
- tmpl_array.push('');
- tmpl_array.push('');
- tmpl_array.push('');
tmpl_array.push(' </tbody>');
tmpl_array.push('</table>');
-
+
+ tmpl_array.push('</div>');
return tmpl_array.join('');
},
// render
render: function () {
+ //hack to show scrollbars
+ $("#center").css('overflow','auto');
var that = this;
// if (typeof libraries === "undefined") {
libraries = new Libraries();
@@ -394,12 +652,11 @@
// show/hide create library modal
show_library_modal : function (e){
- // prevent default
+ // prevent default, may be unnecessary
e.preventDefault();
// create modal
if (!this.modal){
- // make modal
var self = this;
this.modal = new mod_modal.GalaxyModal(
{
@@ -412,9 +669,11 @@
});
}
- // show modal
+ // show prepared modal
this.modal.show();
},
+
+ // create the new library from modal
create_new_library_event: function(){
var libraryDetails = this.serialize_new_library();
var library = new Library();
@@ -428,11 +687,15 @@
});
return false;
},
+
+ // clear the library modal once saved
clear_library_modal : function(){
$("input[name='Name']").val('');
$("input[name='Description']").val('');
$("input[name='Synopsis']").val('');
},
+
+ // serialize data from the form
serialize_new_library : function(){
return {
name: $("input[name='Name']").val(),
@@ -440,10 +703,12 @@
synopsis: $("input[name='Synopsis']").val()
};
},
- // load html template
+
+ // template for new library modal
template_new_library: function()
{
tmpl_array = [];
+
tmpl_array.push('<div id="new_library_modal">');
tmpl_array.push('<form>');
tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');
@@ -452,10 +717,8 @@
tmpl_array.push('');
tmpl_array.push('</form>');
tmpl_array.push('</div>');
+
return tmpl_array.join('');
-
-
- // return '<div id="'+ id +'"></div>';
}
});
@@ -473,19 +736,28 @@
library_router.on('route:libraries', function() {
// render libraries list
galaxyLibraryview.render();
- })
+ });
library_router.on('route:folder_content', function(id) {
// render folder's contents
folderContentView.render({id: id});
+ });
- })
+ library_router.on('route:download', function(folder_id, format) {
+ // send download stream
+ if (typeof folderContentView === 'undefined'){
+ alert('you cant touch this!');
+ // } else if (folderContentView.modal !== null){
+ // folderContentView.download(folder_id, format);
+ } else if ($('#center').find(':checked').length === 0) { // coming from outside of the library app
+ library_router.navigate('folders/' + folder_id, {trigger: true, replace: true});
+ // TODO show message of redirection
+ } else {
+ folderContentView.download(folder_id, format);
+ library_router.navigate('folders/' + folder_id, {trigger: false, replace: true});
+ }
- // library_router.on('route:show_library_modal', function() {
- // // render folder's contents
- // galaxyLibraryview.show_library_modal();
-
- // })
+ });
Backbone.history.start();
diff -r 4e53d7b759d1999758a30611e5d897e3e1d20782 -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -20,9 +20,28 @@
// initialize
initialize : function(options) {
+ self = this;
// create
if (options)
this.create(options);
+ // bind the ESC key to hide() function
+ $(document).on('keyup', function(event){
+ if (event.keyCode == 27) { self.hide(); }
+ })
+ // bind the click anywhere to hide() function...
+ $('html').on('click', function(event){
+ self.hide();
+ })
+ // ...but don't hide if the click is on modal content
+ $('.modal-content').on('click', function(event){
+ event.stopPropagation();
+ })
+ },
+
+ // destroy
+ destroy : function(){
+ this.hide();
+ $('.modal').html('');
},
// adds and displays a new frame/window
https://bitbucket.org/galaxy/galaxy-central/commits/f466834f693f/
Changeset: f466834f693f
Branch: data_library
User: martenson
Date: 2013-12-10 19:01:19
Summary: improved styles for libraries, added features to modal window, API tweaks
Affected #: 5 files
diff -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 -r f466834f693ff7ae0f60cf121d97f67a5338ea95 lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -1988,15 +1988,6 @@
table = self.table
trans = conn.begin()
try:
-
-# log.debug("XXXXXXXXXXXXXXX NEW GENERATOOOOOR")
-# next_hid = table.update() \
-# .returning( table.c.hid_counter ) \
-# .where( table.c.id == self.id ) \
-# .values( hid_counter = hid_counter + 1 )
-# log.debug('XXXXXXXXXXXXXXXXXXXXXXXXXXX NEXT HID: ' + str(next_hid))
-
-
current_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
next_hid = current_hid + 1
table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid ) )
diff -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 -r f466834f693ff7ae0f60cf121d97f67a5338ea95 lib/galaxy/webapps/galaxy/api/folder_contents.py
--- a/lib/galaxy/webapps/galaxy/api/folder_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/folder_contents.py
@@ -46,17 +46,6 @@
"""
folder_container = []
current_user_roles = trans.get_current_user_roles()
-
-# log.debug( 'XXXXXXXXXXXXXXXXXXXXX GOT id: ' + folder_id )
-# log.debug( 'XXXXXXXXXXXXXXXXXXXXX decode id: ' + folder_id[1:] )
-# log.debug( 'XXXXXXXXXXXXXXXXXXXXX call decode: ' + str(trans.security.decode_id( folder_id[1:] )) )
-
-# try:
-# decoded_folder_id = trans.security.decode_id( folder_id[-16:] )
-# except TypeError:
-# trans.response.status = 400
-# return "Malformed folder id ( %s ) specified, unable to decode." % str( folder_id )
-# log.debug( 'XXXXXXXXXXXXXXXXXXXXX decoded id: ' + decoded_folder_id )
if ( folder_id.startswith( 'F' ) ):
try:
@@ -67,9 +56,6 @@
try:
folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( decoded_folder_id )
-# log.debug( 'XXXXXXXXXXXXXXXXXXXXX FOLDER id: ' + str(folder.id) )
-# log.debug( 'XXXXXXXXXXXXXXXXXXXXX FOLDER name: ' + str(folder.name) )
-# parent_library = folder.parent_library
except:
folder = None
log.error( "FolderContentsController.index: Unable to retrieve folder with ID: %s" % folder_id )
@@ -92,17 +78,9 @@
path_to_root = []
# We are almost in root
if folder.parent_id is None:
-
-# log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ALMOST ROOT FOLDER! ADDING ID: " + str( folder.id ) )
-# log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ALMOST ROOT FOLDER! ADDING NAME: " + str( folder.name ) )
path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
-# upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_library.id )
-# path_to_root.append( ( upper_folder.id, upper_folder.name ) )
else:
# We add the current folder and traverse up one folder.
-# log.debug( "XXXXXXXXXXXXXXXXXXXXXXX folder.parent_id " + str( folder.parent_id ) )
-# log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ALMOST ROOT FOLDER! ADDING ID: " + str( folder.id ) )
-# log.debug( "XXXXXXXXXXXXXXXXXXXXXXX ADDING THIS FOLDER AND TRAVERSING UP: " + str( folder.name ) )
path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_id )
path_to_root.extend( build_path( upper_folder ) )
@@ -113,25 +91,40 @@
folder_container.append( dict( full_path = full_path ) )
folder_contents = []
+ time_updated = ''
+ time_created = ''
# Go through every item in the folder and include its meta-data.
for content_item in self.load_folder_contents( trans, folder ):
+# rval = content_item.to_dict()
return_item = {}
encoded_id = trans.security.encode_id( content_item.id )
+ time_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
+ time_created = content_item.create_time.strftime( "%Y-%m-%d %I:%M %p" )
+# log.debug('XXXXXXXXXXXXXXXXXXX api type: ' + str(content_item.api_type))
+# log.debug('XXXXXXXX ALL: ' + str(content_item.__dict__))
# For folder return also hierarchy values
if content_item.api_type == 'folder':
-# encoded_parent_library_id = trans.security.encode_id( content_item.parent_library.id )
encoded_id = 'F' + encoded_id
-# if content_item.parent_id is not None: # Return folder's parent id for browsing back.
-# encoded_parent_id = 'F' + trans.security.encode_id( content_item.parent_id )
- last_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
- return_item.update ( dict ( item_count = content_item.item_count, last_updated = last_updated ) )
-
+# time_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
+ return_item.update ( dict ( item_count = content_item.item_count ) )
+
+ if content_item.api_type == 'file':
+# log.debug('XXXXX content item class: ' + str(content_item.__class__))
+ library_dataset_dict = content_item.to_dict()
+ library_dataset_dict['data_type']
+ library_dataset_dict['file_size']
+ library_dataset_dict['date_uploaded']
+ return_item.update ( dict ( data_type = library_dataset_dict['data_type'],
+ file_size = library_dataset_dict['file_size'],
+ date_uploaded = library_dataset_dict['date_uploaded'] ) )
+
# For every item return also the default meta-data
return_item.update( dict( id = encoded_id,
type = content_item.api_type,
- name = content_item.name
-
+ name = content_item.name,
+ time_updated = time_updated,
+ time_created = time_created
) )
folder_contents.append( return_item )
# Put the data in the container
diff -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 -r f466834f693ff7ae0f60cf121d97f67a5338ea95 static/scripts/galaxy.library.js
--- a/static/scripts/galaxy.library.js
+++ b/static/scripts/galaxy.library.js
@@ -6,8 +6,16 @@
var view = null;
var library_router = null;
var responses = [];
-var KEYCODE_ENTER = 13;
-var KEYCODE_ESC = 27;
+
+
+// load required libraries
+require([
+ // load js libraries
+ 'utils/galaxy.css',
+ ], function(css){
+ // load css
+ css.load_file("static/style/library.css");
+});
// dependencies
define(["galaxy.modal", "galaxy.master"], function(mod_modal, mod_master) {
@@ -115,20 +123,18 @@
// CONTAINER
tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; ">');
- tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to Martin.</h3>');
+ tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');
// TOOLBAR
tmpl_array.push('<div id="library_folder_toolbar" >');
- tmpl_array.push(' <button id="toolbtn_create_folder" class="btn btn-primary" type="button">new folder</button>');
- tmpl_array.push(' <button id="toolbtn_bulk_import" style="display: none; margin-left: 0.5em;" type="button">Import into History</button>');
+ tmpl_array.push(' <button title="Create New Folder" id="toolbtn_create_folder" class="btn btn-primary" type="button"><span class="fa fa-icon-plus"></span><span class="fa fa-icon-folder-close"></span> folder</button>');
+ tmpl_array.push(' <button id="toolbtn_bulk_import" class="btn btn-primary" style="display: none; margin-left: 0.5em;" type="button"><span class="fa fa-icon-external-link"></span> to history</button>');
tmpl_array.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; display: none; ">');
- tmpl_array.push(' <button id="drop_toggle" type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">');
- tmpl_array.push(' Download <span class="caret"></span>');
+ tmpl_array.push(' <button id="drop_toggle" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">');
+ tmpl_array.push(' <span class="fa fa-icon-download"></span> download <span class="caret"></span>');
tmpl_array.push(' </button>');
tmpl_array.push(' <ul class="dropdown-menu" role="menu">');
- // tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/uncompressed">uncompressed</a></li>');
- // tmpl_array.push(' <li class="divider"></li>');
tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');
tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');
tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');
@@ -138,41 +144,64 @@
tmpl_array.push('</div>');
// BREADCRUMBS
- tmpl_array.push('<a href="#">Libraries</a><b>|</b> ');
- tmpl_array.push('<% _.each(path, function(path_item) { %>'); //breadcrumb
+ tmpl_array.push('<div class="library_breadcrumb">');
+ tmpl_array.push('<a title="Return to the list of libraries" href="#">Libraries</a><b>|</b> ');
+ tmpl_array.push('<% _.each(path, function(path_item) { %>');
tmpl_array.push('<% if (path_item[0] != id) { %>');
- tmpl_array.push('<a href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a><b>|</b> ');
+ tmpl_array.push('<a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a><b>|</b> ');
tmpl_array.push('<% } else { %>');
- tmpl_array.push('<%- path_item[1] %>');
+ tmpl_array.push('<span title="You are in this folder"><%- path_item[1] %></span>');
tmpl_array.push('<% } %>');
tmpl_array.push('<% }); %>');
+ tmpl_array.push('</div>');
- // FODLER CONTENT
- tmpl_array.push('<table id="folder_table" class="table table-hover table-condensed">');
+ // FOLDER CONTENT
+ tmpl_array.push('<table id="folder_table" class="table table-condensed">');
tmpl_array.push(' <thead>');
tmpl_array.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');
+ tmpl_array.push(' <th class="button_heading">view</th>');
tmpl_array.push(' <th>name</th>');
- tmpl_array.push(' <th>type</th>');
+ tmpl_array.push(' <th>data type</th>');
+ tmpl_array.push(' <th>size</th>');
+ tmpl_array.push(' <th>date</th>');
tmpl_array.push(' </thead>');
tmpl_array.push(' <tbody>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td><button title="Go to parent folder" type="button" data-id="<%- upper_folder_id %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-arrow-up"></span> .. go up</td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' </tr>');
tmpl_array.push(' <% _.each(items, function(content_item) { %>');
- tmpl_array.push(' <tr class="folder_row" id="<%- content_item.id %>">');
- tmpl_array.push(' <% if (content_item.get("type") === "folder") { %>'); //folder
- tmpl_array.push(' <td style="text-align: center; "></td>');
- tmpl_array.push(' <td><a href="#/folders/<%- content_item.id %>"><%- content_item.get("name") %></a>');
- tmpl_array.push(' <% if (content_item.get("item_count") === 0) { %>'); //empty folder
+ tmpl_array.push(' <tr class="folder_row light" id="<%- content_item.id %>">');
+ tmpl_array.push(' <% if (content_item.get("type") === "folder") { %>'); // folder
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td><button title="Open this folder" type="button" data-id="<%- content_item.id %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-folder-open"></span> browse</td>');
+ tmpl_array.push(' <td><%- content_item.get("name") %>');
+ tmpl_array.push(' <% if (content_item.get("item_count") === 0) { %>'); // empty folder
tmpl_array.push(' <span class="muted">(empty folder)</span>');
tmpl_array.push(' <% } %>');
tmpl_array.push(' </td>');
+ tmpl_array.push(' <td>folder</td>'); // data type
+ tmpl_array.push(' <td><%= _.escape(content_item.get("item_count")) %> item(s)</td>'); // size
tmpl_array.push(' <% } else { %>');
tmpl_array.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');
- tmpl_array.push(' <td><a class="library-dataset" href="#"><%- content_item.get("name") %></a></td>'); //dataset
+ tmpl_array.push(' <td>');
+ tmpl_array.push(' <button title="See details of this dataset" type="button" class="library-dataset btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-eye-open"></span> details');
+ tmpl_array.push(' </button>');
+ tmpl_array.push(' </td>');
+ tmpl_array.push(' <td><%- content_item.get("name") %></td>'); // dataset
+ tmpl_array.push(' <td><%= _.escape(content_item.get("data_type")) %></td>'); // data type
+ tmpl_array.push(' <td><%= _.escape(content_item.get("readable_size")) %></td>'); // size
tmpl_array.push(' <% } %> ');
- tmpl_array.push(' <td><%= _.escape(content_item.get("type")) %></td>');
+ tmpl_array.push(' <td><%= _.escape(content_item.get("time_updated")) %></td>'); // time updated
tmpl_array.push(' </tr>');
tmpl_array.push(' <% }); %>');
tmpl_array.push(' ');
- tmpl_array.push(' ');
tmpl_array.push(' </tbody>');
tmpl_array.push('</table>');
@@ -292,7 +321,9 @@
'click .folder_row' : 'selectClicked',
'click #toolbtn_bulk_import' : 'modalBulkImport',
'click #toolbtn_dl' : 'bulkDownload',
- 'click .library-dataset' : 'showDatasetDetails'
+ 'click .library-dataset' : 'showDatasetDetails',
+ 'click #toolbtn_create_folder' : 'createFolderModal',
+ 'click .btn_open_folder' : 'navigateToFolder'
},
//render the folder view
@@ -308,22 +339,52 @@
folderContainer.fetch({
success: function (container) {
- // folderContainer.attributes.folder = container.attributes.folder;
- var template = _.template(that.templateFolder(), {path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id});
+
+ // prepare nice size strings
+ for (var i = 0; i < folderContainer.attributes.folder.models.length; i++) {
+ var model = folderContainer.attributes.folder.models[i]
+ if (model.get('type') === 'file'){
+ model.set('readable_size', that.size_to_string(model.get('file_size')))
+ }
+ };
+
+ // find the upper id
+ var path = folderContainer.full_path;
+ var upper_folder_id;
+ if (path.length === 1){ // library is above us
+ upper_folder_id = 0;
+ } else {
+ upper_folder_id = path[path.length-2][0];
+ }
+
+ var template = _.template(that.templateFolder(), { path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id, upper_folder_id: upper_folder_id });
+ // var template = _.template(that.templateFolder(), { path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id });
that.$el.html(template);
}
})
},
+ // handles the click on 'open' and 'upper' folder icons
+ navigateToFolder : function(event){
+ var folder_id = $(event.target).attr('data-id');
+ if (typeof folder_id === 'undefined') {
+ return false;
+ } else if (folder_id === '0'){
+ library_router.navigate('#', {trigger: true, replace: true});
+ } else {
+ library_router.navigate('folders/' + folder_id, {trigger: true, replace: true});
+ }
+ },
+
//show modal with current dataset info
- showDatasetDetails : function(e){
+ showDatasetDetails : function(event){
// prevent default
- e.preventDefault();
+ event.preventDefault();
//TODO check whether we already have the data
//load the ID of the row
- var id = $(e.target).parent().parent().attr('id');
+ var id = $(event.target).parent().parent().attr('id');
//create new item
var item = new Item();
@@ -356,7 +417,7 @@
buttons : {
'Import' : function() { self.importCurrentIntoHistory() },
'Download' : function() { self.downloadCurrent() },
- 'Close' : function() { self.modal.hide(); $('.modal').html(''); self.modal = null; } // TODO refill nicely modal with data
+ 'Close' : function() { self.modal.hide(); $('.modal').remove(); self.modal = null; } // TODO refill nicely modal with data
}
});
$(".peek").html(item.get("peek"));
@@ -405,12 +466,12 @@
// save the dataset into selected history
historyItem.save({ content : library_dataset_id, source : 'library' }, { success : function(){
- self.modal.showNotification('Dataset imported', 3000, '#e1f4e0', '#32a427');
+ self.modal.showNotification('Dataset imported', 3000, 'success');
//enable the buttons
self.modal.enableButton('Import');
self.modal.enableButton('Download');
}, error : function(){
- self.modal.showNotification('An error occured! Dataset not imported. Please try again later.', 5000, '#f4e0e1', '#a42732');
+ self.modal.showNotification('An error occured! Dataset not imported. Please try again later.', 5000, 'error');
//enable the buttons
self.modal.enableButton('Import');
self.modal.enableButton('Download');
@@ -418,20 +479,32 @@
});
},
- selectAll : function (ev) {
- var selected = ev.target.checked;
+ // select all datasets
+ selectAll : function (event) {
+ var selected = event.target.checked;
// Iterate each checkbox
$(':checkbox').each(function () { this.checked = selected; });
this.showTools();
},
- selectClicked : function (ev) {
- var checkbox = $("#" + ev.target.parentElement.id).find(':checkbox')
+ // click checkbox on folder click
+ selectClicked : function (event) {
+ var checkbox = $("#" + event.target.parentElement.id).find(':checkbox')
if (checkbox[0] != undefined) {
if (checkbox[0].checked){
checkbox[0].checked = '';
+ // $(event.target.parentElement).css('background-color', '').css('color', '');
+ $(event.target.parentElement).removeClass('dark');
+ $(event.target.parentElement).find('a').removeClass('dark');
+ $(event.target.parentElement).addClass('light');
+ $(event.target.parentElement).find('a').addClass('light');
} else {
checkbox[0].checked = 'selected';
+ $(event.target.parentElement).removeClass('light');
+ $(event.target.parentElement).find('a').removeClass('light');
+ $(event.target.parentElement).addClass('dark');
+ $(event.target.parentElement).find('a').addClass('dark');
+ // $(event.target.parentElement).css('background-color', '#8389a1').css('color', 'white');
}
}
this.showTools();
@@ -464,7 +537,7 @@
body : history_modal_tmpl,
buttons : {
'Import' : function() {self.importAllIntoHistory()},
- 'Close' : function() {self.modal.hide()}
+ 'Close' : function() {self.modal.hide(); $('.modal').remove(); self.modal = null;}
}
});
// show the prepared modal
@@ -514,7 +587,7 @@
var self = this;
var popped_item = history_item_set.pop();
if (typeof popped_item === "undefined") {
- self.modal.showNotification('All datasets imported', 3000, '#e1f4e0', '#32a427');
+ self.modal.showNotification('All datasets imported', 3000, 'success');
// enable button again
self.modal.enableButton('Import');
return
@@ -584,6 +657,11 @@
$('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
.appendTo('body').submit().remove();
};
+ },
+
+ // shows modal for creating folder
+ createFolderModal: function(){
+ alert('creating folder');
}
});
@@ -606,11 +684,12 @@
tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; overflow: auto !important; ">');
tmpl_array.push('');
- tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to Martin.</h3>');
+ tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');
tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary icon-file ">New Library</a>');
- tmpl_array.push('<table class="table table-striped">');
+ tmpl_array.push('<table class="table table-condensed">');
tmpl_array.push(' <thead>');
- tmpl_array.push(' <th>name</th>');
+ tmpl_array.push(' <th class="button_heading"></th>');
+ tmpl_array.push(' <th>name</th>');
tmpl_array.push(' <th>description</th>');
tmpl_array.push(' <th>synopsis</th> ');
tmpl_array.push(' <th>model type</th> ');
@@ -618,7 +697,9 @@
tmpl_array.push(' <tbody>');
tmpl_array.push(' <% _.each(libraries, function(library) { %>');
tmpl_array.push(' <tr>');
- tmpl_array.push(' <td><a href="#/folders/<%- library.get("root_folder_id") %>"><%- library.get("name") %></a></td>');
+ tmpl_array.push(' <td><button title="Open this library" type="button" data-id="<%- library.get("root_folder_id") %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-folder-open"></span> browse</td>');
+ tmpl_array.push(' <td><%- library.get("name") %></td>');
tmpl_array.push(' <td><%= _.escape(library.get("description")) %></td>');
tmpl_array.push(' <td><%= _.escape(library.get("synopsis")) %></td>');
tmpl_array.push(' <td><%= _.escape(library.get("model_class")) %></td>');
@@ -651,12 +732,11 @@
modal : null,
// show/hide create library modal
- show_library_modal : function (e){
- // prevent default, may be unnecessary
- e.preventDefault();
+ show_library_modal : function (event){
+ event.preventDefault();
+ event.stopPropagation();
// create modal
- if (!this.modal){
var self = this;
this.modal = new mod_modal.GalaxyModal(
{
@@ -667,7 +747,6 @@
'Close' : function() {self.modal.hide()}
}
});
- }
// show prepared modal
this.modal.show();
@@ -676,6 +755,7 @@
// create the new library from modal
create_new_library_event: function(){
var libraryDetails = this.serialize_new_library();
+ var valid = this.validate_new_library(libraryDetails);
var library = new Library();
var self = this;
library.save(libraryDetails, {
@@ -683,7 +763,10 @@
self.modal.hide();
self.clear_library_modal();
self.render();
- }
+ },
+ error: function(){
+ self.modal.showNotification('An error occured', 5000, 'error');
+ }
});
return false;
},
@@ -704,6 +787,11 @@
};
},
+ validate_new_library: function(library){
+
+ },
+
+
// template for new library modal
template_new_library: function()
{
@@ -714,7 +802,6 @@
tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');
tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');
tmpl_array.push('<input type="text" name="Synopsis" value="" placeholder="Synopsis">');
- tmpl_array.push('');
tmpl_array.push('</form>');
tmpl_array.push('</div>');
diff -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 -r f466834f693ff7ae0f60cf121d97f67a5338ea95 static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -1,6 +1,5 @@
-/*
- galaxy modal
-*/
+// galaxy modal
+
// dependencies
define(["libs/backbone/backbone-relational"], function() {
@@ -24,24 +23,48 @@
// create
if (options)
this.create(options);
- // bind the ESC key to hide() function
- $(document).on('keyup', function(event){
- if (event.keyCode == 27) { self.hide(); }
- })
- // bind the click anywhere to hide() function...
- $('html').on('click', function(event){
- self.hide();
- })
- // ...but don't hide if the click is on modal content
- $('.modal-content').on('click', function(event){
- event.stopPropagation();
- })
+
+ this.bindClick(event, self);
},
+ // bind the click-to-hide function
+ bindClick: function(event, that) {
+ // bind the ESC key to hide() function
+ $(document).on('keyup', function(event){
+ if (event.keyCode == 27) { self.hide(); }
+ })
+ // bind the click anywhere to hide() function...
+ $('html').on('click', function(event){
+ that.hide();
+ })
+ // ...but don't hide if the click is on modal content
+ $('.modal-content').on('click', function(event){
+ event.stopPropagation();
+ })
+ },
+
+ // unbind the click-to-hide function
+ unbindClick: function(event, that){
+ // bind the ESC key to hide() function
+ $(document).off('keyup', function(event){
+ if (event.keyCode == 27) { that.hide(); }
+ })
+ // unbind the click anywhere to hide() function...
+ $('html').off('click', function(event){
+ that.hide();
+ })
+ // ...but don't hide if the click is on modal content
+ $('.modal-content').off('click', function(event){
+ event.stopPropagation();
+ })
+ },
+
+
// destroy
destroy : function(){
this.hide();
- $('.modal').html('');
+ this.unbindClick();
+ $('.modal').remove();
},
// adds and displays a new frame/window
@@ -152,16 +175,28 @@
},
// show notification
- showNotification : function(message, duration, bgColor, txtColor) {
+ showNotification : function(message, duration, type) {
// defaults
var duration = typeof duration !== 'undefined' ? duration : 1500;
- var bgColor = typeof bgColor !== 'undefined' ? bgColor : "#F4E0E1";
- var txtColor = typeof txtColor !== 'undefined' ? txtColor : "#A42732";
+ // var bgColor = typeof bgColor !== 'undefined' ? bgColor : "#F4E0E1";
+ // var txtColor = typeof txtColor !== 'undefined' ? txtColor : "#A42732";
+ var bgColor;
+ var txtColor;
+
+ if (type === 'error'){
+ bgColor = '#f4e0e1';
+ txtColor = '#a42732';
+ // } else if (type === 'success'){
+ } else { // success is default
+ bgColor = '#e1f4e0';
+ txtColor = '#32a427';
+ }
var HTMLmessage = "<div class='notification-message' style='text-align:center; line-height:16px; '> " + message + " </div>";
this.$notification.html("<div id='notification-bar' style='display:none; float: right; height: 16px; width:100%; background-color: " + bgColor + "; z-index: 100; color: " + txtColor + ";border-bottom: 1px solid " + txtColor + ";'>" + HTMLmessage + "</div>");
var self = this;
+
/*animate the bar*/
$('#notification-bar').slideDown(function() {
setTimeout(function() {
diff -r 44399512777bdefb39ea3c99cc04b3f7947c5d33 -r f466834f693ff7ae0f60cf121d97f67a5338ea95 static/style/src/less/library.less
--- a/static/style/src/less/library.less
+++ b/static/style/src/less/library.less
@@ -29,6 +29,50 @@
background-color: @table-bg-accent;
}
+
+tr.light td
+{
+ background-color: white;
+ color: black;
+}
+tr.light:hover td
+{
+ background-color: #f5f5f5;
+ color: #8389a1;
+}
+tr.dark td
+{
+ background-color: #8389a1;
+ color: white;
+}
+tr.dark:hover td
+{
+ background-color: #bbbfd0;
+ color: white;
+}
+a.dark:hover
+{
+ color: yellow;
+ // text-decoration: none;
+}
+a.dark
+{
+ color: white;
+ // text-decoration: none;
+}
+th.button_heading
+{
+ width: 7em;
+}
+div.library_breadcrumb{
+ padding-top: 0.8em;
+ padding-bottom: 0.8em;
+}
+div.library_breadcrumb a:hover{
+ color:green;
+}
+
+
img.expanderIcon {
padding-right: 4px;
}
https://bitbucket.org/galaxy/galaxy-central/commits/e2b8d3481d66/
Changeset: e2b8d3481d66
User: martenson
Date: 2013-12-11 00:02:36
Summary: Merge data libraries branch to default
Affected #: 19 files
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 .hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -116,3 +116,6 @@
*.rej
*~
+
+syntax: regexp
+^static/AAA_scratch$
\ No newline at end of file
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -1827,7 +1827,7 @@
class Library( object, Dictifiable ):
permitted_actions = get_permitted_actions( filter='LIBRARY' )
dict_collection_visible_keys = ( 'id', 'name' )
- dict_element_visible_keys = ( 'id', 'deleted', 'name', 'description', 'synopsis' )
+ dict_element_visible_keys = ( 'id', 'deleted', 'name', 'description', 'synopsis', 'root_folder_id' )
def __init__( self, name=None, description=None, synopsis=None, root_folder=None ):
self.name = name or "Unnamed library"
self.description = description
@@ -1894,7 +1894,7 @@
return name
class LibraryFolder( object, Dictifiable ):
- dict_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build' )
+ dict_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build', 'update_time' )
def __init__( self, name=None, description=None, item_count=0, order_id=None ):
self.name = name or "Unnamed folder"
self.description = description
@@ -2060,6 +2060,7 @@
genome_build = ldda.dbkey,
misc_info = ldda.info,
misc_blurb = ldda.blurb,
+ peek = ( lambda ldda: ldda.display_peek() if ldda.peek and ldda.peek != 'no peek' else None )( ldda ),
template_data = template_data )
if ldda.dataset.uuid is None:
rval['uuid'] = None
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -1878,8 +1878,9 @@
table = self.table
trans = conn.begin()
try:
- next_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
- table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid + 1 ) )
+ current_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
+ next_hid = current_hid + 1
+ table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid ) )
trans.commit()
return next_hid
except:
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 lib/galaxy/webapps/galaxy/api/datasets.py
--- a/lib/galaxy/webapps/galaxy/api/datasets.py
+++ b/lib/galaxy/webapps/galaxy/api/datasets.py
@@ -1,5 +1,5 @@
"""
-API operations on the contents of a dataset.
+API operations on the contents of a history dataset.
"""
from galaxy import web
from galaxy.visualization.data_providers.genome import FeatureLocationIndexDataProvider
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 lib/galaxy/webapps/galaxy/api/folder_contents.py
--- a/lib/galaxy/webapps/galaxy/api/folder_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/folder_contents.py
@@ -1,5 +1,5 @@
"""
-API operations on the contents of a library.
+API operations on the contents of a folder.
"""
import logging, os, string, shutil, urllib, re, socket
from cgi import escape, FieldStorage
@@ -11,67 +11,125 @@
log = logging.getLogger( __name__ )
class FolderContentsController( BaseAPIController, UsesLibraryMixin, UsesLibraryMixinItems ):
+ """
+ Class controls retrieval, creation and updating of folder contents.
+ """
+
+ def load_folder_contents( self, trans, folder ):
+ """
+ Loads all contents of the folder (folders and data sets) but only in the first level.
+ """
+ current_user_roles = trans.get_current_user_roles()
+ is_admin = trans.user_is_admin()
+ content_items = []
+ for subfolder in folder.active_folders:
+ if not is_admin:
+ can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder )
+ if (is_admin or can_access) and not subfolder.deleted:
+ subfolder.api_type = 'folder'
+ content_items.append( subfolder )
+ for dataset in folder.datasets:
+ if not is_admin:
+ can_access = trans.app.security_agent.can_access_dataset( current_user_roles, dataset.library_dataset_dataset_association.dataset )
+ if (is_admin or can_access) and not dataset.deleted:
+ dataset.api_type = 'file'
+ content_items.append( dataset )
+ return content_items
@web.expose_api
def index( self, trans, folder_id, **kwd ):
"""
GET /api/folders/{encoded_folder_id}/contents
Displays a collection (list) of a folder's contents (files and folders).
- The /api/library_contents/{encoded_library_id}/contents
- lists everything in a library recursively, which is not what
- we want here. We could add a parameter to use the recursive
- style, but this is meant to act similar to an "ls" directory listing.
+ Encoded folder ID is prepended with 'F' if it is a folder as opposed to a data set which does not have it.
+ Full path is provided as a separate object in response providing data for breadcrumb path building.
"""
- rval = []
+ folder_container = []
current_user_roles = trans.get_current_user_roles()
- def traverse( folder ):
- admin = trans.user_is_admin()
- rval = []
- for subfolder in folder.active_folders:
- if not admin:
- can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder )
- if (admin or can_access) and not subfolder.deleted:
- subfolder.api_type = 'folder'
- rval.append( subfolder )
- for ld in folder.datasets:
- if not admin:
- can_access = trans.app.security_agent.can_access_dataset( current_user_roles, ld.library_dataset_dataset_association.dataset )
- if (admin or can_access) and not ld.deleted:
- ld.api_type = 'file'
- rval.append( ld )
- return rval
-
- try:
- decoded_folder_id = trans.security.decode_id( folder_id[-16:] )
- except TypeError:
- trans.response.status = 400
- return "Malformed folder id ( %s ) specified, unable to decode." % str( folder_id )
+ if ( folder_id.startswith( 'F' ) ):
+ try:
+ decoded_folder_id = trans.security.decode_id( folder_id[1:] )
+ except TypeError:
+ trans.response.status = 400
+ return "Malformed folder id ( %s ) specified, unable to decode." % str( folder_id )
try:
folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( decoded_folder_id )
- parent_library = folder.parent_library
except:
folder = None
- log.error( "FolderContentsController.index: Unable to retrieve folder %s"
- % folder_id )
+ log.error( "FolderContentsController.index: Unable to retrieve folder with ID: %s" % folder_id )
- # TODO: Find the API's path to this folder if necessary.
- # This was needed in recursive descent, but it's not needed
- # for "ls"-style content checking:
- if not folder or not ( trans.user_is_admin() or trans.app.security_agent.can_access_library_item( current_user_roles, folder, trans.user ) ):
+ # We didn't find the folder or user does not have an access to it.
+ if not folder:
trans.response.status = 400
return "Invalid folder id ( %s ) specified." % str( folder_id )
+
+ if not ( trans.user_is_admin() or trans.app.security_agent.can_access_library_item( current_user_roles, folder, trans.user ) ):
+ log.warning( "SECURITY: User (id: %s) without proper access rights is trying to load folder with ID of %s" % ( trans.user.id, folder.id ) )
+ trans.response.status = 400
+ return "Invalid folder id ( %s ) specified." % str( folder_id )
+
+ path_to_root = []
+ def build_path ( folder ):
+ """
+ Search the path upwards recursively and load the whole route of names and ids for breadcrumb purposes.
+ """
+ path_to_root = []
+ # We are almost in root
+ if folder.parent_id is None:
+ path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
+ else:
+ # We add the current folder and traverse up one folder.
+ path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
+ upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_id )
+ path_to_root.extend( build_path( upper_folder ) )
+ return path_to_root
+
+ # Return the reversed path so it starts with the library node.
+ full_path = build_path( folder )[::-1]
+ folder_container.append( dict( full_path = full_path ) )
+
+ folder_contents = []
+ time_updated = ''
+ time_created = ''
+ # Go through every item in the folder and include its meta-data.
+ for content_item in self.load_folder_contents( trans, folder ):
+# rval = content_item.to_dict()
+ return_item = {}
+ encoded_id = trans.security.encode_id( content_item.id )
+ time_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
+ time_created = content_item.create_time.strftime( "%Y-%m-%d %I:%M %p" )
+# log.debug('XXXXXXXXXXXXXXXXXXX api type: ' + str(content_item.api_type))
+# log.debug('XXXXXXXX ALL: ' + str(content_item.__dict__))
+
+ # For folder return also hierarchy values
+ if content_item.api_type == 'folder':
+ encoded_id = 'F' + encoded_id
+# time_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
+ return_item.update ( dict ( item_count = content_item.item_count ) )
- for content in traverse( folder ):
- encoded_id = trans.security.encode_id( content.id )
- if content.api_type == 'folder':
- encoded_id = 'F' + encoded_id
- rval.append( dict( id = encoded_id,
- type = content.api_type,
- name = content.name,
- url = url_for( 'folder_contents', folder_id=encoded_id ) ) )
- return rval
+ if content_item.api_type == 'file':
+# log.debug('XXXXX content item class: ' + str(content_item.__class__))
+ library_dataset_dict = content_item.to_dict()
+ library_dataset_dict['data_type']
+ library_dataset_dict['file_size']
+ library_dataset_dict['date_uploaded']
+ return_item.update ( dict ( data_type = library_dataset_dict['data_type'],
+ file_size = library_dataset_dict['file_size'],
+ date_uploaded = library_dataset_dict['date_uploaded'] ) )
+
+ # For every item return also the default meta-data
+ return_item.update( dict( id = encoded_id,
+ type = content_item.api_type,
+ name = content_item.name,
+ time_updated = time_updated,
+ time_created = time_created
+ ) )
+ folder_contents.append( return_item )
+ # Put the data in the container
+ folder_container.append( dict( folder_contents = folder_contents ) )
+ return folder_container
@web.expose_api
def show( self, trans, id, library_id, **kwd ):
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 lib/galaxy/webapps/galaxy/api/lda_datasets.py
--- /dev/null
+++ b/lib/galaxy/webapps/galaxy/api/lda_datasets.py
@@ -0,0 +1,241 @@
+"""
+API operations on the dataset from library.
+"""
+import glob
+import logging
+import operator
+import os
+import os.path
+import string
+import sys
+import tarfile
+import tempfile
+import urllib
+import urllib2
+import zipfile
+from galaxy.security import Action
+from galaxy import util, web
+from galaxy.util.streamball import StreamBall
+from galaxy.web.base.controller import BaseAPIController, UsesLibraryMixinItems
+
+import logging
+log = logging.getLogger( __name__ )
+
+# Test for available compression types
+# tmpd = tempfile.mkdtemp()
+# comptypes = []
+# for comptype in ( 'gz', 'bz2' ):
+# tmpf = os.path.join( tmpd, 'compression_test.tar.' + comptype )
+# try:
+# archive = tarfile.open( tmpf, 'w:' + comptype )
+# archive.close()
+# comptypes.append( comptype )
+# except tarfile.CompressionError:
+# log.exception( "Compression error when testing %s compression. This option will be disabled for library downloads." % comptype )
+# try:
+# os.unlink( tmpf )
+# except OSError:
+# pass
+ziptype = '32'
+# tmpf = os.path.join( tmpd, 'compression_test.zip' )
+# try:
+# archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True )
+# archive.close()
+# comptypes.append( 'zip' )
+# ziptype = '64'
+# except RuntimeError:
+# log.exception( "Compression error when testing zip compression. This option will be disabled for library downloads." )
+# except (TypeError, zipfile.LargeZipFile):
+# # ZIP64 is only in Python2.5+. Remove TypeError when 2.4 support is dropped
+# log.warning( 'Max zip file size is 2GB, ZIP64 not supported' )
+# comptypes.append( 'zip' )
+# try:
+# os.unlink( tmpf )
+# except OSError:
+# pass
+# os.rmdir( tmpd )
+
+
+
+class DatasetsController( BaseAPIController, UsesLibraryMixinItems ):
+
+ @web.expose_api
+ def show( self, trans, id, **kwd ):
+ """
+ GET /api/libraries/datasets/{encoded_dataset_id}
+ Displays information about the dataset identified by the lda ID.
+ """
+ # Get dataset.
+ try:
+ dataset = self.get_library_dataset( trans, id = id )
+ except Exception, e:
+ return str( e )
+ try:
+ # Default: return dataset as dict.
+ rval = dataset.to_dict()
+ except Exception, e:
+ rval = "Error in dataset API at listing contents: " + str( e )
+ log.error( rval + ": %s" % str(e), exc_info=True )
+ trans.response.status = 500
+
+ rval['id'] = trans.security.encode_id(rval['id']);
+ rval['ldda_id'] = trans.security.encode_id(rval['ldda_id']);
+ rval['folder_id'] = 'f' + trans.security.encode_id(rval['folder_id'])
+
+ return rval
+
+ @web.expose
+ def download( self, trans, format, **kwd ):
+ """
+ POST /api/libraries/datasets/download/{format}
+ POST data: ldda_ids = []
+ Downloads dataset(s) in the requested format.
+ """
+ lddas = []
+# is_admin = trans.user_is_admin()
+# current_user_roles = trans.get_current_user_roles()
+
+ datasets_to_download = kwd['ldda_ids%5B%5D']
+
+ if ( datasets_to_download != None ):
+ datasets_to_download = util.listify( datasets_to_download )
+ for dataset_id in datasets_to_download:
+ try:
+ ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( dataset_id ) )
+ assert not ldda.dataset.purged
+ lddas.append( ldda )
+ except:
+ ldda = None
+ message += "Invalid library dataset id (%s) specified. " % str( dataset_id )
+
+ if format in [ 'zip','tgz','tbz' ]:
+ error = False
+ killme = string.punctuation + string.whitespace
+ trantab = string.maketrans(killme,'_'*len(killme))
+ try:
+ outext = 'zip'
+ if format == 'zip':
+ # Can't use mkstemp - the file must not exist first
+ tmpd = tempfile.mkdtemp()
+ util.umask_fix_perms( tmpd, trans.app.config.umask, 0777, self.app.config.gid )
+ tmpf = os.path.join( tmpd, 'library_download.' + format )
+ if ziptype == '64' and trans.app.config.upstream_gzip:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED, True )
+ elif ziptype == '64':
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True )
+ elif trans.app.config.upstream_gzip:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED )
+ else:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED )
+ archive.add = lambda x, y: archive.write( x, y.encode('CP437') )
+ elif format == 'tgz':
+ if trans.app.config.upstream_gzip:
+ archive = StreamBall( 'w|' )
+ outext = 'tar'
+ else:
+ archive = StreamBall( 'w|gz' )
+ outext = 'tgz'
+ elif format == 'tbz':
+ archive = StreamBall( 'w|bz2' )
+ outext = 'tbz2'
+ except ( OSError, zipfile.BadZipfile ):
+ error = True
+ log.exception( "Unable to create archive for download" )
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ except:
+ error = True
+ log.exception( "Unexpected error %s in create archive for download" % sys.exc_info()[0] )
+ message = "Unable to create archive for download, please report - %s" % sys.exc_info()[0]
+ status = 'error'
+ if not error:
+ composite_extensions = trans.app.datatypes_registry.get_composite_extensions()
+ seen = []
+ for ldda in lddas:
+ if ldda.dataset.state in [ 'new', 'upload', 'queued', 'running', 'empty', 'discarded' ]:
+ continue
+ ext = ldda.extension
+ is_composite = ext in composite_extensions
+ path = ""
+ parent_folder = ldda.library_dataset.folder
+ while parent_folder is not None:
+ # Exclude the now-hidden "root folder"
+ if parent_folder.parent is None:
+ path = os.path.join( parent_folder.library_root[0].name, path )
+ break
+ path = os.path.join( parent_folder.name, path )
+ parent_folder = parent_folder.parent
+ path += ldda.name
+ while path in seen:
+ path += '_'
+ seen.append( path )
+ zpath = os.path.split(path)[-1] # comes as base_name/fname
+ outfname,zpathext = os.path.splitext(zpath)
+ if is_composite:
+ # need to add all the components from the extra_files_path to the zip
+ if zpathext == '':
+ zpath = '%s.html' % zpath # fake the real nature of the html file
+ try:
+ archive.add(ldda.dataset.file_name,zpath) # add the primary of a composite set
+ except IOError:
+ error = True
+ log.exception( "Unable to add composite parent %s to temporary library download archive" % ldda.dataset.file_name)
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ continue
+ flist = glob.glob(os.path.join(ldda.dataset.extra_files_path,'*.*')) # glob returns full paths
+ for fpath in flist:
+ efp,fname = os.path.split(fpath)
+ if fname > '':
+ fname = fname.translate(trantab)
+ try:
+ archive.add( fpath,fname )
+ except IOError:
+ error = True
+ log.exception( "Unable to add %s to temporary library download archive %s" % (fname,outfname))
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ continue
+ else: # simple case
+ try:
+ archive.add( ldda.dataset.file_name, path )
+ except IOError:
+ error = True
+ log.exception( "Unable to write %s to temporary library download archive" % ldda.dataset.file_name)
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ if not error:
+ lname = 'selected_dataset'
+ fname = lname.replace( ' ', '_' ) + '_files'
+ if format == 'zip':
+ archive.close()
+ trans.response.set_content_type( "application/octet-stream" )
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
+ archive = util.streamball.ZipBall(tmpf, tmpd)
+ archive.wsgi_status = trans.response.wsgi_status()
+ archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ return archive.stream
+ else:
+ trans.response.set_content_type( "application/x-tar" )
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
+ archive.wsgi_status = trans.response.wsgi_status()
+ archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ return archive.stream
+ elif format == 'uncompressed':
+ if len(lddas) != 1:
+ return 'Wrong request'
+ else:
+ single_dataset = lddas[0]
+ trans.response.set_content_type( single_dataset.get_mime() )
+ fStat = os.stat( ldda.file_name )
+ trans.response.headers[ 'Content-Length' ] = int( fStat.st_size )
+ valid_chars = '.,^_-()[]0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ fname = ldda.name
+ fname = ''.join( c in valid_chars and c or '_' for c in fname )[ 0:150 ]
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s"' % fname
+ try:
+ return open( single_dataset.file_name )
+ except:
+ return 'This dataset contains no content'
+ else:
+ return 'Wrong format';
\ No newline at end of file
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 lib/galaxy/webapps/galaxy/api/libraries.py
--- a/lib/galaxy/webapps/galaxy/api/libraries.py
+++ b/lib/galaxy/webapps/galaxy/api/libraries.py
@@ -49,9 +49,10 @@
trans.model.Library.table.c.id.in_( accessible_restricted_library_ids ) ) )
rval = []
for library in query:
- item = library.to_dict()
+ item = library.to_dict( view='element' )
item['url'] = url_for( route, id=trans.security.encode_id( library.id ) )
- item['id'] = trans.security.encode_id( item['id'] )
+ item['id'] = 'F' + trans.security.encode_id( item['id'] )
+ item['root_folder_id'] = 'F' + trans.security.encode_id( item['root_folder_id'] )
rval.append( item )
return rval
@@ -131,6 +132,9 @@
rval['name'] = name
rval['id'] = encoded_id
return rval
+
+ def edit( self, trans, payload, **kwd ):
+ return "Not implemented yet"
@web.expose_api
def delete( self, trans, id, **kwd ):
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -46,6 +46,7 @@
atexit.register( app.shutdown )
# Create the universe WSGI application
webapp = GalaxyWebApplication( app, session_cookie='galaxysession', name='galaxy' )
+ # Add the controllers folder
# Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
controller='admin_toolshed',
@@ -53,11 +54,17 @@
repository_id=None,
image_file=None )
webapp.add_ui_controllers( 'galaxy.webapps.galaxy.controllers', app )
+ # Add the api folder
+ webapp.add_api_controllers( 'galaxy.webapps.galaxy.api', app )
+ # Add the api folder VERSION 2
+ webapp.add_api_controllers( 'galaxy.webapps.galaxy.api.v2', app )
+
# Force /history to go to /root/history -- needed since the tests assume this
webapp.add_route( '/history', controller='root', action='history' )
# Force /activate to go to the controller
webapp.add_route( '/activate', controller='user', action='activate' )
# These two routes handle our simple needs at the moment
+
webapp.add_route( '/async/:tool_id/:data_id/:data_secret', controller='async', action='index', tool_id=None, data_id=None, data_secret=None )
webapp.add_route( '/:controller/:action', action='index' )
webapp.add_route( '/:action', controller='root', action='index' )
@@ -66,8 +73,6 @@
webapp.add_route( '/datasets/:dataset_id/display/{filename:.+?}', controller='dataset', action='display', dataset_id=None, filename=None)
webapp.add_route( '/datasets/:dataset_id/:action/:filename', controller='dataset', action='index', dataset_id=None, filename=None)
webapp.add_route( '/display_application/:dataset_id/:app_name/:link_name/:user_id/:app_action/:action_param',
- controller='dataset', action='display_application', dataset_id=None, user_id=None,
- app_name = None, link_name = None, app_action = None, action_param = None )
webapp.add_route( '/u/:username/d/:slug/:filename', controller='dataset', action='display_by_username_and_slug', filename=None )
webapp.add_route( '/u/:username/p/:slug', controller='page', action='display_by_username_and_slug' )
webapp.add_route( '/u/:username/h/:slug', controller='history', action='display_by_username_and_slug' )
@@ -171,9 +176,44 @@
#webapp.mapper.connect( 'run_workflow', '/api/workflow/{workflow_id}/library/{library_id}', controller='workflows', action='run', workflow_id=None, library_id=None, conditions=dict(method=["GET"]) )
webapp.mapper.resource( 'search', 'search', path_prefix='/api' )
+ webapp.mapper.resource_with_deleted( 'quota', 'quotas', path_prefix='/api' )
+ webapp.mapper.resource_with_deleted( 'history', 'histories', path_prefix='/api' )
+ webapp.mapper.resource_with_deleted( 'user', 'users', path_prefix='/api' )
+
+ # =======================
+ # ===== LIBRARY API =====
+ # =======================
+
+ webapp.mapper.connect( 'show_lda_item', '/api/libraries/datasets/:id', controller='lda_datasets', action='show', conditions=dict(method=["GET"]) )
+ webapp.mapper.connect( 'download_lda_items', '/api/libraries/datasets/download/:format', controller='lda_datasets', action='download', conditions=dict(method=["POST"]) )
+
+ webapp.mapper.resource_with_deleted( 'library', 'libraries', path_prefix='/api' )
+ webapp.mapper.resource( 'folder', 'folders', path_prefix='/api' )
+
+ webapp.mapper.resource( 'content', 'contents',
+ controller='folder_contents',
+ name_prefix='folder_',
+ path_prefix='/api/folders/:folder_id',
+ parent_resources=dict( member_name='folder', collection_name='folders' ) )
+ webapp.mapper.resource( 'content',
+ 'contents',
+ controller='library_contents',
+ name_prefix='library_',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
+ webapp.mapper.resource( 'permission',
+ 'permissions',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
+ _add_item_extended_metadata_controller( webapp,
+ name_prefix="library_dataset_",
+ path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
+
+
# add as a non-ATOM API call to support the notion of a 'current/working' history unique to the history resource
webapp.mapper.connect( "set_as_current", "/api/histories/{id}/set_as_current",
controller="histories", action="set_as_current", conditions=dict( method=["POST"] ) )
+ webapp.mapper.connect( "set_as_current", "/api/histories/{id}/set_as_current", controller="histories", action="set_as_current", conditions=dict( method=["POST"] ) )
webapp.mapper.connect( "create_api_key", "/api/users/:user_id/api_key",
controller="users", action="api_key", user_id=None, conditions=dict( method=["POST"] ) )
@@ -181,6 +221,7 @@
# visualizations registry generic template renderer
webapp.add_route( '/visualization/show/:visualization_name',
controller='visualization', action='render', visualization_name=None )
+ webapp.add_route( '/visualization/show/:visualization_name', controller='visualization', action='render', visualization_name=None )
# "POST /api/workflows/import" => ``workflows.import_workflow()``.
# Defines a named route "import_workflow".
@@ -188,6 +229,15 @@
webapp.mapper.connect("workflow_dict", '/api/workflows/{workflow_id}/download', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
# Preserve the following download route for now for dependent applications -- deprecate at some point
webapp.mapper.connect("workflow_dict", '/api/workflows/download/{workflow_id}', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
+
+
+ # Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
+ webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
+ controller='admin_toolshed',
+ action='display_image_in_repository',
+ repository_id=None,
+ image_file=None )
+
# Galaxy API for tool shed features.
webapp.mapper.resource( 'tool_shed_repository',
'tool_shed_repositories',
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 lib/galaxy/webapps/galaxy/controllers/library.py
--- a/lib/galaxy/webapps/galaxy/controllers/library.py
+++ b/lib/galaxy/webapps/galaxy/controllers/library.py
@@ -76,6 +76,17 @@
library_list_grid = LibraryListGrid()
+
+ @web.expose
+ def list( self, trans, **kwd ):
+ params = util.Params( kwd )
+ # define app configuration for generic mako template
+ app = {
+ 'jscript' : "galaxy.library"
+ }
+ # fill template
+ return trans.fill_template('galaxy.panels.mako', config = {'app' : app})
+
@web.expose
def index( self, trans, **kwd ):
params = util.Params( kwd )
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 static/scripts/galaxy.library.js
--- /dev/null
+++ b/static/scripts/galaxy.library.js
@@ -0,0 +1,860 @@
+// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+// === GALAXY LIBRARY MODULE ====
+// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+
+// global variables
+var view = null;
+var library_router = null;
+var responses = [];
+
+
+// load required libraries
+// require([
+// // load js libraries
+// 'utils/galaxy.css',
+// ], function(css){
+// // load css
+// css.load_file("static/style/library.css");
+// });
+
+// dependencies
+define(["galaxy.modal", "galaxy.masthead", "utils/galaxy.utils"], function(mod_modal, mod_masthead, mod_utils) {
+
+// MMMMMMMMMMMMMMM
+// === Models ====
+// MMMMMMMMMMMMMMM
+
+ // LIBRARY
+ var Library = Backbone.Model.extend({
+ urlRoot: '/api/libraries'
+ });
+
+ // LIBRARIES
+ var Libraries = Backbone.Collection.extend({
+ url: '/api/libraries',
+ model: Library
+ });
+
+ // ITEM
+ var Item = Backbone.Model.extend({
+ urlRoot : '/api/libraries/datasets'
+ })
+
+ // FOLDER
+ var Folder = Backbone.Collection.extend({
+ model: Item
+ })
+
+ // CONTAINER for folder contents (folders, items and metadata).
+ var FolderContainer = Backbone.Model.extend({
+ defaults : {
+ folder : new Folder(),
+ full_path : "unknown",
+ urlRoot : "/api/folders/",
+ id : "unknown"
+ },
+ parse : function(obj) {
+ this.full_path = obj[0].full_path;
+ // update the inner collection
+ this.get("folder").reset(obj[1].folder_contents);
+ return obj;
+ }
+ })
+
+ // HISTORY ITEM
+ var HistoryItem = Backbone.Model.extend({
+ urlRoot : '/api/histories/'
+ });
+
+ // HISTORY
+ var GalaxyHistory = Backbone.Model.extend({
+ url : '/api/histories/'
+ });
+
+ // HISTORIES
+ var GalaxyHistories = Backbone.Collection.extend({
+ url : '/api/histories',
+ model : GalaxyHistory
+ });
+
+ //ROUTER
+ var LibraryRouter = Backbone.Router.extend({
+ routes: {
+ "" : "libraries",
+ "folders/:id" : "folder_content",
+ "folders/:folder_id/download/:format" : "download"
+ }
+ });
+
+
+// MMMMMMMMMMMMMM
+// === VIEWS ====
+// MMMMMMMMMMMMMM
+
+// galaxy folder
+var FolderContentView = Backbone.View.extend({
+ // main element definition
+ el : '#center',
+ // progress percentage
+ progress: 0,
+ // progress rate per one item
+ progressStep: 1,
+ // last selected history in modal for UX
+ lastSelectedHistory: '',
+ // self modal
+ modal : null,
+ // loaded folders
+ folders : null,
+
+ // initialize
+ initialize : function(){
+ this.folders = [];
+ this.queue = jQuery.Deferred();
+ this.queue.resolve();
+ },
+
+// MMMMMMMMMMMMMMMMMM
+// === TEMPLATES ====
+// MMMMMMMMMMMMMMMMMM
+
+ // set up
+ templateFolder : function (){
+ var tmpl_array = [];
+
+ // CONTAINER
+ tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; ">');
+ tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');
+
+ // TOOLBAR
+ tmpl_array.push('<div id="library_folder_toolbar" >');
+ tmpl_array.push(' <button title="Create New Folder" id="toolbtn_create_folder" class="btn btn-primary" type="button"><span class="fa fa-icon-plus"></span><span class="fa fa-icon-folder-close"></span> folder</button>');
+ tmpl_array.push(' <button id="toolbtn_bulk_import" class="btn btn-primary" style="display: none; margin-left: 0.5em;" type="button"><span class="fa fa-icon-external-link"></span> to history</button>');
+
+ tmpl_array.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; display: none; ">');
+ tmpl_array.push(' <button id="drop_toggle" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">');
+ tmpl_array.push(' <span class="fa fa-icon-download"></span> download <span class="caret"></span>');
+ tmpl_array.push(' </button>');
+ tmpl_array.push(' <ul class="dropdown-menu" role="menu">');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');
+ tmpl_array.push(' </ul>');
+ tmpl_array.push(' </div>');
+
+ tmpl_array.push('</div>');
+
+ // BREADCRUMBS
+ tmpl_array.push('<div class="library_breadcrumb">');
+ tmpl_array.push('<a title="Return to the list of libraries" href="#">Libraries</a><b>|</b> ');
+ tmpl_array.push('<% _.each(path, function(path_item) { %>');
+ tmpl_array.push('<% if (path_item[0] != id) { %>');
+ tmpl_array.push('<a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a><b>|</b> ');
+ tmpl_array.push('<% } else { %>');
+ tmpl_array.push('<span title="You are in this folder"><%- path_item[1] %></span>');
+ tmpl_array.push('<% } %>');
+ tmpl_array.push('<% }); %>');
+ tmpl_array.push('</div>');
+
+ // FOLDER CONTENT
+ tmpl_array.push('<table id="folder_table" class="table table-condensed">');
+ tmpl_array.push(' <thead>');
+ tmpl_array.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');
+ tmpl_array.push(' <th class="button_heading">view</th>');
+ tmpl_array.push(' <th>name</th>');
+ tmpl_array.push(' <th>data type</th>');
+ tmpl_array.push(' <th>size</th>');
+ tmpl_array.push(' <th>date</th>');
+ tmpl_array.push(' </thead>');
+ tmpl_array.push(' <tbody>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td><button title="Go to parent folder" type="button" data-id="<%- upper_folder_id %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-arrow-up"></span> .. go up</td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% _.each(items, function(content_item) { %>');
+ tmpl_array.push(' <tr class="folder_row light" id="<%- content_item.id %>">');
+ tmpl_array.push(' <% if (content_item.get("type") === "folder") { %>'); // folder
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td><button title="Open this folder" type="button" data-id="<%- content_item.id %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-folder-open"></span> browse</td>');
+ tmpl_array.push(' <td><%- content_item.get("name") %>');
+ tmpl_array.push(' <% if (content_item.get("item_count") === 0) { %>'); // empty folder
+ tmpl_array.push(' <span class="muted">(empty folder)</span>');
+ tmpl_array.push(' <% } %>');
+ tmpl_array.push(' </td>');
+ tmpl_array.push(' <td>folder</td>'); // data type
+ tmpl_array.push(' <td><%= _.escape(content_item.get("item_count")) %> item(s)</td>'); // size
+ tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');
+ tmpl_array.push(' <td>');
+ tmpl_array.push(' <button title="See details of this dataset" type="button" class="library-dataset btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-eye-open"></span> details');
+ tmpl_array.push(' </button>');
+ tmpl_array.push(' </td>');
+ tmpl_array.push(' <td><%- content_item.get("name") %></td>'); // dataset
+ tmpl_array.push(' <td><%= _.escape(content_item.get("data_type")) %></td>'); // data type
+ tmpl_array.push(' <td><%= _.escape(content_item.get("readable_size")) %></td>'); // size
+ tmpl_array.push(' <% } %> ');
+ tmpl_array.push(' <td><%= _.escape(content_item.get("time_updated")) %></td>'); // time updated
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push(' ');
+ tmpl_array.push(' </tbody>');
+ tmpl_array.push('</table>');
+
+ tmpl_array.push('</div>');
+ return tmpl_array.join('');
+ },
+ templateDatasetModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<div id="dataset_info_modal">');
+ tmpl_array.push(' <table class="table table-striped table-condensed">');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row" id="id_row" data-id="<%= _.escape(item.get("ldda_id")) %>">Name</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("name")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Data type</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("data_type")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Genome build</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("genome_build")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <th scope="row">Size</th>');
+ tmpl_array.push(' <td><%= _.escape(size) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Date uploaded</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("date_uploaded")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Uploaded by</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("uploaded_by")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr scope="row">');
+ tmpl_array.push(' <th scope="row">Data Lines</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_data_lines")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <th scope="row">Comment Lines</th>');
+ tmpl_array.push(' <% if (item.get("metadata_comment_lines") === "") { %>'); //folder
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_comment_lines")) %></td>');
+ tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td scope="row">unknown</td>');
+ tmpl_array.push(' <% } %>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Number of Columns</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_columns")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Column Types</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_column_types")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Miscellaneous information</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("misc_blurb")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' </table>');
+ tmpl_array.push(' <pre class="peek">');
+ tmpl_array.push(' </pre>');
+ tmpl_array.push('</div>');
+
+ return tmpl_array.join('');
+ },
+
+ templateHistorySelectInModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<span id="history_modal_combo" style="width:90%; margin-left: 1em; margin-right: 1em; ">');
+ tmpl_array.push('Select history: ');
+ tmpl_array.push('<select id="dataset_import_single" name="dataset_import_single" style="width:50%; margin-bottom: 1em; "> ');
+ tmpl_array.push(' <% _.each(histories, function(history) { %>'); //history select box
+ tmpl_array.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push('</select>');
+ tmpl_array.push('</span>');
+
+ return tmpl_array.join('');
+ },
+
+ templateBulkImportInModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');
+ tmpl_array.push('Select history: ');
+ tmpl_array.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');
+ tmpl_array.push(' <% _.each(histories, function(history) { %>'); //history select box
+ tmpl_array.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push('</select>');
+ tmpl_array.push('</span>');
+
+ return tmpl_array.join('');
+ },
+
+ // convert size to nice string
+ size_to_string : function (size)
+ {
+ // identify unit
+ var unit = "";
+ if (size >= 100000000000) { size = size / 100000000000; unit = "TB"; } else
+ if (size >= 100000000) { size = size / 100000000; unit = "GB"; } else
+ if (size >= 100000) { size = size / 100000; unit = "MB"; } else
+ if (size >= 100) { size = size / 100; unit = "KB"; } else
+ { size = size * 10; unit = "b"; }
+ // return formatted string
+ return (Math.round(size) / 10) + unit;
+ },
+
+// MMMMMMMMMMMMMMM
+// === EVENTS ====
+// MMMMMMMMMMMMMMM
+
+ // event binding
+ events: {
+ 'click #select-all-checkboxes' : 'selectAll',
+ 'click .folder_row' : 'selectClicked',
+ 'click #toolbtn_bulk_import' : 'modalBulkImport',
+ 'click #toolbtn_dl' : 'bulkDownload',
+ 'click .library-dataset' : 'showDatasetDetails',
+ 'click #toolbtn_create_folder' : 'createFolderModal',
+ 'click .btn_open_folder' : 'navigateToFolder'
+ },
+
+ //render the folder view
+ render: function (options) {
+ //hack to show scrollbars
+ $("#center").css('overflow','auto');
+
+ view = this;
+ var that = this;
+
+ var folderContainer = new FolderContainer({id: options.id});
+ folderContainer.url = folderContainer.attributes.urlRoot + options.id + '/contents';
+
+ folderContainer.fetch({
+ success: function (container) {
+
+ // prepare nice size strings
+ for (var i = 0; i < folderContainer.attributes.folder.models.length; i++) {
+ var model = folderContainer.attributes.folder.models[i]
+ if (model.get('type') === 'file'){
+ model.set('readable_size', that.size_to_string(model.get('file_size')))
+ }
+ };
+
+ // find the upper id
+ var path = folderContainer.full_path;
+ var upper_folder_id;
+ if (path.length === 1){ // library is above us
+ upper_folder_id = 0;
+ } else {
+ upper_folder_id = path[path.length-2][0];
+ }
+
+ var template = _.template(that.templateFolder(), { path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id, upper_folder_id: upper_folder_id });
+ // var template = _.template(that.templateFolder(), { path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id });
+ that.$el.html(template);
+ }
+ })
+ },
+
+ // handles the click on 'open' and 'upper' folder icons
+ navigateToFolder : function(event){
+ var folder_id = $(event.target).attr('data-id');
+ if (typeof folder_id === 'undefined') {
+ return false;
+ } else if (folder_id === '0'){
+ library_router.navigate('#', {trigger: true, replace: true});
+ } else {
+ library_router.navigate('folders/' + folder_id, {trigger: true, replace: true});
+ }
+ },
+
+ //show modal with current dataset info
+ showDatasetDetails : function(event){
+ // prevent default
+ event.preventDefault();
+
+//TODO check whether we already have the data
+
+ //load the ID of the row
+ var id = $(event.target).parent().parent().attr('id');
+
+ //create new item
+ var item = new Item();
+ var histories = new GalaxyHistories();
+ item.id = id;
+ var self = this;
+
+ //fetch the dataset info
+ item.fetch({
+ success: function (item) {
+// TODO can render here already
+ //fetch user histories for import purposes
+ histories.fetch({
+ success: function (histories){self.renderModalAfterFetch(item, histories)}
+ });
+ }
+ });
+ },
+
+ // show the current dataset in a modal
+ renderModalAfterFetch : function(item, histories){
+ var size = this.size_to_string(item.get('file_size'));
+ var template = _.template(this.templateDatasetModal(), { item : item, size : size });
+ this.modal = null;
+ // make modal
+ var self = this;
+ this.modal = new mod_modal.GalaxyModal({
+ title : 'Dataset Details',
+ body : template,
+ buttons : {
+ 'Import' : function() { self.importCurrentIntoHistory() },
+ 'Download' : function() { self.downloadCurrent() },
+ 'Close' : function() { self.modal.hide(); $('.modal').remove(); self.modal = null; } // TODO refill nicely modal with data
+ }
+ });
+ $(".peek").html(item.get("peek"));
+ var history_footer_tmpl = _.template(this.templateHistorySelectInModal(), {histories : histories.models});
+ $(this.modal.elMain).find('.buttons').prepend(history_footer_tmpl);
+
+ // preset last selected history if we know it
+ if (self.lastSelectedHistory.length > 0) {
+ $(this.modal.elMain).find('#dataset_import_single').val(self.lastSelectedHistory);
+ }
+
+ // show the prepared modal
+ this.modal.show();
+ },
+
+ // download dataset shown currently in modal
+ downloadCurrent : function(){
+ //disable the buttons
+ this.modal.disableButton('Import');
+ this.modal.disableButton('Download');
+
+ var library_dataset_id = [];
+ library_dataset_id.push($('#id_row').attr('data-id'));
+ var url = '/api/libraries/datasets/download/uncompressed';
+ var data = {'ldda_ids' : library_dataset_id};
+
+ // we assume the view is existent
+ folderContentView.processDownload(url, data);
+ this.modal.enableButton('Import');
+ this.modal.enableButton('Download');
+ },
+
+ // import dataset shown currently in modal into selected history
+ importCurrentIntoHistory : function(){
+ //disable the buttons
+ this.modal.disableButton('Import');
+ this.modal.disableButton('Download');
+
+ var history_id = $(this.modal.elMain).find('select[name=dataset_import_single] option:selected').val();
+ this.lastSelectedHistory = history_id; //save selected history for further use
+
+ var library_dataset_id = $('#id_row').attr('data-id');
+ var historyItem = new HistoryItem();
+ var self = this;
+ historyItem.url = historyItem.urlRoot + history_id + '/contents';
+
+ // save the dataset into selected history
+ historyItem.save({ content : library_dataset_id, source : 'library' }, { success : function(){
+ self.modal.showNotification('Dataset imported', 3000, 'success');
+ //enable the buttons
+ self.modal.enableButton('Import');
+ self.modal.enableButton('Download');
+ }, error : function(){
+ self.modal.showNotification('An error occured! Dataset not imported. Please try again later.', 5000, 'error');
+ //enable the buttons
+ self.modal.enableButton('Import');
+ self.modal.enableButton('Download');
+ }
+ });
+ },
+
+ // select all datasets
+ selectAll : function (event) {
+ var selected = event.target.checked;
+ // Iterate each checkbox
+ $(':checkbox').each(function () { this.checked = selected; });
+ this.showTools();
+ },
+
+ // click checkbox on folder click
+ selectClicked : function (event) {
+ var checkbox = $("#" + event.target.parentElement.id).find(':checkbox')
+ if (checkbox[0] != undefined) {
+ if (checkbox[0].checked){
+ checkbox[0].checked = '';
+ // $(event.target.parentElement).css('background-color', '').css('color', '');
+ $(event.target.parentElement).removeClass('dark');
+ $(event.target.parentElement).find('a').removeClass('dark');
+ $(event.target.parentElement).addClass('light');
+ $(event.target.parentElement).find('a').addClass('light');
+ } else {
+ checkbox[0].checked = 'selected';
+ $(event.target.parentElement).removeClass('light');
+ $(event.target.parentElement).find('a').removeClass('light');
+ $(event.target.parentElement).addClass('dark');
+ $(event.target.parentElement).find('a').addClass('dark');
+ // $(event.target.parentElement).css('background-color', '#8389a1').css('color', 'white');
+ }
+ }
+ this.showTools();
+ },
+
+ // show toolbar in case something is selected
+ showTools : function(){
+ var checkedValues = $('#folder_table').find(':checked');
+ if(checkedValues.length > 0){
+ $('#toolbtn_bulk_import').show();
+ $('#toolbtn_dl').show();
+ } else {
+ $('#toolbtn_bulk_import').hide();
+ $('#toolbtn_dl').hide();
+ }
+
+ },
+
+ // show bulk import modal
+ modalBulkImport : function(){
+ var self = this;
+ // fetch histories
+ var histories = new GalaxyHistories();
+ histories.fetch({
+ success: function (histories){
+ // make modal
+ var history_modal_tmpl = _.template(self.templateBulkImportInModal(), {histories : histories.models});
+ self.modal = new mod_modal.GalaxyModal({
+ title : 'Import into History',
+ body : history_modal_tmpl,
+ buttons : {
+ 'Import' : function() {self.importAllIntoHistory()},
+ 'Close' : function() {self.modal.hide(); $('.modal').remove(); self.modal = null;}
+ }
+ });
+ // show the prepared modal
+ self.modal.show();
+ }
+ });
+ },
+
+ // import all selected datasets into history
+ importAllIntoHistory : function (){
+ //disable the button
+ this.modal.disableButton('Import');
+
+ var history_id = $("select[name=dataset_import_bulk] option:selected").val();
+ var history_name = $("select[name=dataset_import_bulk] option:selected").text();
+
+ var dataset_ids = [];
+ $('#folder_table').find(':checked').each(function(){
+ if (this.parentElement.parentElement.id != '') {
+ dataset_ids.push(this.parentElement.parentElement.id);
+ }
+ });
+ var progress_bar_tmpl = _.template(this.templateProgressBar(), { history_name : history_name });
+ $(this.modal.elMain).find('.modal-body').html(progress_bar_tmpl);
+
+ // init the progress bar
+ var progressStep = 100 / dataset_ids.length;
+ this.initProgress(progressStep);
+
+ // prepare the dataset objects to be imported
+ var datasets_to_import = [];
+ for (var i = dataset_ids.length - 1; i >= 0; i--) {
+ library_dataset_id = dataset_ids[i];
+ var historyItem = new HistoryItem();
+ var self = this;
+ historyItem.url = historyItem.urlRoot + history_id + '/contents';
+ historyItem.content = library_dataset_id;
+ historyItem.source = 'library';
+ datasets_to_import.push(historyItem);
+ };
+
+ // call the recursive function to call ajax one after each other
+ this.chainCall(datasets_to_import);
+ },
+
+ chainCall: function(history_item_set){
+ var self = this;
+ var popped_item = history_item_set.pop();
+ if (typeof popped_item === "undefined") {
+ self.modal.showNotification('All datasets imported', 3000, 'success');
+ // enable button again
+ self.modal.enableButton('Import');
+ return
+ }
+ var promise = $.when(popped_item.save({content: popped_item.content, source: popped_item.source})).done(function(a1){
+ self.updateProgress();
+ responses.push(a1);
+ self.chainCall(history_item_set);
+ });
+ },
+
+ initProgress: function(progressStep){
+ this.progress = 0;
+ this.progressStep = progressStep;
+ },
+ updateProgress: function(){
+ this.progress += this.progressStep;
+ $('.progress-bar').width(Math.round(this.progress) + '%');
+ txt_representation = Math.round(this.progress) + '% Complete';
+ $('.completion_span').text(txt_representation);
+ },
+
+ // progress bar
+ templateProgressBar : function (){
+ var tmpl_array = [];
+
+ tmpl_array.push('<div class="import_text">');
+ tmpl_array.push('Importing selected datasets to history <b><%= _.escape(history_name) %></b>');
+ tmpl_array.push('</div>');
+ tmpl_array.push('<div class="progress">');
+ tmpl_array.push(' <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');
+ tmpl_array.push(' <span class="completion_span">0% Complete</span>');
+ tmpl_array.push(' </div>');
+ tmpl_array.push('</div>');
+ tmpl_array.push('');
+
+ return tmpl_array.join('');
+ },
+
+ // download selected datasets
+ download : function(folder_id, format){
+ var dataset_ids = [];
+ $('#folder_table').find(':checked').each(function(){
+ if (this.parentElement.parentElement.id != '') {
+ dataset_ids.push(this.parentElement.parentElement.id);
+ }
+ });
+
+ var url = '/api/libraries/datasets/download/' + format;
+ var data = {'ldda_ids' : dataset_ids};
+ this.processDownload(url, data);
+ },
+
+ // create hidden form and submit through POST to initialize download
+ processDownload: function(url, data, method){
+ //url and data options required
+ if( url && data ){
+ //data can be string of parameters or array/object
+ data = typeof data == 'string' ? data : $.param(data);
+ //split params into form inputs
+ var inputs = '';
+ $.each(data.split('&'), function(){
+ var pair = this.split('=');
+ inputs+='<input type="hidden" name="'+ pair[0] +'" value="'+ pair[1] +'" />';
+ });
+ //send request
+ $('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
+ .appendTo('body').submit().remove();
+ };
+ },
+
+ // shows modal for creating folder
+ createFolderModal: function(){
+ alert('creating folder');
+ }
+
+ });
+
+// galaxy library view
+var GalaxyLibraryview = Backbone.View.extend({
+ el: '#center',
+
+ events: {
+ 'click #create_new_library_btn' : 'show_library_modal'
+ },
+
+ // initialize
+ initialize : function(){
+ },
+
+ // template
+ template_library_list : function (){
+ tmpl_array = [];
+ tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; overflow: auto !important; ">');
+
+ tmpl_array.push('');
+ tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');
+ tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary icon-file ">New Library</a>');
+ tmpl_array.push('<table class="table table-condensed">');
+ tmpl_array.push(' <thead>');
+ tmpl_array.push(' <th class="button_heading"></th>');
+ tmpl_array.push(' <th>name</th>');
+ tmpl_array.push(' <th>description</th>');
+ tmpl_array.push(' <th>synopsis</th> ');
+ tmpl_array.push(' <th>model type</th> ');
+ tmpl_array.push(' </thead>');
+ tmpl_array.push(' <tbody>');
+ tmpl_array.push(' <% _.each(libraries, function(library) { %>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <td><button title="Open this library" type="button" data-id="<%- library.get("root_folder_id") %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-folder-open"></span> browse</td>');
+ tmpl_array.push(' <td><%- library.get("name") %></td>');
+ tmpl_array.push(' <td><%= _.escape(library.get("description")) %></td>');
+ tmpl_array.push(' <td><%= _.escape(library.get("synopsis")) %></td>');
+ tmpl_array.push(' <td><%= _.escape(library.get("model_class")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push(' </tbody>');
+ tmpl_array.push('</table>');
+
+ tmpl_array.push('</div>');
+ return tmpl_array.join('');
+ },
+
+ // render
+ render: function () {
+ //hack to show scrollbars
+ $("#center").css('overflow','auto');
+ var that = this;
+ // if (typeof libraries === "undefined") {
+ libraries = new Libraries();
+ // }
+ libraries.fetch({
+ success: function (libraries) {
+ var template = _.template(that.template_library_list(), { libraries : libraries.models });
+ that.$el.html(template);
+ }
+ })
+ },
+
+ // own modal
+ modal : null,
+
+ // show/hide create library modal
+ show_library_modal : function (event){
+ event.preventDefault();
+ event.stopPropagation();
+
+ // create modal
+ var self = this;
+ this.modal = new mod_modal.GalaxyModal(
+ {
+ title : 'Create New Library',
+ body : this.template_new_library(),
+ buttons : {
+ 'Create' : function() {self.create_new_library_event()},
+ 'Close' : function() {self.modal.hide()}
+ }
+ });
+
+ // show prepared modal
+ this.modal.show();
+ },
+
+ // create the new library from modal
+ create_new_library_event: function(){
+ var libraryDetails = this.serialize_new_library();
+ var valid = this.validate_new_library(libraryDetails);
+ var library = new Library();
+ var self = this;
+ library.save(libraryDetails, {
+ success: function (library) {
+ self.modal.hide();
+ self.clear_library_modal();
+ self.render();
+ },
+ error: function(){
+ self.modal.showNotification('An error occured', 5000, 'error');
+ }
+ });
+ return false;
+ },
+
+ // clear the library modal once saved
+ clear_library_modal : function(){
+ $("input[name='Name']").val('');
+ $("input[name='Description']").val('');
+ $("input[name='Synopsis']").val('');
+ },
+
+ // serialize data from the form
+ serialize_new_library : function(){
+ return {
+ name: $("input[name='Name']").val(),
+ description: $("input[name='Description']").val(),
+ synopsis: $("input[name='Synopsis']").val()
+ };
+ },
+
+ validate_new_library: function(library){
+
+ },
+
+
+ // template for new library modal
+ template_new_library: function()
+ {
+ tmpl_array = [];
+
+ tmpl_array.push('<div id="new_library_modal">');
+ tmpl_array.push('<form>');
+ tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');
+ tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');
+ tmpl_array.push('<input type="text" name="Synopsis" value="" placeholder="Synopsis">');
+ tmpl_array.push('</form>');
+ tmpl_array.push('</div>');
+
+ return tmpl_array.join('');
+ }
+});
+
+// galaxy library wrapper View
+var GalaxyLibrary = Backbone.View.extend({
+ folderContentView : null,
+ galaxyLibraryview : null,
+ initialize : function(){
+
+ folderContentView = new FolderContentView();
+ galaxyLibraryview = new GalaxyLibraryview();
+
+ library_router = new LibraryRouter();
+
+ library_router.on('route:libraries', function() {
+ // render libraries list
+ galaxyLibraryview.render();
+ });
+
+ library_router.on('route:folder_content', function(id) {
+ // render folder's contents
+ folderContentView.render({id: id});
+ });
+
+ library_router.on('route:download', function(folder_id, format) {
+ // send download stream
+ if (typeof folderContentView === 'undefined'){
+ alert('you cant touch this!');
+ // } else if (folderContentView.modal !== null){
+ // folderContentView.download(folder_id, format);
+ } else if ($('#center').find(':checked').length === 0) { // coming from outside of the library app
+ library_router.navigate('folders/' + folder_id, {trigger: true, replace: true});
+ // TODO show message of redirection
+ } else {
+ folderContentView.download(folder_id, format);
+ library_router.navigate('folders/' + folder_id, {trigger: false, replace: true});
+ }
+
+ });
+
+Backbone.history.start();
+
+return this
+}
+});
+
+// return
+return {
+ GalaxyApp: GalaxyLibrary
+};
+
+});
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 static/scripts/galaxy.library.router.js
--- /dev/null
+++ b/static/scripts/galaxy.library.router.js
@@ -0,0 +1,21 @@
+define( [], function() {
+
+/**
+ * -- Routers --
+ */
+
+/**
+ * Router for library browser.
+ */
+var LibraryRouter = Backbone.Router.extend({
+ routes: {
+ "" : "libraries",
+ "folders/:id" : "folder_content"
+ }
+});
+
+return {
+ LibraryRouter: LibraryRouter,
+};
+
+})
\ No newline at end of file
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 static/scripts/galaxy.menu.js
--- a/static/scripts/galaxy.menu.js
+++ b/static/scripts/galaxy.menu.js
@@ -58,6 +58,12 @@
tab_shared.add({
title : "Data Libraries",
content : "library/index",
+ content : "library/index"
+ });
+
+ tab_shared.add({
+ title : "New Libraries",
+ content : "library/list",
divider : true
});
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -1,6 +1,3 @@
-/*
- galaxy modal
-*/
// dependencies
define([], function() {
@@ -25,8 +22,51 @@
// initialize
initialize : function(options) {
+ self = this;
if (options)
this.create(options);
+
+ this.bindClick(event, self);
+ },
+
+ // bind the click-to-hide function
+ bindClick: function(event, that) {
+ // bind the ESC key to hide() function
+ $(document).on('keyup', function(event){
+ if (event.keyCode == 27) { self.hide(); }
+ })
+ // bind the click anywhere to hide() function...
+ $('html').on('click', function(event){
+ that.hide();
+ })
+ // ...but don't hide if the click is on modal content
+ $('.modal-content').on('click', function(event){
+ event.stopPropagation();
+ })
+ },
+
+ // unbind the click-to-hide function
+ unbindClick: function(event, that){
+ // bind the ESC key to hide() function
+ $(document).off('keyup', function(event){
+ if (event.keyCode == 27) { that.hide(); }
+ })
+ // unbind the click anywhere to hide() function...
+ $('html').off('click', function(event){
+ that.hide();
+ })
+ // ...but don't hide if the click is on modal content
+ $('.modal-content').off('click', function(event){
+ event.stopPropagation();
+ })
+ },
+
+
+ // destroy
+ destroy : function(){
+ this.hide();
+ this.unbindClick();
+ $('.modal').remove();
},
// adds and displays a new frame/window
@@ -87,6 +127,7 @@
this.$footer = (this.$el).find('.modal-footer');
this.$buttons = (this.$el).find('.buttons');
this.$backdrop = (this.$el).find('.modal-backdrop');
+ this.$notification = (this.$el).find('.notification-modal');
// append body
this.$body.html(this.options.body);
@@ -120,6 +161,47 @@
this.$buttons.find('#' + String(name).toLowerCase()).prop('disabled', true);
},
+ // hide buttons
+ hideButton: function(name) {
+ this.$buttons.find('#' + String(name).toLowerCase()).hide();
+ },
+ // show buttons
+ showButton: function(name) {
+ this.$buttons.find('#' + String(name).toLowerCase()).show();
+ },
+
+ // show notification
+ showNotification : function(message, duration, type) {
+ // defaults
+ var duration = typeof duration !== 'undefined' ? duration : 1500;
+ // var bgColor = typeof bgColor !== 'undefined' ? bgColor : "#F4E0E1";
+ // var txtColor = typeof txtColor !== 'undefined' ? txtColor : "#A42732";
+ var bgColor;
+ var txtColor;
+
+ if (type === 'error'){
+ bgColor = '#f4e0e1';
+ txtColor = '#a42732';
+ // } else if (type === 'success'){
+ } else { // success is default
+ bgColor = '#e1f4e0';
+ txtColor = '#32a427';
+ }
+
+ var HTMLmessage = "<div class='notification-message' style='text-align:center; line-height:16px; '> " + message + " </div>";
+ this.$notification.html("<div id='notification-bar' style='display:none; float: right; height: 16px; width:100%; background-color: " + bgColor + "; z-index: 100; color: " + txtColor + ";border-bottom: 1px solid " + txtColor + ";'>" + HTMLmessage + "</div>");
+
+ var self = this;
+
+ /*animate the bar*/
+ $('#notification-bar').slideDown(function() {
+ setTimeout(function() {
+ $('#notification-bar').slideUp(function() {self.$notification.html('');});
+ }, duration);
+ });
+
+ },
+
// returns scroll top for body element
scrollTop: function()
{
@@ -135,10 +217,10 @@
return '<div class="modal">' +
'<div class="modal-backdrop fade in" style="z-index: -1;"></div>' +
'<div class="modal-dialog">' +
- '<div class="modal-content">' +
'<div class="modal-header">' +
'<button type="button" class="close" style="display: none;">×</button>' +
'<h4 class="title">' + title + '</h4>' +
+ '<span class="notification-modal"></span>' +
'</div>' +
'<div class="modal-body"></div>' +
'<div class="modal-footer">' +
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 static/scripts/libs/bootstrap.js
--- a/static/scripts/libs/bootstrap.js
+++ b/static/scripts/libs/bootstrap.js
@@ -575,3 +575,158 @@
}
}(window.jQuery);
+
+/* ========================================================================
+ * Bootstrap: dropdown.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#dropdowns
+ * ========================================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // DROPDOWN CLASS DEFINITION
+ // =========================
+
+ var backdrop = '.dropdown-backdrop'
+ var toggle = '[data-toggle=dropdown]'
+ var Dropdown = function (element) {
+ var $el = $(element).on('click.bs.dropdown', this.toggle)
+ }
+
+ Dropdown.prototype.toggle = function (e) {
+ var $this = $(this)
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ clearMenus()
+
+ if (!isActive) {
+ if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
+ // if mobile we we use a backdrop because click events don't delegate
+ $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
+ }
+
+ $parent.trigger(e = $.Event('show.bs.dropdown'))
+
+ if (e.isDefaultPrevented()) return
+
+ $parent
+ .toggleClass('open')
+ .trigger('shown.bs.dropdown')
+
+ $this.focus()
+ }
+
+ return false
+ }
+
+ Dropdown.prototype.keydown = function (e) {
+ if (!/(38|40|27)/.test(e.keyCode)) return
+
+ var $this = $(this)
+
+ e.preventDefault()
+ e.stopPropagation()
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ if (!isActive || (isActive && e.keyCode == 27)) {
+ if (e.which == 27) $parent.find(toggle).focus()
+ return $this.click()
+ }
+
+ var $items = $('[role=menu] li:not(.divider):visible a', $parent)
+
+ if (!$items.length) return
+
+ var index = $items.index($items.filter(':focus'))
+
+ if (e.keyCode == 38 && index > 0) index-- // up
+ if (e.keyCode == 40 && index < $items.length - 1) index++ // down
+ if (!~index) index=0
+
+ $items.eq(index).focus()
+ }
+
+ function clearMenus() {
+ $(backdrop).remove()
+ $(toggle).each(function (e) {
+ var $parent = getParent($(this))
+ if (!$parent.hasClass('open')) return
+ $parent.trigger(e = $.Event('hide.bs.dropdown'))
+ if (e.isDefaultPrevented()) return
+ $parent.removeClass('open').trigger('hidden.bs.dropdown')
+ })
+ }
+
+ function getParent($this) {
+ var selector = $this.attr('data-target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ var $parent = selector && $(selector)
+
+ return $parent && $parent.length ? $parent : $this.parent()
+ }
+
+
+ // DROPDOWN PLUGIN DEFINITION
+ // ==========================
+
+ var old = $.fn.dropdown
+
+ $.fn.dropdown = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('dropdown')
+
+ if (!data) $this.data('dropdown', (data = new Dropdown(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ $.fn.dropdown.Constructor = Dropdown
+
+
+ // DROPDOWN NO CONFLICT
+ // ====================
+
+ $.fn.dropdown.noConflict = function () {
+ $.fn.dropdown = old
+ return this
+ }
+
+
+ // APPLY TO STANDARD DROPDOWN ELEMENTS
+ // ===================================
+
+ $(document)
+ .on('click.bs.dropdown.data-api', clearMenus)
+ .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+ .on('click.bs.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
+ .on('keydown.bs.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
+
+}(window.jQuery);
\ No newline at end of file
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 static/style/Gruntfile.js
--- a/static/style/Gruntfile.js
+++ b/static/style/Gruntfile.js
@@ -6,7 +6,7 @@
var theme = grunt.option( 'theme', 'blue' );
var out = 'blue'
- var lessFiles = [ 'base', 'autocomplete_tagging', 'embed_item', 'iphone', 'masthead', 'library', 'trackster' ];
+ var lessFiles = [ 'base', 'autocomplete_tagging', 'embed_item', 'iphone', 'masthead', 'library', 'new_library', 'trackster' ];
var _ = grunt.util._;
var fmt = _.sprintf;
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 static/style/src/less/library.less
--- a/static/style/src/less/library.less
+++ b/static/style/src/less/library.less
@@ -29,6 +29,50 @@
background-color: @table-bg-accent;
}
+
+tr.light td
+{
+ background-color: white;
+ color: black;
+}
+tr.light:hover td
+{
+ background-color: #f5f5f5;
+ color: #8389a1;
+}
+tr.dark td
+{
+ background-color: #8389a1;
+ color: white;
+}
+tr.dark:hover td
+{
+ background-color: #bbbfd0;
+ color: white;
+}
+a.dark:hover
+{
+ color: yellow;
+ // text-decoration: none;
+}
+a.dark
+{
+ color: white;
+ // text-decoration: none;
+}
+th.button_heading
+{
+ width: 7em;
+}
+div.library_breadcrumb{
+ padding-top: 0.8em;
+ padding-bottom: 0.8em;
+}
+div.library_breadcrumb a:hover{
+ color:green;
+}
+
+
img.expanderIcon {
padding-right: 4px;
}
diff -r d5217f01960360a9d7298fa679b839c806cab893 -r e2b8d3481d668203bfad7efe306925c72c644839 templates/base/base_panels.mako
--- a/templates/base/base_panels.mako
+++ b/templates/base/base_panels.mako
@@ -357,4 +357,4 @@
## Scripts can be loaded later since they progressively add features to
## the panels, but do not change layout
${self.late_javascripts()}
-</html>
+</html>
\ No newline at end of file
https://bitbucket.org/galaxy/galaxy-central/commits/fb3161b21a4b/
Changeset: fb3161b21a4b
User: martenson
Date: 2013-12-11 00:12:14
Summary: aiding the merge of data_library branch
Affected #: 5 files
diff -r e2b8d3481d668203bfad7efe306925c72c644839 -r fb3161b21a4bfc1c5bd2f6bea8dc63c9c5bb4b38 .hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -114,8 +114,4 @@
*.orig
.DS_Store
*.rej
-*~
-
-
-syntax: regexp
-^static/AAA_scratch$
\ No newline at end of file
+*~
\ No newline at end of file
diff -r e2b8d3481d668203bfad7efe306925c72c644839 -r fb3161b21a4bfc1c5bd2f6bea8dc63c9c5bb4b38 lib/galaxy/webapps/galaxy/api/folder_contents.py
--- a/lib/galaxy/webapps/galaxy/api/folder_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/folder_contents.py
@@ -100,8 +100,6 @@
encoded_id = trans.security.encode_id( content_item.id )
time_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
time_created = content_item.create_time.strftime( "%Y-%m-%d %I:%M %p" )
-# log.debug('XXXXXXXXXXXXXXXXXXX api type: ' + str(content_item.api_type))
-# log.debug('XXXXXXXX ALL: ' + str(content_item.__dict__))
# For folder return also hierarchy values
if content_item.api_type == 'folder':
@@ -110,7 +108,6 @@
return_item.update ( dict ( item_count = content_item.item_count ) )
if content_item.api_type == 'file':
-# log.debug('XXXXX content item class: ' + str(content_item.__class__))
library_dataset_dict = content_item.to_dict()
library_dataset_dict['data_type']
library_dataset_dict['file_size']
diff -r e2b8d3481d668203bfad7efe306925c72c644839 -r fb3161b21a4bfc1c5bd2f6bea8dc63c9c5bb4b38 static/scripts/galaxy.library.router.js
--- a/static/scripts/galaxy.library.router.js
+++ /dev/null
@@ -1,21 +0,0 @@
-define( [], function() {
-
-/**
- * -- Routers --
- */
-
-/**
- * Router for library browser.
- */
-var LibraryRouter = Backbone.Router.extend({
- routes: {
- "" : "libraries",
- "folders/:id" : "folder_content"
- }
-});
-
-return {
- LibraryRouter: LibraryRouter,
-};
-
-})
\ No newline at end of file
diff -r e2b8d3481d668203bfad7efe306925c72c644839 -r fb3161b21a4bfc1c5bd2f6bea8dc63c9c5bb4b38 static/scripts/galaxy.menu.js
--- a/static/scripts/galaxy.menu.js
+++ b/static/scripts/galaxy.menu.js
@@ -57,7 +57,6 @@
tab_shared.add({
title : "Data Libraries",
- content : "library/index",
content : "library/index"
});
diff -r e2b8d3481d668203bfad7efe306925c72c644839 -r fb3161b21a4bfc1c5bd2f6bea8dc63c9c5bb4b38 static/style/Gruntfile.js
--- a/static/style/Gruntfile.js
+++ b/static/style/Gruntfile.js
@@ -6,7 +6,7 @@
var theme = grunt.option( 'theme', 'blue' );
var out = 'blue'
- var lessFiles = [ 'base', 'autocomplete_tagging', 'embed_item', 'iphone', 'masthead', 'library', 'new_library', 'trackster' ];
+ var lessFiles = [ 'base', 'autocomplete_tagging', 'embed_item', 'iphone', 'masthead', 'library', 'trackster' ];
var _ = grunt.util._;
var fmt = _.sprintf;
https://bitbucket.org/galaxy/galaxy-central/commits/96f52c9fd562/
Changeset: 96f52c9fd562
User: martenson
Date: 2013-12-11 00:21:06
Summary: Merged galaxy/galaxy-central into default
Affected #: 3 files
diff -r fb3161b21a4bfc1c5bd2f6bea8dc63c9c5bb4b38 -r 96f52c9fd562fe160fc8ac71aa6cddd902d23e0f config/plugins/visualizations/scatterplot/templates/scatterplot.mako
--- a/config/plugins/visualizations/scatterplot/templates/scatterplot.mako
+++ b/config/plugins/visualizations/scatterplot/templates/scatterplot.mako
@@ -15,7 +15,6 @@
<script type="text/javascript" src="/static/scripts/libs/jquery/jquery.migrate.js"></script><script type="text/javascript" src="/static/scripts/libs/underscore.js"></script><script type="text/javascript" src="/static/scripts/libs/backbone/backbone.js"></script>
-<script type="text/javascript" src="/static/scripts/libs/backbone/backbone-relational.js"></script><script type="text/javascript" src="/static/scripts/libs/handlebars.runtime.js"></script><script type="text/javascript" src="/static/scripts/libs/d3.js"></script><script type="text/javascript" src="/static/scripts/libs/bootstrap.js"></script>
diff -r fb3161b21a4bfc1c5bd2f6bea8dc63c9c5bb4b38 -r 96f52c9fd562fe160fc8ac71aa6cddd902d23e0f lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py
+++ b/lib/galaxy/web/base/controller.py
@@ -474,6 +474,9 @@
"""Returns history data in the form of a dictionary.
"""
history_dict = history.to_dict( view='element', value_mapper={ 'id':trans.security.encode_id })
+ history_dict[ 'user_id' ] = None
+ if history.user_id:
+ history_dict[ 'user_id' ] = trans.security.encode_id( history.user_id )
history_dict[ 'nice_size' ] = history.get_disk_size( nice_size=True )
history_dict[ 'annotation' ] = history.get_item_annotation_str( trans.sa_session, trans.user, history )
diff -r fb3161b21a4bfc1c5bd2f6bea8dc63c9c5bb4b38 -r 96f52c9fd562fe160fc8ac71aa6cddd902d23e0f lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
--- a/lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
+++ b/lib/tool_shed/galaxy_install/tool_dependencies/install_util.py
@@ -93,7 +93,7 @@
env_file_builder.append_line( action="source", value=required_tool_dependency_env_file_path )
return_code = env_file_builder.return_code
if return_code:
- error_message = 'Error defining env.sh file for package %s, return_code: %s' % ( str( package_name, str( return_code ) ) )
+ error_message = 'Error defining env.sh file for package %s, return_code: %s' % ( str( package_name ), str( return_code ) )
tool_dependency = tool_dependency_util.handle_tool_dependency_installation_error( app,
tool_dependency,
error_message,
https://bitbucket.org/galaxy/galaxy-central/commits/42e74befc796/
Changeset: 42e74befc796
User: martenson
Date: 2013-12-11 00:29:50
Summary: aid after merge data_library branch
Affected #: 1 file
diff -r 96f52c9fd562fe160fc8ac71aa6cddd902d23e0f -r 42e74befc7962afb93baba6197273565755e086c lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -73,6 +73,8 @@
webapp.add_route( '/datasets/:dataset_id/display/{filename:.+?}', controller='dataset', action='display', dataset_id=None, filename=None)
webapp.add_route( '/datasets/:dataset_id/:action/:filename', controller='dataset', action='index', dataset_id=None, filename=None)
webapp.add_route( '/display_application/:dataset_id/:app_name/:link_name/:user_id/:app_action/:action_param',
+ controller='dataset', action='display_application', dataset_id=None, user_id=None,
+ app_name = None, link_name = None, app_action = None, action_param = None )
webapp.add_route( '/u/:username/d/:slug/:filename', controller='dataset', action='display_by_username_and_slug', filename=None )
webapp.add_route( '/u/:username/p/:slug', controller='page', action='display_by_username_and_slug' )
webapp.add_route( '/u/:username/h/:slug', controller='history', action='display_by_username_and_slug' )
https://bitbucket.org/galaxy/galaxy-central/commits/8fcfddb3e1b5/
Changeset: 8fcfddb3e1b5
User: martenson
Date: 2013-12-11 16:41:39
Summary: aiding problematic merging of data_library branch, removing duplicates, removing API v2 relicts
Affected #: 3 files
diff -r 42e74befc7962afb93baba6197273565755e086c -r 8fcfddb3e1b57ad627ebab3619920552a6e967ff .hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -114,4 +114,5 @@
*.orig
.DS_Store
*.rej
-*~
\ No newline at end of file
+*~
+
diff -r 42e74befc7962afb93baba6197273565755e086c -r 8fcfddb3e1b57ad627ebab3619920552a6e967ff lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -46,7 +46,6 @@
atexit.register( app.shutdown )
# Create the universe WSGI application
webapp = GalaxyWebApplication( app, session_cookie='galaxysession', name='galaxy' )
- # Add the controllers folder
# Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
controller='admin_toolshed',
@@ -54,17 +53,11 @@
repository_id=None,
image_file=None )
webapp.add_ui_controllers( 'galaxy.webapps.galaxy.controllers', app )
- # Add the api folder
- webapp.add_api_controllers( 'galaxy.webapps.galaxy.api', app )
- # Add the api folder VERSION 2
- webapp.add_api_controllers( 'galaxy.webapps.galaxy.api.v2', app )
-
# Force /history to go to /root/history -- needed since the tests assume this
webapp.add_route( '/history', controller='root', action='history' )
# Force /activate to go to the controller
webapp.add_route( '/activate', controller='user', action='activate' )
# These two routes handle our simple needs at the moment
-
webapp.add_route( '/async/:tool_id/:data_id/:data_secret', controller='async', action='index', tool_id=None, data_id=None, data_secret=None )
webapp.add_route( '/:controller/:action', action='index' )
webapp.add_route( '/:action', controller='root', action='index' )
@@ -73,8 +66,8 @@
webapp.add_route( '/datasets/:dataset_id/display/{filename:.+?}', controller='dataset', action='display', dataset_id=None, filename=None)
webapp.add_route( '/datasets/:dataset_id/:action/:filename', controller='dataset', action='index', dataset_id=None, filename=None)
webapp.add_route( '/display_application/:dataset_id/:app_name/:link_name/:user_id/:app_action/:action_param',
- controller='dataset', action='display_application', dataset_id=None, user_id=None,
- app_name = None, link_name = None, app_action = None, action_param = None )
+ controller='dataset', action='display_application', dataset_id=None, user_id=None,
+ app_name = None, link_name = None, app_action = None, action_param = None )
webapp.add_route( '/u/:username/d/:slug/:filename', controller='dataset', action='display_by_username_and_slug', filename=None )
webapp.add_route( '/u/:username/p/:slug', controller='page', action='display_by_username_and_slug' )
webapp.add_route( '/u/:username/h/:slug', controller='history', action='display_by_username_and_slug' )
@@ -82,22 +75,12 @@
webapp.add_route( '/u/:username/v/:slug', controller='visualization', action='display_by_username_and_slug' )
webapp.add_route( '/search', controller='search', action='index' )
- # Add the web API
+ # ================
+ # ===== API =====
+ # ================
+
webapp.add_api_controllers( 'galaxy.webapps.galaxy.api', app )
- # The /folders section is experimental at this point:
- log.debug( "app.config.api_folders: %s" % app.config.api_folders )
- webapp.mapper.resource( 'folder', 'folders', path_prefix='/api' )
- webapp.mapper.resource( 'content', 'contents',
- controller='folder_contents',
- name_prefix='folder_',
- path_prefix='/api/folders/:folder_id',
- parent_resources=dict( member_name='folder', collection_name='folders' ) )
- webapp.mapper.resource( 'content',
- 'contents',
- controller='library_contents',
- name_prefix='library_',
- path_prefix='/api/libraries/:library_id',
- parent_resources=dict( member_name='library', collection_name='libraries' ) )
+
webapp.mapper.resource( 'content',
'contents',
controller='history_contents',
@@ -109,10 +92,6 @@
controller="datasets",
action="display",
conditions=dict(method=["GET"]))
- webapp.mapper.resource( 'permission',
- 'permissions',
- path_prefix='/api/libraries/:library_id',
- parent_resources=dict( member_name='library', collection_name='libraries' ) )
webapp.mapper.resource( 'user',
'users',
controller='group_users',
@@ -134,11 +113,6 @@
_add_item_tags_controller( webapp,
name_prefix="workflow_",
path_prefix='/api/workflows/:workflow_id' )
-
- _add_item_extended_metadata_controller( webapp,
- name_prefix="library_dataset_",
- path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
-
_add_item_annotation_controller( webapp,
name_prefix="history_content_",
path_prefix='/api/histories/:history_id/contents/:history_content_id' )
@@ -148,7 +122,6 @@
_add_item_annotation_controller( webapp,
name_prefix="workflow_",
path_prefix='/api/workflows/:workflow_id' )
-
_add_item_provenance_controller( webapp,
name_prefix="history_content_",
path_prefix='/api/histories/:history_id/contents/:history_content_id' )
@@ -178,13 +151,12 @@
#webapp.mapper.connect( 'run_workflow', '/api/workflow/{workflow_id}/library/{library_id}', controller='workflows', action='run', workflow_id=None, library_id=None, conditions=dict(method=["GET"]) )
webapp.mapper.resource( 'search', 'search', path_prefix='/api' )
- webapp.mapper.resource_with_deleted( 'quota', 'quotas', path_prefix='/api' )
- webapp.mapper.resource_with_deleted( 'history', 'histories', path_prefix='/api' )
- webapp.mapper.resource_with_deleted( 'user', 'users', path_prefix='/api' )
-
# =======================
# ===== LIBRARY API =====
# =======================
+
+ # The /folders section is experimental at this point:
+ log.debug( "app.config.api_folders: %s" % app.config.api_folders )
webapp.mapper.connect( 'show_lda_item', '/api/libraries/datasets/:id', controller='lda_datasets', action='show', conditions=dict(method=["GET"]) )
webapp.mapper.connect( 'download_lda_items', '/api/libraries/datasets/download/:format', controller='lda_datasets', action='download', conditions=dict(method=["POST"]) )
@@ -232,6 +204,9 @@
# Preserve the following download route for now for dependent applications -- deprecate at some point
webapp.mapper.connect("workflow_dict", '/api/workflows/download/{workflow_id}', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
+ # ====================
+ # ===== TOOLSHED =====
+ # ====================
# Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
@@ -239,7 +214,7 @@
action='display_image_in_repository',
repository_id=None,
image_file=None )
-
+
# Galaxy API for tool shed features.
webapp.mapper.resource( 'tool_shed_repository',
'tool_shed_repositories',
@@ -253,6 +228,7 @@
path_prefix='/api',
new={ 'install_repository_revision' : 'POST' },
parent_resources=dict( member_name='tool_shed_repository', collection_name='tool_shed_repositories' ) )
+
# Connect logger from app
if app.trace_logger:
webapp.trace_logger = app.trace_logger
@@ -273,11 +249,6 @@
galaxy.model.mapping.metadata.engine.connection_provider._pool.dispose()
except:
pass
- # Close any pooled database connections before forking
- try:
- galaxy.model.tool_shed_install.mapping.metadata.engine.connection_provider._pool.dispose()
- except:
- pass
# Return
return webapp
https://bitbucket.org/galaxy/galaxy-central/commits/4506583cb1bc/
Changeset: 4506583cb1bc
User: martenson
Date: 2013-12-11 16:51:21
Summary: aiding merg problems with data_library branch
Affected #: 1 file
diff -r 8fcfddb3e1b57ad627ebab3619920552a6e967ff -r 4506583cb1bc66db01d98c1619b6829fcda31e00 lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -46,12 +46,6 @@
atexit.register( app.shutdown )
# Create the universe WSGI application
webapp = GalaxyWebApplication( app, session_cookie='galaxysession', name='galaxy' )
- # Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
- webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
- controller='admin_toolshed',
- action='display_image_in_repository',
- repository_id=None,
- image_file=None )
webapp.add_ui_controllers( 'galaxy.webapps.galaxy.controllers', app )
# Force /history to go to /root/history -- needed since the tests assume this
webapp.add_route( '/history', controller='root', action='history' )
@@ -187,7 +181,6 @@
# add as a non-ATOM API call to support the notion of a 'current/working' history unique to the history resource
webapp.mapper.connect( "set_as_current", "/api/histories/{id}/set_as_current",
controller="histories", action="set_as_current", conditions=dict( method=["POST"] ) )
- webapp.mapper.connect( "set_as_current", "/api/histories/{id}/set_as_current", controller="histories", action="set_as_current", conditions=dict( method=["POST"] ) )
webapp.mapper.connect( "create_api_key", "/api/users/:user_id/api_key",
controller="users", action="api_key", user_id=None, conditions=dict( method=["POST"] ) )
@@ -195,7 +188,6 @@
# visualizations registry generic template renderer
webapp.add_route( '/visualization/show/:visualization_name',
controller='visualization', action='render', visualization_name=None )
- webapp.add_route( '/visualization/show/:visualization_name', controller='visualization', action='render', visualization_name=None )
# "POST /api/workflows/import" => ``workflows.import_workflow()``.
# Defines a named route "import_workflow".
https://bitbucket.org/galaxy/galaxy-central/commits/2fb8dfdb5b19/
Changeset: 2fb8dfdb5b19
User: martenson
Date: 2013-12-11 16:53:42
Summary: aiding merg problems with data_library branch
Affected #: 1 file
diff -r 4506583cb1bc66db01d98c1619b6829fcda31e00 -r 2fb8dfdb5b19378a5bc61ee713aa4da3f9767a91 lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -241,6 +241,11 @@
galaxy.model.mapping.metadata.engine.connection_provider._pool.dispose()
except:
pass
+ # Close any pooled database connections before forking
+ try:
+ galaxy.model.tool_shed_install.mapping.metadata.engine.connection_provider._pool.dispose()
+ except:
+ pass
# Return
return webapp
https://bitbucket.org/galaxy/galaxy-central/commits/78c39d32ac09/
Changeset: 78c39d32ac09
User: martenson
Date: 2013-12-11 17:01:45
Summary: aiding merg problems with data_library branch
Affected #: 1 file
diff -r 2fb8dfdb5b19378a5bc61ee713aa4da3f9767a91 -r 78c39d32ac09a796966cc192636f4f8b1ba1bc07 templates/base/base_panels.mako
--- a/templates/base/base_panels.mako
+++ b/templates/base/base_panels.mako
@@ -357,4 +357,4 @@
## Scripts can be loaded later since they progressively add features to
## the panels, but do not change layout
${self.late_javascripts()}
-</html>
\ No newline at end of file
+</html>
https://bitbucket.org/galaxy/galaxy-central/commits/8528640574a5/
Changeset: 8528640574a5
User: martenson
Date: 2013-12-11 17:08:59
Summary: aiding merg problems with data_library branch
Affected #: 1 file
diff -r 78c39d32ac09a796966cc192636f4f8b1ba1bc07 -r 8528640574a5b981ffb0940477abb0bd65ea2885 static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -217,11 +217,12 @@
return '<div class="modal">' +
'<div class="modal-backdrop fade in" style="z-index: -1;"></div>' +
'<div class="modal-dialog">' +
+ '<div class="modal-content">' +
'<div class="modal-header">' +
'<button type="button" class="close" style="display: none;">×</button>' +
'<h4 class="title">' + title + '</h4>' +
'<span class="notification-modal"></span>' +
- '</div>' +
+ '</div>' +
'<div class="modal-body"></div>' +
'<div class="modal-footer">' +
'<div class="buttons" style="float: right;"></div>' +
https://bitbucket.org/galaxy/galaxy-central/commits/181b293ea659/
Changeset: 181b293ea659
User: martenson
Date: 2013-12-11 17:19:42
Summary: Merge with second head of ‘default’ branch
Affected #: 17 files
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -1827,7 +1827,7 @@
class Library( object, Dictifiable ):
permitted_actions = get_permitted_actions( filter='LIBRARY' )
dict_collection_visible_keys = ( 'id', 'name' )
- dict_element_visible_keys = ( 'id', 'deleted', 'name', 'description', 'synopsis' )
+ dict_element_visible_keys = ( 'id', 'deleted', 'name', 'description', 'synopsis', 'root_folder_id' )
def __init__( self, name=None, description=None, synopsis=None, root_folder=None ):
self.name = name or "Unnamed library"
self.description = description
@@ -1894,7 +1894,7 @@
return name
class LibraryFolder( object, Dictifiable ):
- dict_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build' )
+ dict_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build', 'update_time' )
def __init__( self, name=None, description=None, item_count=0, order_id=None ):
self.name = name or "Unnamed folder"
self.description = description
@@ -2060,6 +2060,7 @@
genome_build = ldda.dbkey,
misc_info = ldda.info,
misc_blurb = ldda.blurb,
+ peek = ( lambda ldda: ldda.display_peek() if ldda.peek and ldda.peek != 'no peek' else None )( ldda ),
template_data = template_data )
if ldda.dataset.uuid is None:
rval['uuid'] = None
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -1878,8 +1878,9 @@
table = self.table
trans = conn.begin()
try:
- next_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
- table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid + 1 ) )
+ current_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
+ next_hid = current_hid + 1
+ table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid ) )
trans.commit()
return next_hid
except:
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 lib/galaxy/webapps/galaxy/api/datasets.py
--- a/lib/galaxy/webapps/galaxy/api/datasets.py
+++ b/lib/galaxy/webapps/galaxy/api/datasets.py
@@ -1,5 +1,5 @@
"""
-API operations on the contents of a dataset.
+API operations on the contents of a history dataset.
"""
from galaxy import web
from galaxy.visualization.data_providers.genome import FeatureLocationIndexDataProvider
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 lib/galaxy/webapps/galaxy/api/folder_contents.py
--- a/lib/galaxy/webapps/galaxy/api/folder_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/folder_contents.py
@@ -1,5 +1,5 @@
"""
-API operations on the contents of a library.
+API operations on the contents of a folder.
"""
import logging, os, string, shutil, urllib, re, socket
from cgi import escape, FieldStorage
@@ -11,67 +11,122 @@
log = logging.getLogger( __name__ )
class FolderContentsController( BaseAPIController, UsesLibraryMixin, UsesLibraryMixinItems ):
+ """
+ Class controls retrieval, creation and updating of folder contents.
+ """
+
+ def load_folder_contents( self, trans, folder ):
+ """
+ Loads all contents of the folder (folders and data sets) but only in the first level.
+ """
+ current_user_roles = trans.get_current_user_roles()
+ is_admin = trans.user_is_admin()
+ content_items = []
+ for subfolder in folder.active_folders:
+ if not is_admin:
+ can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder )
+ if (is_admin or can_access) and not subfolder.deleted:
+ subfolder.api_type = 'folder'
+ content_items.append( subfolder )
+ for dataset in folder.datasets:
+ if not is_admin:
+ can_access = trans.app.security_agent.can_access_dataset( current_user_roles, dataset.library_dataset_dataset_association.dataset )
+ if (is_admin or can_access) and not dataset.deleted:
+ dataset.api_type = 'file'
+ content_items.append( dataset )
+ return content_items
@web.expose_api
def index( self, trans, folder_id, **kwd ):
"""
GET /api/folders/{encoded_folder_id}/contents
Displays a collection (list) of a folder's contents (files and folders).
- The /api/library_contents/{encoded_library_id}/contents
- lists everything in a library recursively, which is not what
- we want here. We could add a parameter to use the recursive
- style, but this is meant to act similar to an "ls" directory listing.
+ Encoded folder ID is prepended with 'F' if it is a folder as opposed to a data set which does not have it.
+ Full path is provided as a separate object in response providing data for breadcrumb path building.
"""
- rval = []
+ folder_container = []
current_user_roles = trans.get_current_user_roles()
- def traverse( folder ):
- admin = trans.user_is_admin()
- rval = []
- for subfolder in folder.active_folders:
- if not admin:
- can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder )
- if (admin or can_access) and not subfolder.deleted:
- subfolder.api_type = 'folder'
- rval.append( subfolder )
- for ld in folder.datasets:
- if not admin:
- can_access = trans.app.security_agent.can_access_dataset( current_user_roles, ld.library_dataset_dataset_association.dataset )
- if (admin or can_access) and not ld.deleted:
- ld.api_type = 'file'
- rval.append( ld )
- return rval
-
- try:
- decoded_folder_id = trans.security.decode_id( folder_id[-16:] )
- except TypeError:
- trans.response.status = 400
- return "Malformed folder id ( %s ) specified, unable to decode." % str( folder_id )
+ if ( folder_id.startswith( 'F' ) ):
+ try:
+ decoded_folder_id = trans.security.decode_id( folder_id[1:] )
+ except TypeError:
+ trans.response.status = 400
+ return "Malformed folder id ( %s ) specified, unable to decode." % str( folder_id )
try:
folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( decoded_folder_id )
- parent_library = folder.parent_library
except:
folder = None
- log.error( "FolderContentsController.index: Unable to retrieve folder %s"
- % folder_id )
+ log.error( "FolderContentsController.index: Unable to retrieve folder with ID: %s" % folder_id )
- # TODO: Find the API's path to this folder if necessary.
- # This was needed in recursive descent, but it's not needed
- # for "ls"-style content checking:
- if not folder or not ( trans.user_is_admin() or trans.app.security_agent.can_access_library_item( current_user_roles, folder, trans.user ) ):
+ # We didn't find the folder or user does not have an access to it.
+ if not folder:
trans.response.status = 400
return "Invalid folder id ( %s ) specified." % str( folder_id )
+
+ if not ( trans.user_is_admin() or trans.app.security_agent.can_access_library_item( current_user_roles, folder, trans.user ) ):
+ log.warning( "SECURITY: User (id: %s) without proper access rights is trying to load folder with ID of %s" % ( trans.user.id, folder.id ) )
+ trans.response.status = 400
+ return "Invalid folder id ( %s ) specified." % str( folder_id )
+
+ path_to_root = []
+ def build_path ( folder ):
+ """
+ Search the path upwards recursively and load the whole route of names and ids for breadcrumb purposes.
+ """
+ path_to_root = []
+ # We are almost in root
+ if folder.parent_id is None:
+ path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
+ else:
+ # We add the current folder and traverse up one folder.
+ path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
+ upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_id )
+ path_to_root.extend( build_path( upper_folder ) )
+ return path_to_root
+
+ # Return the reversed path so it starts with the library node.
+ full_path = build_path( folder )[::-1]
+ folder_container.append( dict( full_path = full_path ) )
+
+ folder_contents = []
+ time_updated = ''
+ time_created = ''
+ # Go through every item in the folder and include its meta-data.
+ for content_item in self.load_folder_contents( trans, folder ):
+# rval = content_item.to_dict()
+ return_item = {}
+ encoded_id = trans.security.encode_id( content_item.id )
+ time_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
+ time_created = content_item.create_time.strftime( "%Y-%m-%d %I:%M %p" )
+
+ # For folder return also hierarchy values
+ if content_item.api_type == 'folder':
+ encoded_id = 'F' + encoded_id
+# time_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
+ return_item.update ( dict ( item_count = content_item.item_count ) )
- for content in traverse( folder ):
- encoded_id = trans.security.encode_id( content.id )
- if content.api_type == 'folder':
- encoded_id = 'F' + encoded_id
- rval.append( dict( id = encoded_id,
- type = content.api_type,
- name = content.name,
- url = url_for( 'folder_contents', folder_id=encoded_id ) ) )
- return rval
+ if content_item.api_type == 'file':
+ library_dataset_dict = content_item.to_dict()
+ library_dataset_dict['data_type']
+ library_dataset_dict['file_size']
+ library_dataset_dict['date_uploaded']
+ return_item.update ( dict ( data_type = library_dataset_dict['data_type'],
+ file_size = library_dataset_dict['file_size'],
+ date_uploaded = library_dataset_dict['date_uploaded'] ) )
+
+ # For every item return also the default meta-data
+ return_item.update( dict( id = encoded_id,
+ type = content_item.api_type,
+ name = content_item.name,
+ time_updated = time_updated,
+ time_created = time_created
+ ) )
+ folder_contents.append( return_item )
+ # Put the data in the container
+ folder_container.append( dict( folder_contents = folder_contents ) )
+ return folder_container
@web.expose_api
def show( self, trans, id, library_id, **kwd ):
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 lib/galaxy/webapps/galaxy/api/lda_datasets.py
--- /dev/null
+++ b/lib/galaxy/webapps/galaxy/api/lda_datasets.py
@@ -0,0 +1,241 @@
+"""
+API operations on the dataset from library.
+"""
+import glob
+import logging
+import operator
+import os
+import os.path
+import string
+import sys
+import tarfile
+import tempfile
+import urllib
+import urllib2
+import zipfile
+from galaxy.security import Action
+from galaxy import util, web
+from galaxy.util.streamball import StreamBall
+from galaxy.web.base.controller import BaseAPIController, UsesLibraryMixinItems
+
+import logging
+log = logging.getLogger( __name__ )
+
+# Test for available compression types
+# tmpd = tempfile.mkdtemp()
+# comptypes = []
+# for comptype in ( 'gz', 'bz2' ):
+# tmpf = os.path.join( tmpd, 'compression_test.tar.' + comptype )
+# try:
+# archive = tarfile.open( tmpf, 'w:' + comptype )
+# archive.close()
+# comptypes.append( comptype )
+# except tarfile.CompressionError:
+# log.exception( "Compression error when testing %s compression. This option will be disabled for library downloads." % comptype )
+# try:
+# os.unlink( tmpf )
+# except OSError:
+# pass
+ziptype = '32'
+# tmpf = os.path.join( tmpd, 'compression_test.zip' )
+# try:
+# archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True )
+# archive.close()
+# comptypes.append( 'zip' )
+# ziptype = '64'
+# except RuntimeError:
+# log.exception( "Compression error when testing zip compression. This option will be disabled for library downloads." )
+# except (TypeError, zipfile.LargeZipFile):
+# # ZIP64 is only in Python2.5+. Remove TypeError when 2.4 support is dropped
+# log.warning( 'Max zip file size is 2GB, ZIP64 not supported' )
+# comptypes.append( 'zip' )
+# try:
+# os.unlink( tmpf )
+# except OSError:
+# pass
+# os.rmdir( tmpd )
+
+
+
+class DatasetsController( BaseAPIController, UsesLibraryMixinItems ):
+
+ @web.expose_api
+ def show( self, trans, id, **kwd ):
+ """
+ GET /api/libraries/datasets/{encoded_dataset_id}
+ Displays information about the dataset identified by the lda ID.
+ """
+ # Get dataset.
+ try:
+ dataset = self.get_library_dataset( trans, id = id )
+ except Exception, e:
+ return str( e )
+ try:
+ # Default: return dataset as dict.
+ rval = dataset.to_dict()
+ except Exception, e:
+ rval = "Error in dataset API at listing contents: " + str( e )
+ log.error( rval + ": %s" % str(e), exc_info=True )
+ trans.response.status = 500
+
+ rval['id'] = trans.security.encode_id(rval['id']);
+ rval['ldda_id'] = trans.security.encode_id(rval['ldda_id']);
+ rval['folder_id'] = 'f' + trans.security.encode_id(rval['folder_id'])
+
+ return rval
+
+ @web.expose
+ def download( self, trans, format, **kwd ):
+ """
+ POST /api/libraries/datasets/download/{format}
+ POST data: ldda_ids = []
+ Downloads dataset(s) in the requested format.
+ """
+ lddas = []
+# is_admin = trans.user_is_admin()
+# current_user_roles = trans.get_current_user_roles()
+
+ datasets_to_download = kwd['ldda_ids%5B%5D']
+
+ if ( datasets_to_download != None ):
+ datasets_to_download = util.listify( datasets_to_download )
+ for dataset_id in datasets_to_download:
+ try:
+ ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( dataset_id ) )
+ assert not ldda.dataset.purged
+ lddas.append( ldda )
+ except:
+ ldda = None
+ message += "Invalid library dataset id (%s) specified. " % str( dataset_id )
+
+ if format in [ 'zip','tgz','tbz' ]:
+ error = False
+ killme = string.punctuation + string.whitespace
+ trantab = string.maketrans(killme,'_'*len(killme))
+ try:
+ outext = 'zip'
+ if format == 'zip':
+ # Can't use mkstemp - the file must not exist first
+ tmpd = tempfile.mkdtemp()
+ util.umask_fix_perms( tmpd, trans.app.config.umask, 0777, self.app.config.gid )
+ tmpf = os.path.join( tmpd, 'library_download.' + format )
+ if ziptype == '64' and trans.app.config.upstream_gzip:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED, True )
+ elif ziptype == '64':
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True )
+ elif trans.app.config.upstream_gzip:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED )
+ else:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED )
+ archive.add = lambda x, y: archive.write( x, y.encode('CP437') )
+ elif format == 'tgz':
+ if trans.app.config.upstream_gzip:
+ archive = StreamBall( 'w|' )
+ outext = 'tar'
+ else:
+ archive = StreamBall( 'w|gz' )
+ outext = 'tgz'
+ elif format == 'tbz':
+ archive = StreamBall( 'w|bz2' )
+ outext = 'tbz2'
+ except ( OSError, zipfile.BadZipfile ):
+ error = True
+ log.exception( "Unable to create archive for download" )
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ except:
+ error = True
+ log.exception( "Unexpected error %s in create archive for download" % sys.exc_info()[0] )
+ message = "Unable to create archive for download, please report - %s" % sys.exc_info()[0]
+ status = 'error'
+ if not error:
+ composite_extensions = trans.app.datatypes_registry.get_composite_extensions()
+ seen = []
+ for ldda in lddas:
+ if ldda.dataset.state in [ 'new', 'upload', 'queued', 'running', 'empty', 'discarded' ]:
+ continue
+ ext = ldda.extension
+ is_composite = ext in composite_extensions
+ path = ""
+ parent_folder = ldda.library_dataset.folder
+ while parent_folder is not None:
+ # Exclude the now-hidden "root folder"
+ if parent_folder.parent is None:
+ path = os.path.join( parent_folder.library_root[0].name, path )
+ break
+ path = os.path.join( parent_folder.name, path )
+ parent_folder = parent_folder.parent
+ path += ldda.name
+ while path in seen:
+ path += '_'
+ seen.append( path )
+ zpath = os.path.split(path)[-1] # comes as base_name/fname
+ outfname,zpathext = os.path.splitext(zpath)
+ if is_composite:
+ # need to add all the components from the extra_files_path to the zip
+ if zpathext == '':
+ zpath = '%s.html' % zpath # fake the real nature of the html file
+ try:
+ archive.add(ldda.dataset.file_name,zpath) # add the primary of a composite set
+ except IOError:
+ error = True
+ log.exception( "Unable to add composite parent %s to temporary library download archive" % ldda.dataset.file_name)
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ continue
+ flist = glob.glob(os.path.join(ldda.dataset.extra_files_path,'*.*')) # glob returns full paths
+ for fpath in flist:
+ efp,fname = os.path.split(fpath)
+ if fname > '':
+ fname = fname.translate(trantab)
+ try:
+ archive.add( fpath,fname )
+ except IOError:
+ error = True
+ log.exception( "Unable to add %s to temporary library download archive %s" % (fname,outfname))
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ continue
+ else: # simple case
+ try:
+ archive.add( ldda.dataset.file_name, path )
+ except IOError:
+ error = True
+ log.exception( "Unable to write %s to temporary library download archive" % ldda.dataset.file_name)
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ if not error:
+ lname = 'selected_dataset'
+ fname = lname.replace( ' ', '_' ) + '_files'
+ if format == 'zip':
+ archive.close()
+ trans.response.set_content_type( "application/octet-stream" )
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
+ archive = util.streamball.ZipBall(tmpf, tmpd)
+ archive.wsgi_status = trans.response.wsgi_status()
+ archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ return archive.stream
+ else:
+ trans.response.set_content_type( "application/x-tar" )
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
+ archive.wsgi_status = trans.response.wsgi_status()
+ archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ return archive.stream
+ elif format == 'uncompressed':
+ if len(lddas) != 1:
+ return 'Wrong request'
+ else:
+ single_dataset = lddas[0]
+ trans.response.set_content_type( single_dataset.get_mime() )
+ fStat = os.stat( ldda.file_name )
+ trans.response.headers[ 'Content-Length' ] = int( fStat.st_size )
+ valid_chars = '.,^_-()[]0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ fname = ldda.name
+ fname = ''.join( c in valid_chars and c or '_' for c in fname )[ 0:150 ]
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s"' % fname
+ try:
+ return open( single_dataset.file_name )
+ except:
+ return 'This dataset contains no content'
+ else:
+ return 'Wrong format';
\ No newline at end of file
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 lib/galaxy/webapps/galaxy/api/libraries.py
--- a/lib/galaxy/webapps/galaxy/api/libraries.py
+++ b/lib/galaxy/webapps/galaxy/api/libraries.py
@@ -49,9 +49,10 @@
trans.model.Library.table.c.id.in_( accessible_restricted_library_ids ) ) )
rval = []
for library in query:
- item = library.to_dict()
+ item = library.to_dict( view='element' )
item['url'] = url_for( route, id=trans.security.encode_id( library.id ) )
- item['id'] = trans.security.encode_id( item['id'] )
+ item['id'] = 'F' + trans.security.encode_id( item['id'] )
+ item['root_folder_id'] = 'F' + trans.security.encode_id( item['root_folder_id'] )
rval.append( item )
return rval
@@ -131,6 +132,9 @@
rval['name'] = name
rval['id'] = encoded_id
return rval
+
+ def edit( self, trans, payload, **kwd ):
+ return "Not implemented yet"
@web.expose_api
def delete( self, trans, id, **kwd ):
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -46,12 +46,6 @@
atexit.register( app.shutdown )
# Create the universe WSGI application
webapp = GalaxyWebApplication( app, session_cookie='galaxysession', name='galaxy' )
- # Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
- webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
- controller='admin_toolshed',
- action='display_image_in_repository',
- repository_id=None,
- image_file=None )
webapp.add_ui_controllers( 'galaxy.webapps.galaxy.controllers', app )
# Force /history to go to /root/history -- needed since the tests assume this
webapp.add_route( '/history', controller='root', action='history' )
@@ -75,22 +69,12 @@
webapp.add_route( '/u/:username/v/:slug', controller='visualization', action='display_by_username_and_slug' )
webapp.add_route( '/search', controller='search', action='index' )
- # Add the web API
+ # ================
+ # ===== API =====
+ # ================
+
webapp.add_api_controllers( 'galaxy.webapps.galaxy.api', app )
- # The /folders section is experimental at this point:
- log.debug( "app.config.api_folders: %s" % app.config.api_folders )
- webapp.mapper.resource( 'folder', 'folders', path_prefix='/api' )
- webapp.mapper.resource( 'content', 'contents',
- controller='folder_contents',
- name_prefix='folder_',
- path_prefix='/api/folders/:folder_id',
- parent_resources=dict( member_name='folder', collection_name='folders' ) )
- webapp.mapper.resource( 'content',
- 'contents',
- controller='library_contents',
- name_prefix='library_',
- path_prefix='/api/libraries/:library_id',
- parent_resources=dict( member_name='library', collection_name='libraries' ) )
+
webapp.mapper.resource( 'content',
'contents',
controller='history_contents',
@@ -102,10 +86,6 @@
controller="datasets",
action="display",
conditions=dict(method=["GET"]))
- webapp.mapper.resource( 'permission',
- 'permissions',
- path_prefix='/api/libraries/:library_id',
- parent_resources=dict( member_name='library', collection_name='libraries' ) )
webapp.mapper.resource( 'user',
'users',
controller='group_users',
@@ -127,11 +107,6 @@
_add_item_tags_controller( webapp,
name_prefix="workflow_",
path_prefix='/api/workflows/:workflow_id' )
-
- _add_item_extended_metadata_controller( webapp,
- name_prefix="library_dataset_",
- path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
-
_add_item_annotation_controller( webapp,
name_prefix="history_content_",
path_prefix='/api/histories/:history_id/contents/:history_content_id' )
@@ -141,7 +116,6 @@
_add_item_annotation_controller( webapp,
name_prefix="workflow_",
path_prefix='/api/workflows/:workflow_id' )
-
_add_item_provenance_controller( webapp,
name_prefix="history_content_",
path_prefix='/api/histories/:history_id/contents/:history_content_id' )
@@ -171,6 +145,39 @@
#webapp.mapper.connect( 'run_workflow', '/api/workflow/{workflow_id}/library/{library_id}', controller='workflows', action='run', workflow_id=None, library_id=None, conditions=dict(method=["GET"]) )
webapp.mapper.resource( 'search', 'search', path_prefix='/api' )
+ # =======================
+ # ===== LIBRARY API =====
+ # =======================
+
+ # The /folders section is experimental at this point:
+ log.debug( "app.config.api_folders: %s" % app.config.api_folders )
+
+ webapp.mapper.connect( 'show_lda_item', '/api/libraries/datasets/:id', controller='lda_datasets', action='show', conditions=dict(method=["GET"]) )
+ webapp.mapper.connect( 'download_lda_items', '/api/libraries/datasets/download/:format', controller='lda_datasets', action='download', conditions=dict(method=["POST"]) )
+
+ webapp.mapper.resource_with_deleted( 'library', 'libraries', path_prefix='/api' )
+ webapp.mapper.resource( 'folder', 'folders', path_prefix='/api' )
+
+ webapp.mapper.resource( 'content', 'contents',
+ controller='folder_contents',
+ name_prefix='folder_',
+ path_prefix='/api/folders/:folder_id',
+ parent_resources=dict( member_name='folder', collection_name='folders' ) )
+ webapp.mapper.resource( 'content',
+ 'contents',
+ controller='library_contents',
+ name_prefix='library_',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
+ webapp.mapper.resource( 'permission',
+ 'permissions',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
+ _add_item_extended_metadata_controller( webapp,
+ name_prefix="library_dataset_",
+ path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
+
+
# add as a non-ATOM API call to support the notion of a 'current/working' history unique to the history resource
webapp.mapper.connect( "set_as_current", "/api/histories/{id}/set_as_current",
controller="histories", action="set_as_current", conditions=dict( method=["POST"] ) )
@@ -188,6 +195,18 @@
webapp.mapper.connect("workflow_dict", '/api/workflows/{workflow_id}/download', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
# Preserve the following download route for now for dependent applications -- deprecate at some point
webapp.mapper.connect("workflow_dict", '/api/workflows/download/{workflow_id}', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
+
+ # ====================
+ # ===== TOOLSHED =====
+ # ====================
+
+ # Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
+ webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
+ controller='admin_toolshed',
+ action='display_image_in_repository',
+ repository_id=None,
+ image_file=None )
+
# Galaxy API for tool shed features.
webapp.mapper.resource( 'tool_shed_repository',
'tool_shed_repositories',
@@ -201,6 +220,7 @@
path_prefix='/api',
new={ 'install_repository_revision' : 'POST' },
parent_resources=dict( member_name='tool_shed_repository', collection_name='tool_shed_repositories' ) )
+
# Connect logger from app
if app.trace_logger:
webapp.trace_logger = app.trace_logger
@@ -221,7 +241,7 @@
galaxy.model.mapping.metadata.engine.connection_provider._pool.dispose()
except:
pass
- # Close any pooled database connections before forking
+ # Close any pooled database connections before forking
try:
galaxy.model.tool_shed_install.mapping.metadata.engine.connection_provider._pool.dispose()
except:
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 lib/galaxy/webapps/galaxy/controllers/library.py
--- a/lib/galaxy/webapps/galaxy/controllers/library.py
+++ b/lib/galaxy/webapps/galaxy/controllers/library.py
@@ -76,6 +76,17 @@
library_list_grid = LibraryListGrid()
+
+ @web.expose
+ def list( self, trans, **kwd ):
+ params = util.Params( kwd )
+ # define app configuration for generic mako template
+ app = {
+ 'jscript' : "galaxy.library"
+ }
+ # fill template
+ return trans.fill_template('galaxy.panels.mako', config = {'app' : app})
+
@web.expose
def index( self, trans, **kwd ):
params = util.Params( kwd )
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 static/scripts/galaxy.library.js
--- /dev/null
+++ b/static/scripts/galaxy.library.js
@@ -0,0 +1,860 @@
+// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+// === GALAXY LIBRARY MODULE ====
+// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+
+// global variables
+var view = null;
+var library_router = null;
+var responses = [];
+
+
+// load required libraries
+// require([
+// // load js libraries
+// 'utils/galaxy.css',
+// ], function(css){
+// // load css
+// css.load_file("static/style/library.css");
+// });
+
+// dependencies
+define(["galaxy.modal", "galaxy.masthead", "utils/galaxy.utils"], function(mod_modal, mod_masthead, mod_utils) {
+
+// MMMMMMMMMMMMMMM
+// === Models ====
+// MMMMMMMMMMMMMMM
+
+ // LIBRARY
+ var Library = Backbone.Model.extend({
+ urlRoot: '/api/libraries'
+ });
+
+ // LIBRARIES
+ var Libraries = Backbone.Collection.extend({
+ url: '/api/libraries',
+ model: Library
+ });
+
+ // ITEM
+ var Item = Backbone.Model.extend({
+ urlRoot : '/api/libraries/datasets'
+ })
+
+ // FOLDER
+ var Folder = Backbone.Collection.extend({
+ model: Item
+ })
+
+ // CONTAINER for folder contents (folders, items and metadata).
+ var FolderContainer = Backbone.Model.extend({
+ defaults : {
+ folder : new Folder(),
+ full_path : "unknown",
+ urlRoot : "/api/folders/",
+ id : "unknown"
+ },
+ parse : function(obj) {
+ this.full_path = obj[0].full_path;
+ // update the inner collection
+ this.get("folder").reset(obj[1].folder_contents);
+ return obj;
+ }
+ })
+
+ // HISTORY ITEM
+ var HistoryItem = Backbone.Model.extend({
+ urlRoot : '/api/histories/'
+ });
+
+ // HISTORY
+ var GalaxyHistory = Backbone.Model.extend({
+ url : '/api/histories/'
+ });
+
+ // HISTORIES
+ var GalaxyHistories = Backbone.Collection.extend({
+ url : '/api/histories',
+ model : GalaxyHistory
+ });
+
+ //ROUTER
+ var LibraryRouter = Backbone.Router.extend({
+ routes: {
+ "" : "libraries",
+ "folders/:id" : "folder_content",
+ "folders/:folder_id/download/:format" : "download"
+ }
+ });
+
+
+// MMMMMMMMMMMMMM
+// === VIEWS ====
+// MMMMMMMMMMMMMM
+
+// galaxy folder
+var FolderContentView = Backbone.View.extend({
+ // main element definition
+ el : '#center',
+ // progress percentage
+ progress: 0,
+ // progress rate per one item
+ progressStep: 1,
+ // last selected history in modal for UX
+ lastSelectedHistory: '',
+ // self modal
+ modal : null,
+ // loaded folders
+ folders : null,
+
+ // initialize
+ initialize : function(){
+ this.folders = [];
+ this.queue = jQuery.Deferred();
+ this.queue.resolve();
+ },
+
+// MMMMMMMMMMMMMMMMMM
+// === TEMPLATES ====
+// MMMMMMMMMMMMMMMMMM
+
+ // set up
+ templateFolder : function (){
+ var tmpl_array = [];
+
+ // CONTAINER
+ tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; ">');
+ tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');
+
+ // TOOLBAR
+ tmpl_array.push('<div id="library_folder_toolbar" >');
+ tmpl_array.push(' <button title="Create New Folder" id="toolbtn_create_folder" class="btn btn-primary" type="button"><span class="fa fa-icon-plus"></span><span class="fa fa-icon-folder-close"></span> folder</button>');
+ tmpl_array.push(' <button id="toolbtn_bulk_import" class="btn btn-primary" style="display: none; margin-left: 0.5em;" type="button"><span class="fa fa-icon-external-link"></span> to history</button>');
+
+ tmpl_array.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; display: none; ">');
+ tmpl_array.push(' <button id="drop_toggle" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">');
+ tmpl_array.push(' <span class="fa fa-icon-download"></span> download <span class="caret"></span>');
+ tmpl_array.push(' </button>');
+ tmpl_array.push(' <ul class="dropdown-menu" role="menu">');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');
+ tmpl_array.push(' </ul>');
+ tmpl_array.push(' </div>');
+
+ tmpl_array.push('</div>');
+
+ // BREADCRUMBS
+ tmpl_array.push('<div class="library_breadcrumb">');
+ tmpl_array.push('<a title="Return to the list of libraries" href="#">Libraries</a><b>|</b> ');
+ tmpl_array.push('<% _.each(path, function(path_item) { %>');
+ tmpl_array.push('<% if (path_item[0] != id) { %>');
+ tmpl_array.push('<a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a><b>|</b> ');
+ tmpl_array.push('<% } else { %>');
+ tmpl_array.push('<span title="You are in this folder"><%- path_item[1] %></span>');
+ tmpl_array.push('<% } %>');
+ tmpl_array.push('<% }); %>');
+ tmpl_array.push('</div>');
+
+ // FOLDER CONTENT
+ tmpl_array.push('<table id="folder_table" class="table table-condensed">');
+ tmpl_array.push(' <thead>');
+ tmpl_array.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');
+ tmpl_array.push(' <th class="button_heading">view</th>');
+ tmpl_array.push(' <th>name</th>');
+ tmpl_array.push(' <th>data type</th>');
+ tmpl_array.push(' <th>size</th>');
+ tmpl_array.push(' <th>date</th>');
+ tmpl_array.push(' </thead>');
+ tmpl_array.push(' <tbody>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td><button title="Go to parent folder" type="button" data-id="<%- upper_folder_id %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-arrow-up"></span> .. go up</td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% _.each(items, function(content_item) { %>');
+ tmpl_array.push(' <tr class="folder_row light" id="<%- content_item.id %>">');
+ tmpl_array.push(' <% if (content_item.get("type") === "folder") { %>'); // folder
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td><button title="Open this folder" type="button" data-id="<%- content_item.id %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-folder-open"></span> browse</td>');
+ tmpl_array.push(' <td><%- content_item.get("name") %>');
+ tmpl_array.push(' <% if (content_item.get("item_count") === 0) { %>'); // empty folder
+ tmpl_array.push(' <span class="muted">(empty folder)</span>');
+ tmpl_array.push(' <% } %>');
+ tmpl_array.push(' </td>');
+ tmpl_array.push(' <td>folder</td>'); // data type
+ tmpl_array.push(' <td><%= _.escape(content_item.get("item_count")) %> item(s)</td>'); // size
+ tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');
+ tmpl_array.push(' <td>');
+ tmpl_array.push(' <button title="See details of this dataset" type="button" class="library-dataset btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-eye-open"></span> details');
+ tmpl_array.push(' </button>');
+ tmpl_array.push(' </td>');
+ tmpl_array.push(' <td><%- content_item.get("name") %></td>'); // dataset
+ tmpl_array.push(' <td><%= _.escape(content_item.get("data_type")) %></td>'); // data type
+ tmpl_array.push(' <td><%= _.escape(content_item.get("readable_size")) %></td>'); // size
+ tmpl_array.push(' <% } %> ');
+ tmpl_array.push(' <td><%= _.escape(content_item.get("time_updated")) %></td>'); // time updated
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push(' ');
+ tmpl_array.push(' </tbody>');
+ tmpl_array.push('</table>');
+
+ tmpl_array.push('</div>');
+ return tmpl_array.join('');
+ },
+ templateDatasetModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<div id="dataset_info_modal">');
+ tmpl_array.push(' <table class="table table-striped table-condensed">');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row" id="id_row" data-id="<%= _.escape(item.get("ldda_id")) %>">Name</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("name")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Data type</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("data_type")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Genome build</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("genome_build")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <th scope="row">Size</th>');
+ tmpl_array.push(' <td><%= _.escape(size) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Date uploaded</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("date_uploaded")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Uploaded by</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("uploaded_by")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr scope="row">');
+ tmpl_array.push(' <th scope="row">Data Lines</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_data_lines")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <th scope="row">Comment Lines</th>');
+ tmpl_array.push(' <% if (item.get("metadata_comment_lines") === "") { %>'); //folder
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_comment_lines")) %></td>');
+ tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td scope="row">unknown</td>');
+ tmpl_array.push(' <% } %>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Number of Columns</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_columns")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Column Types</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_column_types")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Miscellaneous information</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("misc_blurb")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' </table>');
+ tmpl_array.push(' <pre class="peek">');
+ tmpl_array.push(' </pre>');
+ tmpl_array.push('</div>');
+
+ return tmpl_array.join('');
+ },
+
+ templateHistorySelectInModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<span id="history_modal_combo" style="width:90%; margin-left: 1em; margin-right: 1em; ">');
+ tmpl_array.push('Select history: ');
+ tmpl_array.push('<select id="dataset_import_single" name="dataset_import_single" style="width:50%; margin-bottom: 1em; "> ');
+ tmpl_array.push(' <% _.each(histories, function(history) { %>'); //history select box
+ tmpl_array.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push('</select>');
+ tmpl_array.push('</span>');
+
+ return tmpl_array.join('');
+ },
+
+ templateBulkImportInModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');
+ tmpl_array.push('Select history: ');
+ tmpl_array.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');
+ tmpl_array.push(' <% _.each(histories, function(history) { %>'); //history select box
+ tmpl_array.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push('</select>');
+ tmpl_array.push('</span>');
+
+ return tmpl_array.join('');
+ },
+
+ // convert size to nice string
+ size_to_string : function (size)
+ {
+ // identify unit
+ var unit = "";
+ if (size >= 100000000000) { size = size / 100000000000; unit = "TB"; } else
+ if (size >= 100000000) { size = size / 100000000; unit = "GB"; } else
+ if (size >= 100000) { size = size / 100000; unit = "MB"; } else
+ if (size >= 100) { size = size / 100; unit = "KB"; } else
+ { size = size * 10; unit = "b"; }
+ // return formatted string
+ return (Math.round(size) / 10) + unit;
+ },
+
+// MMMMMMMMMMMMMMM
+// === EVENTS ====
+// MMMMMMMMMMMMMMM
+
+ // event binding
+ events: {
+ 'click #select-all-checkboxes' : 'selectAll',
+ 'click .folder_row' : 'selectClicked',
+ 'click #toolbtn_bulk_import' : 'modalBulkImport',
+ 'click #toolbtn_dl' : 'bulkDownload',
+ 'click .library-dataset' : 'showDatasetDetails',
+ 'click #toolbtn_create_folder' : 'createFolderModal',
+ 'click .btn_open_folder' : 'navigateToFolder'
+ },
+
+ //render the folder view
+ render: function (options) {
+ //hack to show scrollbars
+ $("#center").css('overflow','auto');
+
+ view = this;
+ var that = this;
+
+ var folderContainer = new FolderContainer({id: options.id});
+ folderContainer.url = folderContainer.attributes.urlRoot + options.id + '/contents';
+
+ folderContainer.fetch({
+ success: function (container) {
+
+ // prepare nice size strings
+ for (var i = 0; i < folderContainer.attributes.folder.models.length; i++) {
+ var model = folderContainer.attributes.folder.models[i]
+ if (model.get('type') === 'file'){
+ model.set('readable_size', that.size_to_string(model.get('file_size')))
+ }
+ };
+
+ // find the upper id
+ var path = folderContainer.full_path;
+ var upper_folder_id;
+ if (path.length === 1){ // library is above us
+ upper_folder_id = 0;
+ } else {
+ upper_folder_id = path[path.length-2][0];
+ }
+
+ var template = _.template(that.templateFolder(), { path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id, upper_folder_id: upper_folder_id });
+ // var template = _.template(that.templateFolder(), { path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id });
+ that.$el.html(template);
+ }
+ })
+ },
+
+ // handles the click on 'open' and 'upper' folder icons
+ navigateToFolder : function(event){
+ var folder_id = $(event.target).attr('data-id');
+ if (typeof folder_id === 'undefined') {
+ return false;
+ } else if (folder_id === '0'){
+ library_router.navigate('#', {trigger: true, replace: true});
+ } else {
+ library_router.navigate('folders/' + folder_id, {trigger: true, replace: true});
+ }
+ },
+
+ //show modal with current dataset info
+ showDatasetDetails : function(event){
+ // prevent default
+ event.preventDefault();
+
+//TODO check whether we already have the data
+
+ //load the ID of the row
+ var id = $(event.target).parent().parent().attr('id');
+
+ //create new item
+ var item = new Item();
+ var histories = new GalaxyHistories();
+ item.id = id;
+ var self = this;
+
+ //fetch the dataset info
+ item.fetch({
+ success: function (item) {
+// TODO can render here already
+ //fetch user histories for import purposes
+ histories.fetch({
+ success: function (histories){self.renderModalAfterFetch(item, histories)}
+ });
+ }
+ });
+ },
+
+ // show the current dataset in a modal
+ renderModalAfterFetch : function(item, histories){
+ var size = this.size_to_string(item.get('file_size'));
+ var template = _.template(this.templateDatasetModal(), { item : item, size : size });
+ this.modal = null;
+ // make modal
+ var self = this;
+ this.modal = new mod_modal.GalaxyModal({
+ title : 'Dataset Details',
+ body : template,
+ buttons : {
+ 'Import' : function() { self.importCurrentIntoHistory() },
+ 'Download' : function() { self.downloadCurrent() },
+ 'Close' : function() { self.modal.hide(); $('.modal').remove(); self.modal = null; } // TODO refill nicely modal with data
+ }
+ });
+ $(".peek").html(item.get("peek"));
+ var history_footer_tmpl = _.template(this.templateHistorySelectInModal(), {histories : histories.models});
+ $(this.modal.elMain).find('.buttons').prepend(history_footer_tmpl);
+
+ // preset last selected history if we know it
+ if (self.lastSelectedHistory.length > 0) {
+ $(this.modal.elMain).find('#dataset_import_single').val(self.lastSelectedHistory);
+ }
+
+ // show the prepared modal
+ this.modal.show();
+ },
+
+ // download dataset shown currently in modal
+ downloadCurrent : function(){
+ //disable the buttons
+ this.modal.disableButton('Import');
+ this.modal.disableButton('Download');
+
+ var library_dataset_id = [];
+ library_dataset_id.push($('#id_row').attr('data-id'));
+ var url = '/api/libraries/datasets/download/uncompressed';
+ var data = {'ldda_ids' : library_dataset_id};
+
+ // we assume the view is existent
+ folderContentView.processDownload(url, data);
+ this.modal.enableButton('Import');
+ this.modal.enableButton('Download');
+ },
+
+ // import dataset shown currently in modal into selected history
+ importCurrentIntoHistory : function(){
+ //disable the buttons
+ this.modal.disableButton('Import');
+ this.modal.disableButton('Download');
+
+ var history_id = $(this.modal.elMain).find('select[name=dataset_import_single] option:selected').val();
+ this.lastSelectedHistory = history_id; //save selected history for further use
+
+ var library_dataset_id = $('#id_row').attr('data-id');
+ var historyItem = new HistoryItem();
+ var self = this;
+ historyItem.url = historyItem.urlRoot + history_id + '/contents';
+
+ // save the dataset into selected history
+ historyItem.save({ content : library_dataset_id, source : 'library' }, { success : function(){
+ self.modal.showNotification('Dataset imported', 3000, 'success');
+ //enable the buttons
+ self.modal.enableButton('Import');
+ self.modal.enableButton('Download');
+ }, error : function(){
+ self.modal.showNotification('An error occured! Dataset not imported. Please try again later.', 5000, 'error');
+ //enable the buttons
+ self.modal.enableButton('Import');
+ self.modal.enableButton('Download');
+ }
+ });
+ },
+
+ // select all datasets
+ selectAll : function (event) {
+ var selected = event.target.checked;
+ // Iterate each checkbox
+ $(':checkbox').each(function () { this.checked = selected; });
+ this.showTools();
+ },
+
+ // click checkbox on folder click
+ selectClicked : function (event) {
+ var checkbox = $("#" + event.target.parentElement.id).find(':checkbox')
+ if (checkbox[0] != undefined) {
+ if (checkbox[0].checked){
+ checkbox[0].checked = '';
+ // $(event.target.parentElement).css('background-color', '').css('color', '');
+ $(event.target.parentElement).removeClass('dark');
+ $(event.target.parentElement).find('a').removeClass('dark');
+ $(event.target.parentElement).addClass('light');
+ $(event.target.parentElement).find('a').addClass('light');
+ } else {
+ checkbox[0].checked = 'selected';
+ $(event.target.parentElement).removeClass('light');
+ $(event.target.parentElement).find('a').removeClass('light');
+ $(event.target.parentElement).addClass('dark');
+ $(event.target.parentElement).find('a').addClass('dark');
+ // $(event.target.parentElement).css('background-color', '#8389a1').css('color', 'white');
+ }
+ }
+ this.showTools();
+ },
+
+ // show toolbar in case something is selected
+ showTools : function(){
+ var checkedValues = $('#folder_table').find(':checked');
+ if(checkedValues.length > 0){
+ $('#toolbtn_bulk_import').show();
+ $('#toolbtn_dl').show();
+ } else {
+ $('#toolbtn_bulk_import').hide();
+ $('#toolbtn_dl').hide();
+ }
+
+ },
+
+ // show bulk import modal
+ modalBulkImport : function(){
+ var self = this;
+ // fetch histories
+ var histories = new GalaxyHistories();
+ histories.fetch({
+ success: function (histories){
+ // make modal
+ var history_modal_tmpl = _.template(self.templateBulkImportInModal(), {histories : histories.models});
+ self.modal = new mod_modal.GalaxyModal({
+ title : 'Import into History',
+ body : history_modal_tmpl,
+ buttons : {
+ 'Import' : function() {self.importAllIntoHistory()},
+ 'Close' : function() {self.modal.hide(); $('.modal').remove(); self.modal = null;}
+ }
+ });
+ // show the prepared modal
+ self.modal.show();
+ }
+ });
+ },
+
+ // import all selected datasets into history
+ importAllIntoHistory : function (){
+ //disable the button
+ this.modal.disableButton('Import');
+
+ var history_id = $("select[name=dataset_import_bulk] option:selected").val();
+ var history_name = $("select[name=dataset_import_bulk] option:selected").text();
+
+ var dataset_ids = [];
+ $('#folder_table').find(':checked').each(function(){
+ if (this.parentElement.parentElement.id != '') {
+ dataset_ids.push(this.parentElement.parentElement.id);
+ }
+ });
+ var progress_bar_tmpl = _.template(this.templateProgressBar(), { history_name : history_name });
+ $(this.modal.elMain).find('.modal-body').html(progress_bar_tmpl);
+
+ // init the progress bar
+ var progressStep = 100 / dataset_ids.length;
+ this.initProgress(progressStep);
+
+ // prepare the dataset objects to be imported
+ var datasets_to_import = [];
+ for (var i = dataset_ids.length - 1; i >= 0; i--) {
+ library_dataset_id = dataset_ids[i];
+ var historyItem = new HistoryItem();
+ var self = this;
+ historyItem.url = historyItem.urlRoot + history_id + '/contents';
+ historyItem.content = library_dataset_id;
+ historyItem.source = 'library';
+ datasets_to_import.push(historyItem);
+ };
+
+ // call the recursive function to call ajax one after each other
+ this.chainCall(datasets_to_import);
+ },
+
+ chainCall: function(history_item_set){
+ var self = this;
+ var popped_item = history_item_set.pop();
+ if (typeof popped_item === "undefined") {
+ self.modal.showNotification('All datasets imported', 3000, 'success');
+ // enable button again
+ self.modal.enableButton('Import');
+ return
+ }
+ var promise = $.when(popped_item.save({content: popped_item.content, source: popped_item.source})).done(function(a1){
+ self.updateProgress();
+ responses.push(a1);
+ self.chainCall(history_item_set);
+ });
+ },
+
+ initProgress: function(progressStep){
+ this.progress = 0;
+ this.progressStep = progressStep;
+ },
+ updateProgress: function(){
+ this.progress += this.progressStep;
+ $('.progress-bar').width(Math.round(this.progress) + '%');
+ txt_representation = Math.round(this.progress) + '% Complete';
+ $('.completion_span').text(txt_representation);
+ },
+
+ // progress bar
+ templateProgressBar : function (){
+ var tmpl_array = [];
+
+ tmpl_array.push('<div class="import_text">');
+ tmpl_array.push('Importing selected datasets to history <b><%= _.escape(history_name) %></b>');
+ tmpl_array.push('</div>');
+ tmpl_array.push('<div class="progress">');
+ tmpl_array.push(' <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');
+ tmpl_array.push(' <span class="completion_span">0% Complete</span>');
+ tmpl_array.push(' </div>');
+ tmpl_array.push('</div>');
+ tmpl_array.push('');
+
+ return tmpl_array.join('');
+ },
+
+ // download selected datasets
+ download : function(folder_id, format){
+ var dataset_ids = [];
+ $('#folder_table').find(':checked').each(function(){
+ if (this.parentElement.parentElement.id != '') {
+ dataset_ids.push(this.parentElement.parentElement.id);
+ }
+ });
+
+ var url = '/api/libraries/datasets/download/' + format;
+ var data = {'ldda_ids' : dataset_ids};
+ this.processDownload(url, data);
+ },
+
+ // create hidden form and submit through POST to initialize download
+ processDownload: function(url, data, method){
+ //url and data options required
+ if( url && data ){
+ //data can be string of parameters or array/object
+ data = typeof data == 'string' ? data : $.param(data);
+ //split params into form inputs
+ var inputs = '';
+ $.each(data.split('&'), function(){
+ var pair = this.split('=');
+ inputs+='<input type="hidden" name="'+ pair[0] +'" value="'+ pair[1] +'" />';
+ });
+ //send request
+ $('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
+ .appendTo('body').submit().remove();
+ };
+ },
+
+ // shows modal for creating folder
+ createFolderModal: function(){
+ alert('creating folder');
+ }
+
+ });
+
+// galaxy library view
+var GalaxyLibraryview = Backbone.View.extend({
+ el: '#center',
+
+ events: {
+ 'click #create_new_library_btn' : 'show_library_modal'
+ },
+
+ // initialize
+ initialize : function(){
+ },
+
+ // template
+ template_library_list : function (){
+ tmpl_array = [];
+ tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; overflow: auto !important; ">');
+
+ tmpl_array.push('');
+ tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');
+ tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary icon-file ">New Library</a>');
+ tmpl_array.push('<table class="table table-condensed">');
+ tmpl_array.push(' <thead>');
+ tmpl_array.push(' <th class="button_heading"></th>');
+ tmpl_array.push(' <th>name</th>');
+ tmpl_array.push(' <th>description</th>');
+ tmpl_array.push(' <th>synopsis</th> ');
+ tmpl_array.push(' <th>model type</th> ');
+ tmpl_array.push(' </thead>');
+ tmpl_array.push(' <tbody>');
+ tmpl_array.push(' <% _.each(libraries, function(library) { %>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <td><button title="Open this library" type="button" data-id="<%- library.get("root_folder_id") %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-folder-open"></span> browse</td>');
+ tmpl_array.push(' <td><%- library.get("name") %></td>');
+ tmpl_array.push(' <td><%= _.escape(library.get("description")) %></td>');
+ tmpl_array.push(' <td><%= _.escape(library.get("synopsis")) %></td>');
+ tmpl_array.push(' <td><%= _.escape(library.get("model_class")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push(' </tbody>');
+ tmpl_array.push('</table>');
+
+ tmpl_array.push('</div>');
+ return tmpl_array.join('');
+ },
+
+ // render
+ render: function () {
+ //hack to show scrollbars
+ $("#center").css('overflow','auto');
+ var that = this;
+ // if (typeof libraries === "undefined") {
+ libraries = new Libraries();
+ // }
+ libraries.fetch({
+ success: function (libraries) {
+ var template = _.template(that.template_library_list(), { libraries : libraries.models });
+ that.$el.html(template);
+ }
+ })
+ },
+
+ // own modal
+ modal : null,
+
+ // show/hide create library modal
+ show_library_modal : function (event){
+ event.preventDefault();
+ event.stopPropagation();
+
+ // create modal
+ var self = this;
+ this.modal = new mod_modal.GalaxyModal(
+ {
+ title : 'Create New Library',
+ body : this.template_new_library(),
+ buttons : {
+ 'Create' : function() {self.create_new_library_event()},
+ 'Close' : function() {self.modal.hide()}
+ }
+ });
+
+ // show prepared modal
+ this.modal.show();
+ },
+
+ // create the new library from modal
+ create_new_library_event: function(){
+ var libraryDetails = this.serialize_new_library();
+ var valid = this.validate_new_library(libraryDetails);
+ var library = new Library();
+ var self = this;
+ library.save(libraryDetails, {
+ success: function (library) {
+ self.modal.hide();
+ self.clear_library_modal();
+ self.render();
+ },
+ error: function(){
+ self.modal.showNotification('An error occured', 5000, 'error');
+ }
+ });
+ return false;
+ },
+
+ // clear the library modal once saved
+ clear_library_modal : function(){
+ $("input[name='Name']").val('');
+ $("input[name='Description']").val('');
+ $("input[name='Synopsis']").val('');
+ },
+
+ // serialize data from the form
+ serialize_new_library : function(){
+ return {
+ name: $("input[name='Name']").val(),
+ description: $("input[name='Description']").val(),
+ synopsis: $("input[name='Synopsis']").val()
+ };
+ },
+
+ validate_new_library: function(library){
+
+ },
+
+
+ // template for new library modal
+ template_new_library: function()
+ {
+ tmpl_array = [];
+
+ tmpl_array.push('<div id="new_library_modal">');
+ tmpl_array.push('<form>');
+ tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');
+ tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');
+ tmpl_array.push('<input type="text" name="Synopsis" value="" placeholder="Synopsis">');
+ tmpl_array.push('</form>');
+ tmpl_array.push('</div>');
+
+ return tmpl_array.join('');
+ }
+});
+
+// galaxy library wrapper View
+var GalaxyLibrary = Backbone.View.extend({
+ folderContentView : null,
+ galaxyLibraryview : null,
+ initialize : function(){
+
+ folderContentView = new FolderContentView();
+ galaxyLibraryview = new GalaxyLibraryview();
+
+ library_router = new LibraryRouter();
+
+ library_router.on('route:libraries', function() {
+ // render libraries list
+ galaxyLibraryview.render();
+ });
+
+ library_router.on('route:folder_content', function(id) {
+ // render folder's contents
+ folderContentView.render({id: id});
+ });
+
+ library_router.on('route:download', function(folder_id, format) {
+ // send download stream
+ if (typeof folderContentView === 'undefined'){
+ alert('you cant touch this!');
+ // } else if (folderContentView.modal !== null){
+ // folderContentView.download(folder_id, format);
+ } else if ($('#center').find(':checked').length === 0) { // coming from outside of the library app
+ library_router.navigate('folders/' + folder_id, {trigger: true, replace: true});
+ // TODO show message of redirection
+ } else {
+ folderContentView.download(folder_id, format);
+ library_router.navigate('folders/' + folder_id, {trigger: false, replace: true});
+ }
+
+ });
+
+Backbone.history.start();
+
+return this
+}
+});
+
+// return
+return {
+ GalaxyApp: GalaxyLibrary
+};
+
+});
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 static/scripts/galaxy.menu.js
--- a/static/scripts/galaxy.menu.js
+++ b/static/scripts/galaxy.menu.js
@@ -57,7 +57,12 @@
tab_shared.add({
title : "Data Libraries",
- content : "library/index",
+ content : "library/index"
+ });
+
+ tab_shared.add({
+ title : "New Libraries",
+ content : "library/list",
divider : true
});
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -1,6 +1,3 @@
-/*
- galaxy modal
-*/
// dependencies
define([], function() {
@@ -25,8 +22,51 @@
// initialize
initialize : function(options) {
+ self = this;
if (options)
this.create(options);
+
+ this.bindClick(event, self);
+ },
+
+ // bind the click-to-hide function
+ bindClick: function(event, that) {
+ // bind the ESC key to hide() function
+ $(document).on('keyup', function(event){
+ if (event.keyCode == 27) { self.hide(); }
+ })
+ // bind the click anywhere to hide() function...
+ $('html').on('click', function(event){
+ that.hide();
+ })
+ // ...but don't hide if the click is on modal content
+ $('.modal-content').on('click', function(event){
+ event.stopPropagation();
+ })
+ },
+
+ // unbind the click-to-hide function
+ unbindClick: function(event, that){
+ // bind the ESC key to hide() function
+ $(document).off('keyup', function(event){
+ if (event.keyCode == 27) { that.hide(); }
+ })
+ // unbind the click anywhere to hide() function...
+ $('html').off('click', function(event){
+ that.hide();
+ })
+ // ...but don't hide if the click is on modal content
+ $('.modal-content').off('click', function(event){
+ event.stopPropagation();
+ })
+ },
+
+
+ // destroy
+ destroy : function(){
+ this.hide();
+ this.unbindClick();
+ $('.modal').remove();
},
// adds and displays a new frame/window
@@ -87,6 +127,7 @@
this.$footer = (this.$el).find('.modal-footer');
this.$buttons = (this.$el).find('.buttons');
this.$backdrop = (this.$el).find('.modal-backdrop');
+ this.$notification = (this.$el).find('.notification-modal');
// append body
this.$body.html(this.options.body);
@@ -120,6 +161,47 @@
this.$buttons.find('#' + String(name).toLowerCase()).prop('disabled', true);
},
+ // hide buttons
+ hideButton: function(name) {
+ this.$buttons.find('#' + String(name).toLowerCase()).hide();
+ },
+ // show buttons
+ showButton: function(name) {
+ this.$buttons.find('#' + String(name).toLowerCase()).show();
+ },
+
+ // show notification
+ showNotification : function(message, duration, type) {
+ // defaults
+ var duration = typeof duration !== 'undefined' ? duration : 1500;
+ // var bgColor = typeof bgColor !== 'undefined' ? bgColor : "#F4E0E1";
+ // var txtColor = typeof txtColor !== 'undefined' ? txtColor : "#A42732";
+ var bgColor;
+ var txtColor;
+
+ if (type === 'error'){
+ bgColor = '#f4e0e1';
+ txtColor = '#a42732';
+ // } else if (type === 'success'){
+ } else { // success is default
+ bgColor = '#e1f4e0';
+ txtColor = '#32a427';
+ }
+
+ var HTMLmessage = "<div class='notification-message' style='text-align:center; line-height:16px; '> " + message + " </div>";
+ this.$notification.html("<div id='notification-bar' style='display:none; float: right; height: 16px; width:100%; background-color: " + bgColor + "; z-index: 100; color: " + txtColor + ";border-bottom: 1px solid " + txtColor + ";'>" + HTMLmessage + "</div>");
+
+ var self = this;
+
+ /*animate the bar*/
+ $('#notification-bar').slideDown(function() {
+ setTimeout(function() {
+ $('#notification-bar').slideUp(function() {self.$notification.html('');});
+ }, duration);
+ });
+
+ },
+
// returns scroll top for body element
scrollTop: function()
{
@@ -139,7 +221,8 @@
'<div class="modal-header">' +
'<button type="button" class="close" style="display: none;">×</button>' +
'<h4 class="title">' + title + '</h4>' +
- '</div>' +
+ '<span class="notification-modal"></span>' +
+ '</div>' +
'<div class="modal-body"></div>' +
'<div class="modal-footer">' +
'<div class="buttons" style="float: right;"></div>' +
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 static/scripts/libs/bootstrap.js
--- a/static/scripts/libs/bootstrap.js
+++ b/static/scripts/libs/bootstrap.js
@@ -575,3 +575,158 @@
}
}(window.jQuery);
+
+/* ========================================================================
+ * Bootstrap: dropdown.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#dropdowns
+ * ========================================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // DROPDOWN CLASS DEFINITION
+ // =========================
+
+ var backdrop = '.dropdown-backdrop'
+ var toggle = '[data-toggle=dropdown]'
+ var Dropdown = function (element) {
+ var $el = $(element).on('click.bs.dropdown', this.toggle)
+ }
+
+ Dropdown.prototype.toggle = function (e) {
+ var $this = $(this)
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ clearMenus()
+
+ if (!isActive) {
+ if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
+ // if mobile we we use a backdrop because click events don't delegate
+ $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
+ }
+
+ $parent.trigger(e = $.Event('show.bs.dropdown'))
+
+ if (e.isDefaultPrevented()) return
+
+ $parent
+ .toggleClass('open')
+ .trigger('shown.bs.dropdown')
+
+ $this.focus()
+ }
+
+ return false
+ }
+
+ Dropdown.prototype.keydown = function (e) {
+ if (!/(38|40|27)/.test(e.keyCode)) return
+
+ var $this = $(this)
+
+ e.preventDefault()
+ e.stopPropagation()
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ if (!isActive || (isActive && e.keyCode == 27)) {
+ if (e.which == 27) $parent.find(toggle).focus()
+ return $this.click()
+ }
+
+ var $items = $('[role=menu] li:not(.divider):visible a', $parent)
+
+ if (!$items.length) return
+
+ var index = $items.index($items.filter(':focus'))
+
+ if (e.keyCode == 38 && index > 0) index-- // up
+ if (e.keyCode == 40 && index < $items.length - 1) index++ // down
+ if (!~index) index=0
+
+ $items.eq(index).focus()
+ }
+
+ function clearMenus() {
+ $(backdrop).remove()
+ $(toggle).each(function (e) {
+ var $parent = getParent($(this))
+ if (!$parent.hasClass('open')) return
+ $parent.trigger(e = $.Event('hide.bs.dropdown'))
+ if (e.isDefaultPrevented()) return
+ $parent.removeClass('open').trigger('hidden.bs.dropdown')
+ })
+ }
+
+ function getParent($this) {
+ var selector = $this.attr('data-target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ var $parent = selector && $(selector)
+
+ return $parent && $parent.length ? $parent : $this.parent()
+ }
+
+
+ // DROPDOWN PLUGIN DEFINITION
+ // ==========================
+
+ var old = $.fn.dropdown
+
+ $.fn.dropdown = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('dropdown')
+
+ if (!data) $this.data('dropdown', (data = new Dropdown(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ $.fn.dropdown.Constructor = Dropdown
+
+
+ // DROPDOWN NO CONFLICT
+ // ====================
+
+ $.fn.dropdown.noConflict = function () {
+ $.fn.dropdown = old
+ return this
+ }
+
+
+ // APPLY TO STANDARD DROPDOWN ELEMENTS
+ // ===================================
+
+ $(document)
+ .on('click.bs.dropdown.data-api', clearMenus)
+ .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+ .on('click.bs.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
+ .on('keydown.bs.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
+
+}(window.jQuery);
\ No newline at end of file
diff -r df88d6121d48b0b193d34445ce49916adf17a2d2 -r 181b293ea65936b7edb338bcbeac3bdea8e53063 static/style/src/less/library.less
--- a/static/style/src/less/library.less
+++ b/static/style/src/less/library.less
@@ -29,6 +29,50 @@
background-color: @table-bg-accent;
}
+
+tr.light td
+{
+ background-color: white;
+ color: black;
+}
+tr.light:hover td
+{
+ background-color: #f5f5f5;
+ color: #8389a1;
+}
+tr.dark td
+{
+ background-color: #8389a1;
+ color: white;
+}
+tr.dark:hover td
+{
+ background-color: #bbbfd0;
+ color: white;
+}
+a.dark:hover
+{
+ color: yellow;
+ // text-decoration: none;
+}
+a.dark
+{
+ color: white;
+ // text-decoration: none;
+}
+th.button_heading
+{
+ width: 7em;
+}
+div.library_breadcrumb{
+ padding-top: 0.8em;
+ padding-bottom: 0.8em;
+}
+div.library_breadcrumb a:hover{
+ color:green;
+}
+
+
img.expanderIcon {
padding-right: 4px;
}
https://bitbucket.org/galaxy/galaxy-central/commits/17c16125da1a/
Changeset: 17c16125da1a
User: martenson
Date: 2013-12-11 17:22:38
Summary: Merged galaxy/galaxy-central into default
Affected #: 4 files
diff -r 181b293ea65936b7edb338bcbeac3bdea8e53063 -r 17c16125da1a00a53a020ff0fd84e5676dbabaf1 lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -1050,8 +1050,11 @@
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ),
Column( "key", TrimmedString( 32 ), index=True, unique=True ) )
+
# With the tables defined we can define the mappers and setup the
# relationships between the model objects.
+def simple_mapping( model, **kwds ):
+ mapper( model, model.table, properties=kwds )
mapper( model.Sample, model.Sample.table,
@@ -1200,64 +1203,62 @@
mapper( model.ValidationError, model.ValidationError.table )
-mapper( model.HistoryDatasetAssociation, model.HistoryDatasetAssociation.table,
- properties=dict(
- dataset=relation(
- model.Dataset,
- primaryjoin=( model.Dataset.table.c.id == model.HistoryDatasetAssociation.table.c.dataset_id ), lazy=False ),
- # .history defined in History mapper
- copied_from_history_dataset_association=relation(
- model.HistoryDatasetAssociation,
- primaryjoin=( model.HistoryDatasetAssociation.table.c.copied_from_history_dataset_association_id == model.HistoryDatasetAssociation.table.c.id ),
- remote_side=[model.HistoryDatasetAssociation.table.c.id],
- uselist=False ),
- copied_to_history_dataset_associations=relation(
- model.HistoryDatasetAssociation,
- primaryjoin=( model.HistoryDatasetAssociation.table.c.copied_from_history_dataset_association_id == model.HistoryDatasetAssociation.table.c.id ) ),
- copied_from_library_dataset_dataset_association=relation(
- model.LibraryDatasetDatasetAssociation,
- primaryjoin=( model.HistoryDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == model.LibraryDatasetDatasetAssociation.table.c.id ),
- uselist=False ),
- copied_to_library_dataset_dataset_associations=relation(
- model.LibraryDatasetDatasetAssociation,
- primaryjoin=( model.HistoryDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == model.LibraryDatasetDatasetAssociation.table.c.id ) ),
- implicitly_converted_datasets=relation(
- model.ImplicitlyConvertedDatasetAssociation,
- primaryjoin=( model.ImplicitlyConvertedDatasetAssociation.table.c.hda_parent_id == model.HistoryDatasetAssociation.table.c.id ) ),
- implicitly_converted_parent_datasets=relation(
- model.ImplicitlyConvertedDatasetAssociation,
- primaryjoin=( model.ImplicitlyConvertedDatasetAssociation.table.c.hda_id == model.HistoryDatasetAssociation.table.c.id ) ),
- children=relation(
- model.HistoryDatasetAssociation,
- primaryjoin=( model.HistoryDatasetAssociation.table.c.parent_id == model.HistoryDatasetAssociation.table.c.id ),
- backref=backref( "parent", primaryjoin=( model.HistoryDatasetAssociation.table.c.parent_id == model.HistoryDatasetAssociation.table.c.id ), remote_side=[model.HistoryDatasetAssociation.table.c.id], uselist=False ) ),
- visible_children=relation(
- model.HistoryDatasetAssociation,
- primaryjoin=( ( model.HistoryDatasetAssociation.table.c.parent_id == model.HistoryDatasetAssociation.table.c.id ) & ( model.HistoryDatasetAssociation.table.c.visible == True ) ) ),
- tags=relation( model.HistoryDatasetAssociationTagAssociation, order_by=model.HistoryDatasetAssociationTagAssociation.table.c.id, backref='history_tag_associations' ),
- annotations=relation( model.HistoryDatasetAssociationAnnotationAssociation, order_by=model.HistoryDatasetAssociationAnnotationAssociation.table.c.id, backref="hdas" ),
- ratings=relation( model.HistoryDatasetAssociationRatingAssociation, order_by=model.HistoryDatasetAssociationRatingAssociation.table.c.id, backref="hdas" ) )
- )
+simple_mapping( model.HistoryDatasetAssociation,
+ dataset=relation(
+ model.Dataset,
+ primaryjoin=( model.Dataset.table.c.id == model.HistoryDatasetAssociation.table.c.dataset_id ), lazy=False ),
+ # .history defined in History mapper
+ copied_from_history_dataset_association=relation(
+ model.HistoryDatasetAssociation,
+ primaryjoin=( model.HistoryDatasetAssociation.table.c.copied_from_history_dataset_association_id == model.HistoryDatasetAssociation.table.c.id ),
+ remote_side=[model.HistoryDatasetAssociation.table.c.id],
+ uselist=False ),
+ copied_to_history_dataset_associations=relation(
+ model.HistoryDatasetAssociation,
+ primaryjoin=( model.HistoryDatasetAssociation.table.c.copied_from_history_dataset_association_id == model.HistoryDatasetAssociation.table.c.id ) ),
+ copied_from_library_dataset_dataset_association=relation(
+ model.LibraryDatasetDatasetAssociation,
+ primaryjoin=( model.HistoryDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == model.LibraryDatasetDatasetAssociation.table.c.id ),
+ uselist=False ),
+ copied_to_library_dataset_dataset_associations=relation(
+ model.LibraryDatasetDatasetAssociation,
+ primaryjoin=( model.HistoryDatasetAssociation.table.c.copied_from_library_dataset_dataset_association_id == model.LibraryDatasetDatasetAssociation.table.c.id ) ),
+ implicitly_converted_datasets=relation(
+ model.ImplicitlyConvertedDatasetAssociation,
+ primaryjoin=( model.ImplicitlyConvertedDatasetAssociation.table.c.hda_parent_id == model.HistoryDatasetAssociation.table.c.id ) ),
+ implicitly_converted_parent_datasets=relation(
+ model.ImplicitlyConvertedDatasetAssociation,
+ primaryjoin=( model.ImplicitlyConvertedDatasetAssociation.table.c.hda_id == model.HistoryDatasetAssociation.table.c.id ) ),
+ children=relation(
+ model.HistoryDatasetAssociation,
+ primaryjoin=( model.HistoryDatasetAssociation.table.c.parent_id == model.HistoryDatasetAssociation.table.c.id ),
+ backref=backref( "parent", primaryjoin=( model.HistoryDatasetAssociation.table.c.parent_id == model.HistoryDatasetAssociation.table.c.id ), remote_side=[model.HistoryDatasetAssociation.table.c.id], uselist=False ) ),
+ visible_children=relation(
+ model.HistoryDatasetAssociation,
+ primaryjoin=( ( model.HistoryDatasetAssociation.table.c.parent_id == model.HistoryDatasetAssociation.table.c.id ) & ( model.HistoryDatasetAssociation.table.c.visible == True ) ) ),
+ tags=relation( model.HistoryDatasetAssociationTagAssociation, order_by=model.HistoryDatasetAssociationTagAssociation.table.c.id, backref='history_tag_associations' ),
+ annotations=relation( model.HistoryDatasetAssociationAnnotationAssociation, order_by=model.HistoryDatasetAssociationAnnotationAssociation.table.c.id, backref="hdas" ),
+ ratings=relation( model.HistoryDatasetAssociationRatingAssociation, order_by=model.HistoryDatasetAssociationRatingAssociation.table.c.id, backref="hdas" )
+)
-mapper( model.Dataset, model.Dataset.table,
- properties=dict(
- history_associations=relation(
- model.HistoryDatasetAssociation,
- primaryjoin=( model.Dataset.table.c.id == model.HistoryDatasetAssociation.table.c.dataset_id ) ),
- active_history_associations=relation(
- model.HistoryDatasetAssociation,
- primaryjoin=( ( model.Dataset.table.c.id == model.HistoryDatasetAssociation.table.c.dataset_id ) & ( model.HistoryDatasetAssociation.table.c.deleted == False ) & ( model.HistoryDatasetAssociation.table.c.purged == False ) ) ),
- purged_history_associations=relation(
- model.HistoryDatasetAssociation,
- primaryjoin=( ( model.Dataset.table.c.id == model.HistoryDatasetAssociation.table.c.dataset_id ) & ( model.HistoryDatasetAssociation.table.c.purged == True ) ) ),
- library_associations=relation(
- model.LibraryDatasetDatasetAssociation,
- primaryjoin=( model.Dataset.table.c.id == model.LibraryDatasetDatasetAssociation.table.c.dataset_id ) ),
- active_library_associations=relation(
- model.LibraryDatasetDatasetAssociation,
- primaryjoin=( ( model.Dataset.table.c.id == model.LibraryDatasetDatasetAssociation.table.c.dataset_id ) & ( model.LibraryDatasetDatasetAssociation.table.c.deleted == False ) ) ),
- tags=relation(model.DatasetTagAssociation, order_by=model.DatasetTagAssociation.table.c.id, backref='datasets')
- ) )
+simple_mapping( model.Dataset,
+ history_associations=relation(
+ model.HistoryDatasetAssociation,
+ primaryjoin=( model.Dataset.table.c.id == model.HistoryDatasetAssociation.table.c.dataset_id ) ),
+ active_history_associations=relation(
+ model.HistoryDatasetAssociation,
+ primaryjoin=( ( model.Dataset.table.c.id == model.HistoryDatasetAssociation.table.c.dataset_id ) & ( model.HistoryDatasetAssociation.table.c.deleted == False ) & ( model.HistoryDatasetAssociation.table.c.purged == False ) ) ),
+ purged_history_associations=relation(
+ model.HistoryDatasetAssociation,
+ primaryjoin=( ( model.Dataset.table.c.id == model.HistoryDatasetAssociation.table.c.dataset_id ) & ( model.HistoryDatasetAssociation.table.c.purged == True ) ) ),
+ library_associations=relation(
+ model.LibraryDatasetDatasetAssociation,
+ primaryjoin=( model.Dataset.table.c.id == model.LibraryDatasetDatasetAssociation.table.c.dataset_id ) ),
+ active_library_associations=relation(
+ model.LibraryDatasetDatasetAssociation,
+ primaryjoin=( ( model.Dataset.table.c.id == model.LibraryDatasetDatasetAssociation.table.c.dataset_id ) & ( model.LibraryDatasetDatasetAssociation.table.c.deleted == False ) ) ),
+ tags=relation(model.DatasetTagAssociation, order_by=model.DatasetTagAssociation.table.c.id, backref='datasets')
+)
mapper( model.HistoryDatasetAssociationDisplayAtAuthorization, model.HistoryDatasetAssociationDisplayAtAuthorization.table,
properties=dict( history_dataset_association = relation( model.HistoryDatasetAssociation ),
@@ -1751,90 +1752,63 @@
) )
# Tag tables.
+simple_mapping( model.Tag,
+ children=relation(model.Tag, backref=backref( 'parent', remote_side=[model.Tag.table.c.id] ) )
+)
-mapper( model.Tag, model.Tag.table,
- properties=dict( children=relation(model.Tag, backref=backref( 'parent', remote_side=[model.Tag.table.c.id] ) )
- ) )
-mapper( model.HistoryTagAssociation, model.HistoryTagAssociation.table,
- properties=dict( tag=relation(model.Tag, backref="tagged_histories"), user=relation( model.User ) )
- )
+def tag_mapping( tag_association_class, backref_name ):
+ simple_mapping( tag_association_class, tag=relation( model.Tag, backref=backref_name), user=relation( model.User ) )
-mapper( model.DatasetTagAssociation, model.DatasetTagAssociation.table,
- properties=dict( tag=relation(model.Tag, backref="tagged_datasets"), user=relation( model.User ) )
- )
+tag_mapping( model.HistoryTagAssociation, "tagged_histories" )
-mapper( model.HistoryDatasetAssociationTagAssociation, model.HistoryDatasetAssociationTagAssociation.table,
- properties=dict( tag=relation(model.Tag, backref="tagged_history_dataset_associations"), user=relation( model.User ) )
- )
+tag_mapping( model.DatasetTagAssociation, "tagged_datasets" )
-mapper( model.PageTagAssociation, model.PageTagAssociation.table,
- properties=dict( tag=relation(model.Tag, backref="tagged_pages"), user=relation( model.User ) )
- )
+tag_mapping( model.HistoryDatasetAssociationTagAssociation, "tagged_history_dataset_associations" )
-mapper( model.StoredWorkflowTagAssociation, model.StoredWorkflowTagAssociation.table,
- properties=dict( tag=relation(model.Tag, backref="tagged_workflows"), user=relation( model.User ) )
- )
+tag_mapping( model.PageTagAssociation, "tagged_pages" )
-mapper( model.WorkflowStepTagAssociation, model.WorkflowStepTagAssociation.table,
- properties=dict( tag=relation(model.Tag, backref="tagged_workflow_steps"), user=relation( model.User ) )
- )
+tag_mapping( model.StoredWorkflowTagAssociation, "tagged_workflows" )
-mapper( model.VisualizationTagAssociation, model.VisualizationTagAssociation.table,
- properties=dict( tag=relation(model.Tag, backref="tagged_visualizations"), user=relation( model.User ) )
- )
+tag_mapping( model.WorkflowStepTagAssociation, "tagged_workflow_steps" )
-mapper( model.ToolTagAssociation, model.ToolTagAssociation.table,
- properties=dict( tag=relation(model.Tag, backref="tagged_tools"), user=relation( model.User ) )
- )
+tag_mapping( model.VisualizationTagAssociation, "tagged_visualizations" )
+
+tag_mapping( model.ToolTagAssociation, "tagged_tools" )
+
# Annotation tables.
+def annotation_mapping( annotation_class, **kwds ):
+ kwds = dict( [ (key, relation( value ) ) for key, value in kwds.iteritems() ] )
+ simple_mapping( annotation_class, **dict(user=relation( model.User ), **kwds ) )
-mapper( model.HistoryAnnotationAssociation, model.HistoryAnnotationAssociation.table,
- properties=dict( history=relation( model.History ), user=relation( model.User ) )
- )
+annotation_mapping( model.HistoryAnnotationAssociation, history=model.History )
-mapper( model.HistoryDatasetAssociationAnnotationAssociation, model.HistoryDatasetAssociationAnnotationAssociation.table,
- properties=dict( hda=relation( model.HistoryDatasetAssociation ), user=relation( model.User ) )
- )
+annotation_mapping( model.HistoryDatasetAssociationAnnotationAssociation, hda=model.HistoryDatasetAssociation )
-mapper( model.StoredWorkflowAnnotationAssociation, model.StoredWorkflowAnnotationAssociation.table,
- properties=dict( stored_workflow=relation( model.StoredWorkflow ), user=relation( model.User ) )
- )
+annotation_mapping( model.StoredWorkflowAnnotationAssociation, stored_workflow=model.StoredWorkflow )
-mapper( model.WorkflowStepAnnotationAssociation, model.WorkflowStepAnnotationAssociation.table,
- properties=dict( workflow_step=relation( model.WorkflowStep ), user=relation( model.User ) )
- )
+annotation_mapping( model.WorkflowStepAnnotationAssociation, workflow_step=model.WorkflowStep )
-mapper( model.PageAnnotationAssociation, model.PageAnnotationAssociation.table,
- properties=dict( page=relation( model.Page ), user=relation( model.User ) )
- )
+annotation_mapping( model.PageAnnotationAssociation, page=model.Page )
-mapper( model.VisualizationAnnotationAssociation, model.VisualizationAnnotationAssociation.table,
- properties=dict( visualization=relation( model.Visualization ), user=relation( model.User ) )
- )
+annotation_mapping( model.VisualizationAnnotationAssociation, visualization=model.Visualization )
+
# Rating tables.
+def rating_mapping( rating_class, **kwds ):
+ kwds = dict( [ (key, relation( value ) ) for key, value in kwds.iteritems() ] )
+ simple_mapping( rating_class, **dict(user=relation( model.User ), **kwds ) )
-mapper( model.HistoryRatingAssociation, model.HistoryRatingAssociation.table,
- properties=dict( history=relation( model.History ), user=relation( model.User ) )
- )
+rating_mapping( model.HistoryRatingAssociation, history=model.History )
-mapper( model.HistoryDatasetAssociationRatingAssociation, model.HistoryDatasetAssociationRatingAssociation.table,
- properties=dict( hda=relation( model.HistoryDatasetAssociation ), user=relation( model.User ) )
- )
+rating_mapping( model.HistoryDatasetAssociationRatingAssociation, hda=model.HistoryDatasetAssociation )
-mapper( model.StoredWorkflowRatingAssociation, model.StoredWorkflowRatingAssociation.table,
- properties=dict( stored_workflow=relation( model.StoredWorkflow ), user=relation( model.User ) )
- )
+rating_mapping( model.StoredWorkflowRatingAssociation, stored_workflow=model.StoredWorkflow )
-mapper( model.PageRatingAssociation, model.PageRatingAssociation.table,
- properties=dict( page=relation( model.Page ), user=relation( model.User ) )
- )
+rating_mapping( model.PageRatingAssociation, page=model.Page )
-mapper( model.VisualizationRatingAssociation, model.VisualizationRatingAssociation.table,
- properties=dict( visualization=relation( model.Visualization ), user=relation( model.User ) )
- )
+rating_mapping( model.VisualizationRatingAssociation, visualizaiton=model.Visualization )
#Data Manager tables
mapper( model.DataManagerHistoryAssociation, model.DataManagerHistoryAssociation.table,
diff -r 181b293ea65936b7edb338bcbeac3bdea8e53063 -r 17c16125da1a00a53a020ff0fd84e5676dbabaf1 lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -3003,10 +3003,10 @@
# Add link details.
if link_details:
# Add details for creating a hyperlink to the tool.
- if not self.tool_type.startswith( 'data_source' ):
- link = url_for( '/tool_runner', tool_id=self.id )
+ if not isinstance( self, DataSourceTool ):
+ link = url_for( controller='tool_runner', tool_id=self.id )
else:
- link = url_for( self.action, **self.get_static_param_values( trans ) )
+ link = url_for( controller='tool_runner', action='data_source_redirect', tool_id=self.id )
# Basic information
tool_dict.update( { 'link': link,
diff -r 181b293ea65936b7edb338bcbeac3bdea8e53063 -r 17c16125da1a00a53a020ff0fd84e5676dbabaf1 lib/galaxy/webapps/galaxy/controllers/tool_runner.py
--- a/lib/galaxy/webapps/galaxy/controllers/tool_runner.py
+++ b/lib/galaxy/webapps/galaxy/controllers/tool_runner.py
@@ -6,6 +6,7 @@
import galaxy.util
from galaxy import web
from galaxy.tools import DefaultToolState
+from galaxy.tools import DataSourceTool
from galaxy.tools.actions import upload_common
from galaxy.tools.parameters import params_to_incoming
from galaxy.tools.parameters import visit_input_values
@@ -233,6 +234,35 @@
**vars )
@web.expose
+ def data_source_redirect( self, trans, tool_id=None ):
+ """
+ Redirects a user accessing a Data Source tool to its target action link.
+ This method will subvert mix-mode content blocking in several browsers when
+ accessing non-https data_source tools from an https galaxy server.
+
+ Tested as working on Safari 7.0 and FireFox 26
+ Subverting did not work on Chrome 31
+ """
+ if tool_id is None:
+ return trans.response.send_redirect( url_for( controller="root", action="welcome" ) )
+ tool_version_select_field, tools, tool = self.__get_tool_components( tool_id,
+ tool_version=None,
+ get_loaded_tools_by_lineage=False,
+ set_selected=False )
+ # No tool matching the tool id, display an error (shouldn't happen)
+ if not tool:
+ log.error( "data_source_redirect called with tool id '%s' but no such tool exists", tool_id )
+ trans.log_event( "Tool id '%s' does not exist" % tool_id )
+ trans.response.status = 404
+ return "Tool '%s' does not exist, kwd=%s " % ( tool_id, kwd )
+
+ if isinstance( tool, DataSourceTool ):
+ link = url_for( tool.action, **tool.get_static_param_values( trans ) )
+ else:
+ link = url_for( controller='tool_runner', tool_id=tool.id )
+ return trans.response.send_redirect( link )
+
+ @web.expose
def redirect( self, trans, redirect_url=None, **kwd ):
if not redirect_url:
return trans.show_error_message( "Required URL for redirection missing" )
diff -r 181b293ea65936b7edb338bcbeac3bdea8e53063 -r 17c16125da1a00a53a020ff0fd84e5676dbabaf1 test/unit/test_galaxy_mapping.py
--- a/test/unit/test_galaxy_mapping.py
+++ b/test/unit/test_galaxy_mapping.py
@@ -3,10 +3,139 @@
class MappingTests( unittest.TestCase ):
+
+ def test_annotations( self ):
+ model = self.model
+
+ u = model.User( email="annotator(a)example.com", password="password" )
+ self.persist( u )
+
+ def persist_and_check_annotation( annotation_class, **kwds ):
+ annotated_association = annotation_class()
+ annotated_association.annotation = "Test Annotation"
+ annotated_association.user = u
+ for key, value in kwds.iteritems():
+ setattr(annotated_association, key, value)
+ self.persist( annotated_association )
+ self.expunge()
+ stored_annotation = self.query( annotation_class ).all()[0]
+ assert stored_annotation.annotation == "Test Annotation"
+ assert stored_annotation.user.email == "annotator(a)example.com"
+
+ sw = model.StoredWorkflow()
+ sw.user = u
+ self.persist( sw )
+ persist_and_check_annotation( model.StoredWorkflowAnnotationAssociation, stored_workflow=sw )
+
+ workflow = model.Workflow()
+ workflow.stored_workflow = sw
+ self.persist( workflow )
+
+ ws = model.WorkflowStep()
+ ws.workflow = workflow
+ self.persist( ws )
+ persist_and_check_annotation( model.WorkflowStepAnnotationAssociation, workflow_step=ws )
+
+ h = model.History( name="History for Annotation", user=u)
+ self.persist( h )
+ persist_and_check_annotation( model.HistoryAnnotationAssociation, history=h )
+
+ d1 = model.HistoryDatasetAssociation( extension="txt", history=h, create_dataset=True, sa_session=model.session )
+ self.persist( d1 )
+ persist_and_check_annotation( model.HistoryDatasetAssociationAnnotationAssociation, hda=d1 )
+
+ page = model.Page()
+ page.user = u
+ self.persist( page )
+ persist_and_check_annotation( model.PageAnnotationAssociation, page=page )
+
+ visualization = model.Visualization()
+ visualization.user = u
+ self.persist( visualization )
+ persist_and_check_annotation( model.VisualizationAnnotationAssociation, visualization=visualization )
+
+ def test_ratings( self ):
+ model = self.model
+
+ u = model.User( email="rater(a)example.com", password="password" )
+ self.persist( u )
+
+ def persist_and_check_rating( rating_class, **kwds ):
+ rating_association = rating_class()
+ rating_association.rating = 5
+ rating_association.user = u
+ for key, value in kwds.iteritems():
+ setattr(rating_association, key, value)
+ self.persist( rating_association )
+ self.expunge()
+ stored_annotation = self.query( rating_class ).all()[0]
+ assert stored_annotation.rating == 5
+ assert stored_annotation.user.email == "rater(a)example.com"
+
+ sw = model.StoredWorkflow()
+ sw.user = u
+ self.persist( sw )
+ persist_and_check_rating( model.StoredWorkflowRatingAssociation, stored_workflow=sw )
+
+ h = model.History( name="History for Rating", user=u)
+ self.persist( h )
+ persist_and_check_rating( model.HistoryRatingAssociation, history=h )
+
+ d1 = model.HistoryDatasetAssociation( extension="txt", history=h, create_dataset=True, sa_session=model.session )
+ self.persist( d1 )
+ persist_and_check_rating( model.HistoryDatasetAssociationRatingAssociation, hda=d1 )
+
+ page = model.Page()
+ page.user = u
+ self.persist( page )
+ persist_and_check_rating( model.PageRatingAssociation, page=page )
+
+ visualization = model.Visualization()
+ visualization.user = u
+ self.persist( visualization )
+ persist_and_check_rating( model.VisualizationRatingAssociation, visualization=visualization )
+
+ def test_tags( self ):
+ model = self.model
+
+ my_tag = model.Tag(name="Test Tag")
+ u = model.User( email="tagger(a)example.com", password="password" )
+ self.persist( my_tag, u )
+
+ def tag_and_test( taggable_object, tag_association_class, backref_name ):
+ assert len( getattr(self.query( model.Tag ).filter( model.Tag.name == "Test Tag" ).all()[0], backref_name) ) == 0
+
+ tag_association = tag_association_class()
+ tag_association.tag = my_tag
+ taggable_object.tags = [ tag_association ]
+ self.persist( tag_association, taggable_object )
+
+ assert len( getattr(self.query( model.Tag ).filter( model.Tag.name == "Test Tag" ).all()[0], backref_name) ) == 1
+
+ sw = model.StoredWorkflow()
+ sw.user = u
+ #self.persist( sw )
+ tag_and_test( sw, model.StoredWorkflowTagAssociation, "tagged_workflows" )
+
+ h = model.History( name="History for Tagging", user=u)
+ tag_and_test( h, model.HistoryTagAssociation, "tagged_histories" )
+
+ d1 = model.HistoryDatasetAssociation( extension="txt", history=h, create_dataset=True, sa_session=model.session )
+ tag_and_test( d1, model.HistoryDatasetAssociationTagAssociation, "tagged_history_dataset_associations" )
+
+ page = model.Page()
+ page.user = u
+ tag_and_test( page, model.PageTagAssociation, "tagged_pages" )
+
+ visualization = model.Visualization()
+ visualization.user = u
+ tag_and_test( visualization, model.VisualizationTagAssociation, "tagged_visualizations" )
+
def test_basic( self ):
- # Start the database and connect the mapping
- model = mapping.init( "/tmp", "sqlite:///:memory:", create_tables=True )
- assert model.engine is not None
+ model = self.model
+
+ original_user_count = len( model.session.query( model.User ).all() )
+
# Make some changes and commit them
u = model.User( email="james(a)foo.bar.baz", password="password" )
# gs = model.GalaxySession()
@@ -14,40 +143,64 @@
#h1.queries.append( model.Query( "h1->q1" ) )
#h1.queries.append( model.Query( "h1->q2" ) )
h2 = model.History( name=( "H" * 1024 ) )
- model.session.add_all( ( u, h1, h2 ) )
+ self.persist( u, h1, h2 )
#q1 = model.Query( "h2->q1" )
metadata = dict( chromCol=1, startCol=2, endCol=3 )
d1 = model.HistoryDatasetAssociation( extension="interval", metadata=metadata, history=h2, create_dataset=True, sa_session=model.session )
#h2.queries.append( q1 )
#h2.queries.append( model.Query( "h2->q2" ) )
- model.session.add( ( d1 ) )
- model.session.flush()
- model.session.expunge_all()
+ self.persist( d1 )
+
# Check
users = model.session.query( model.User ).all()
- assert len( users ) == 1
- assert users[0].email == "james(a)foo.bar.baz"
- assert users[0].password == "password"
- assert len( users[0].histories ) == 1
- assert users[0].histories[0].name == "History 1"
+ assert len( users ) == original_user_count + 1
+ user = [user for user in users if user.email == "james(a)foo.bar.baz"][0]
+ assert user.email == "james(a)foo.bar.baz"
+ assert user.password == "password"
+ assert len( user.histories ) == 1
+ assert user.histories[0].name == "History 1"
hists = model.session.query( model.History ).all()
- assert hists[0].name == "History 1"
- assert hists[1].name == ( "H" * 255 )
- assert hists[0].user == users[0]
- assert hists[1].user is None
- assert hists[1].datasets[0].metadata.chromCol == 1
+ hist0 = [history for history in hists if history.name == "History 1"][0]
+ hist1 = [history for history in hists if history.name == "H" * 255][0]
+ assert hist0.name == "History 1"
+ assert hist1.name == ( "H" * 255 )
+ assert hist0.user == user
+ assert hist1.user is None
+ assert hist1.datasets[0].metadata.chromCol == 1
# The filename test has moved to objecstore
- #id = hists[1].datasets[0].id
- #assert hists[1].datasets[0].file_name == os.path.join( "/tmp", *directory_hash_id( id ) ) + ( "/dataset_%d.dat" % id )
+ #id = hist1.datasets[0].id
+ #assert hist1.datasets[0].file_name == os.path.join( "/tmp", *directory_hash_id( id ) ) + ( "/dataset_%d.dat" % id )
# Do an update and check
- hists[1].name = "History 2b"
- model.session.flush()
- model.session.expunge_all()
+ hist1.name = "History 2b"
+ self.expunge()
hists = model.session.query( model.History ).all()
- assert hists[0].name == "History 1"
- assert hists[1].name == "History 2b"
+ hist0 = [history for history in hists if history.name == "History 1"][0]
+ hist1 = [history for history in hists if history.name == "History 2b"][0]
+ assert hist0.name == "History 1"
+ assert hist1.name == "History 2b"
# gvk TODO need to ad test for GalaxySessions, but not yet sure what they should look like.
+ @classmethod
+ def setUpClass(cls):
+ # Start the database and connect the mapping
+ cls.model = mapping.init( "/tmp", "sqlite:///:memory:", create_tables=True )
+ assert cls.model.engine is not None
+
+ @classmethod
+ def query( cls, type ):
+ return cls.model.session.query( type )
+
+ @classmethod
+ def persist(cls, *args):
+ for arg in args:
+ cls.model.session.add( arg )
+ cls.expunge()
+
+ @classmethod
+ def expunge(cls):
+ cls.model.session.flush()
+ cls.model.session.expunge_all()
+
def get_suite():
suite = unittest.TestSuite()
https://bitbucket.org/galaxy/galaxy-central/commits/ff102054eec1/
Changeset: ff102054eec1
User: martenson
Date: 2013-12-11 19:03:02
Summary: merge into default
Affected #: 17 files
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -1827,7 +1827,7 @@
class Library( object, Dictifiable ):
permitted_actions = get_permitted_actions( filter='LIBRARY' )
dict_collection_visible_keys = ( 'id', 'name' )
- dict_element_visible_keys = ( 'id', 'deleted', 'name', 'description', 'synopsis' )
+ dict_element_visible_keys = ( 'id', 'deleted', 'name', 'description', 'synopsis', 'root_folder_id' )
def __init__( self, name=None, description=None, synopsis=None, root_folder=None ):
self.name = name or "Unnamed library"
self.description = description
@@ -1894,7 +1894,7 @@
return name
class LibraryFolder( object, Dictifiable ):
- dict_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build' )
+ dict_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build', 'update_time' )
def __init__( self, name=None, description=None, item_count=0, order_id=None ):
self.name = name or "Unnamed folder"
self.description = description
@@ -2060,6 +2060,7 @@
genome_build = ldda.dbkey,
misc_info = ldda.info,
misc_blurb = ldda.blurb,
+ peek = ( lambda ldda: ldda.display_peek() if ldda.peek and ldda.peek != 'no peek' else None )( ldda ),
template_data = template_data )
if ldda.dataset.uuid is None:
rval['uuid'] = None
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -1852,8 +1852,9 @@
table = self.table
trans = conn.begin()
try:
- next_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
- table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid + 1 ) )
+ current_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
+ next_hid = current_hid + 1
+ table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid ) )
trans.commit()
return next_hid
except:
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 lib/galaxy/webapps/galaxy/api/datasets.py
--- a/lib/galaxy/webapps/galaxy/api/datasets.py
+++ b/lib/galaxy/webapps/galaxy/api/datasets.py
@@ -1,5 +1,5 @@
"""
-API operations on the contents of a dataset.
+API operations on the contents of a history dataset.
"""
from galaxy import web
from galaxy.visualization.data_providers.genome import FeatureLocationIndexDataProvider
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 lib/galaxy/webapps/galaxy/api/folder_contents.py
--- a/lib/galaxy/webapps/galaxy/api/folder_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/folder_contents.py
@@ -1,5 +1,5 @@
"""
-API operations on the contents of a library.
+API operations on the contents of a folder.
"""
import logging, os, string, shutil, urllib, re, socket
from cgi import escape, FieldStorage
@@ -11,67 +11,122 @@
log = logging.getLogger( __name__ )
class FolderContentsController( BaseAPIController, UsesLibraryMixin, UsesLibraryMixinItems ):
+ """
+ Class controls retrieval, creation and updating of folder contents.
+ """
+
+ def load_folder_contents( self, trans, folder ):
+ """
+ Loads all contents of the folder (folders and data sets) but only in the first level.
+ """
+ current_user_roles = trans.get_current_user_roles()
+ is_admin = trans.user_is_admin()
+ content_items = []
+ for subfolder in folder.active_folders:
+ if not is_admin:
+ can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder )
+ if (is_admin or can_access) and not subfolder.deleted:
+ subfolder.api_type = 'folder'
+ content_items.append( subfolder )
+ for dataset in folder.datasets:
+ if not is_admin:
+ can_access = trans.app.security_agent.can_access_dataset( current_user_roles, dataset.library_dataset_dataset_association.dataset )
+ if (is_admin or can_access) and not dataset.deleted:
+ dataset.api_type = 'file'
+ content_items.append( dataset )
+ return content_items
@web.expose_api
def index( self, trans, folder_id, **kwd ):
"""
GET /api/folders/{encoded_folder_id}/contents
Displays a collection (list) of a folder's contents (files and folders).
- The /api/library_contents/{encoded_library_id}/contents
- lists everything in a library recursively, which is not what
- we want here. We could add a parameter to use the recursive
- style, but this is meant to act similar to an "ls" directory listing.
+ Encoded folder ID is prepended with 'F' if it is a folder as opposed to a data set which does not have it.
+ Full path is provided as a separate object in response providing data for breadcrumb path building.
"""
- rval = []
+ folder_container = []
current_user_roles = trans.get_current_user_roles()
- def traverse( folder ):
- admin = trans.user_is_admin()
- rval = []
- for subfolder in folder.active_folders:
- if not admin:
- can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder )
- if (admin or can_access) and not subfolder.deleted:
- subfolder.api_type = 'folder'
- rval.append( subfolder )
- for ld in folder.datasets:
- if not admin:
- can_access = trans.app.security_agent.can_access_dataset( current_user_roles, ld.library_dataset_dataset_association.dataset )
- if (admin or can_access) and not ld.deleted:
- ld.api_type = 'file'
- rval.append( ld )
- return rval
-
- try:
- decoded_folder_id = trans.security.decode_id( folder_id[-16:] )
- except TypeError:
- trans.response.status = 400
- return "Malformed folder id ( %s ) specified, unable to decode." % str( folder_id )
+ if ( folder_id.startswith( 'F' ) ):
+ try:
+ decoded_folder_id = trans.security.decode_id( folder_id[1:] )
+ except TypeError:
+ trans.response.status = 400
+ return "Malformed folder id ( %s ) specified, unable to decode." % str( folder_id )
try:
folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( decoded_folder_id )
- parent_library = folder.parent_library
except:
folder = None
- log.error( "FolderContentsController.index: Unable to retrieve folder %s"
- % folder_id )
+ log.error( "FolderContentsController.index: Unable to retrieve folder with ID: %s" % folder_id )
- # TODO: Find the API's path to this folder if necessary.
- # This was needed in recursive descent, but it's not needed
- # for "ls"-style content checking:
- if not folder or not ( trans.user_is_admin() or trans.app.security_agent.can_access_library_item( current_user_roles, folder, trans.user ) ):
+ # We didn't find the folder or user does not have an access to it.
+ if not folder:
trans.response.status = 400
return "Invalid folder id ( %s ) specified." % str( folder_id )
+
+ if not ( trans.user_is_admin() or trans.app.security_agent.can_access_library_item( current_user_roles, folder, trans.user ) ):
+ log.warning( "SECURITY: User (id: %s) without proper access rights is trying to load folder with ID of %s" % ( trans.user.id, folder.id ) )
+ trans.response.status = 400
+ return "Invalid folder id ( %s ) specified." % str( folder_id )
+
+ path_to_root = []
+ def build_path ( folder ):
+ """
+ Search the path upwards recursively and load the whole route of names and ids for breadcrumb purposes.
+ """
+ path_to_root = []
+ # We are almost in root
+ if folder.parent_id is None:
+ path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
+ else:
+ # We add the current folder and traverse up one folder.
+ path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
+ upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_id )
+ path_to_root.extend( build_path( upper_folder ) )
+ return path_to_root
+
+ # Return the reversed path so it starts with the library node.
+ full_path = build_path( folder )[::-1]
+ folder_container.append( dict( full_path = full_path ) )
+
+ folder_contents = []
+ time_updated = ''
+ time_created = ''
+ # Go through every item in the folder and include its meta-data.
+ for content_item in self.load_folder_contents( trans, folder ):
+# rval = content_item.to_dict()
+ return_item = {}
+ encoded_id = trans.security.encode_id( content_item.id )
+ time_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
+ time_created = content_item.create_time.strftime( "%Y-%m-%d %I:%M %p" )
+
+ # For folder return also hierarchy values
+ if content_item.api_type == 'folder':
+ encoded_id = 'F' + encoded_id
+# time_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
+ return_item.update ( dict ( item_count = content_item.item_count ) )
- for content in traverse( folder ):
- encoded_id = trans.security.encode_id( content.id )
- if content.api_type == 'folder':
- encoded_id = 'F' + encoded_id
- rval.append( dict( id = encoded_id,
- type = content.api_type,
- name = content.name,
- url = url_for( 'folder_contents', folder_id=encoded_id ) ) )
- return rval
+ if content_item.api_type == 'file':
+ library_dataset_dict = content_item.to_dict()
+ library_dataset_dict['data_type']
+ library_dataset_dict['file_size']
+ library_dataset_dict['date_uploaded']
+ return_item.update ( dict ( data_type = library_dataset_dict['data_type'],
+ file_size = library_dataset_dict['file_size'],
+ date_uploaded = library_dataset_dict['date_uploaded'] ) )
+
+ # For every item return also the default meta-data
+ return_item.update( dict( id = encoded_id,
+ type = content_item.api_type,
+ name = content_item.name,
+ time_updated = time_updated,
+ time_created = time_created
+ ) )
+ folder_contents.append( return_item )
+ # Put the data in the container
+ folder_container.append( dict( folder_contents = folder_contents ) )
+ return folder_container
@web.expose_api
def show( self, trans, id, library_id, **kwd ):
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 lib/galaxy/webapps/galaxy/api/lda_datasets.py
--- /dev/null
+++ b/lib/galaxy/webapps/galaxy/api/lda_datasets.py
@@ -0,0 +1,241 @@
+"""
+API operations on the dataset from library.
+"""
+import glob
+import logging
+import operator
+import os
+import os.path
+import string
+import sys
+import tarfile
+import tempfile
+import urllib
+import urllib2
+import zipfile
+from galaxy.security import Action
+from galaxy import util, web
+from galaxy.util.streamball import StreamBall
+from galaxy.web.base.controller import BaseAPIController, UsesLibraryMixinItems
+
+import logging
+log = logging.getLogger( __name__ )
+
+# Test for available compression types
+# tmpd = tempfile.mkdtemp()
+# comptypes = []
+# for comptype in ( 'gz', 'bz2' ):
+# tmpf = os.path.join( tmpd, 'compression_test.tar.' + comptype )
+# try:
+# archive = tarfile.open( tmpf, 'w:' + comptype )
+# archive.close()
+# comptypes.append( comptype )
+# except tarfile.CompressionError:
+# log.exception( "Compression error when testing %s compression. This option will be disabled for library downloads." % comptype )
+# try:
+# os.unlink( tmpf )
+# except OSError:
+# pass
+ziptype = '32'
+# tmpf = os.path.join( tmpd, 'compression_test.zip' )
+# try:
+# archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True )
+# archive.close()
+# comptypes.append( 'zip' )
+# ziptype = '64'
+# except RuntimeError:
+# log.exception( "Compression error when testing zip compression. This option will be disabled for library downloads." )
+# except (TypeError, zipfile.LargeZipFile):
+# # ZIP64 is only in Python2.5+. Remove TypeError when 2.4 support is dropped
+# log.warning( 'Max zip file size is 2GB, ZIP64 not supported' )
+# comptypes.append( 'zip' )
+# try:
+# os.unlink( tmpf )
+# except OSError:
+# pass
+# os.rmdir( tmpd )
+
+
+
+class DatasetsController( BaseAPIController, UsesLibraryMixinItems ):
+
+ @web.expose_api
+ def show( self, trans, id, **kwd ):
+ """
+ GET /api/libraries/datasets/{encoded_dataset_id}
+ Displays information about the dataset identified by the lda ID.
+ """
+ # Get dataset.
+ try:
+ dataset = self.get_library_dataset( trans, id = id )
+ except Exception, e:
+ return str( e )
+ try:
+ # Default: return dataset as dict.
+ rval = dataset.to_dict()
+ except Exception, e:
+ rval = "Error in dataset API at listing contents: " + str( e )
+ log.error( rval + ": %s" % str(e), exc_info=True )
+ trans.response.status = 500
+
+ rval['id'] = trans.security.encode_id(rval['id']);
+ rval['ldda_id'] = trans.security.encode_id(rval['ldda_id']);
+ rval['folder_id'] = 'f' + trans.security.encode_id(rval['folder_id'])
+
+ return rval
+
+ @web.expose
+ def download( self, trans, format, **kwd ):
+ """
+ POST /api/libraries/datasets/download/{format}
+ POST data: ldda_ids = []
+ Downloads dataset(s) in the requested format.
+ """
+ lddas = []
+# is_admin = trans.user_is_admin()
+# current_user_roles = trans.get_current_user_roles()
+
+ datasets_to_download = kwd['ldda_ids%5B%5D']
+
+ if ( datasets_to_download != None ):
+ datasets_to_download = util.listify( datasets_to_download )
+ for dataset_id in datasets_to_download:
+ try:
+ ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( dataset_id ) )
+ assert not ldda.dataset.purged
+ lddas.append( ldda )
+ except:
+ ldda = None
+ message += "Invalid library dataset id (%s) specified. " % str( dataset_id )
+
+ if format in [ 'zip','tgz','tbz' ]:
+ error = False
+ killme = string.punctuation + string.whitespace
+ trantab = string.maketrans(killme,'_'*len(killme))
+ try:
+ outext = 'zip'
+ if format == 'zip':
+ # Can't use mkstemp - the file must not exist first
+ tmpd = tempfile.mkdtemp()
+ util.umask_fix_perms( tmpd, trans.app.config.umask, 0777, self.app.config.gid )
+ tmpf = os.path.join( tmpd, 'library_download.' + format )
+ if ziptype == '64' and trans.app.config.upstream_gzip:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED, True )
+ elif ziptype == '64':
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True )
+ elif trans.app.config.upstream_gzip:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED )
+ else:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED )
+ archive.add = lambda x, y: archive.write( x, y.encode('CP437') )
+ elif format == 'tgz':
+ if trans.app.config.upstream_gzip:
+ archive = StreamBall( 'w|' )
+ outext = 'tar'
+ else:
+ archive = StreamBall( 'w|gz' )
+ outext = 'tgz'
+ elif format == 'tbz':
+ archive = StreamBall( 'w|bz2' )
+ outext = 'tbz2'
+ except ( OSError, zipfile.BadZipfile ):
+ error = True
+ log.exception( "Unable to create archive for download" )
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ except:
+ error = True
+ log.exception( "Unexpected error %s in create archive for download" % sys.exc_info()[0] )
+ message = "Unable to create archive for download, please report - %s" % sys.exc_info()[0]
+ status = 'error'
+ if not error:
+ composite_extensions = trans.app.datatypes_registry.get_composite_extensions()
+ seen = []
+ for ldda in lddas:
+ if ldda.dataset.state in [ 'new', 'upload', 'queued', 'running', 'empty', 'discarded' ]:
+ continue
+ ext = ldda.extension
+ is_composite = ext in composite_extensions
+ path = ""
+ parent_folder = ldda.library_dataset.folder
+ while parent_folder is not None:
+ # Exclude the now-hidden "root folder"
+ if parent_folder.parent is None:
+ path = os.path.join( parent_folder.library_root[0].name, path )
+ break
+ path = os.path.join( parent_folder.name, path )
+ parent_folder = parent_folder.parent
+ path += ldda.name
+ while path in seen:
+ path += '_'
+ seen.append( path )
+ zpath = os.path.split(path)[-1] # comes as base_name/fname
+ outfname,zpathext = os.path.splitext(zpath)
+ if is_composite:
+ # need to add all the components from the extra_files_path to the zip
+ if zpathext == '':
+ zpath = '%s.html' % zpath # fake the real nature of the html file
+ try:
+ archive.add(ldda.dataset.file_name,zpath) # add the primary of a composite set
+ except IOError:
+ error = True
+ log.exception( "Unable to add composite parent %s to temporary library download archive" % ldda.dataset.file_name)
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ continue
+ flist = glob.glob(os.path.join(ldda.dataset.extra_files_path,'*.*')) # glob returns full paths
+ for fpath in flist:
+ efp,fname = os.path.split(fpath)
+ if fname > '':
+ fname = fname.translate(trantab)
+ try:
+ archive.add( fpath,fname )
+ except IOError:
+ error = True
+ log.exception( "Unable to add %s to temporary library download archive %s" % (fname,outfname))
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ continue
+ else: # simple case
+ try:
+ archive.add( ldda.dataset.file_name, path )
+ except IOError:
+ error = True
+ log.exception( "Unable to write %s to temporary library download archive" % ldda.dataset.file_name)
+ message = "Unable to create archive for download, please report this error"
+ status = 'error'
+ if not error:
+ lname = 'selected_dataset'
+ fname = lname.replace( ' ', '_' ) + '_files'
+ if format == 'zip':
+ archive.close()
+ trans.response.set_content_type( "application/octet-stream" )
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
+ archive = util.streamball.ZipBall(tmpf, tmpd)
+ archive.wsgi_status = trans.response.wsgi_status()
+ archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ return archive.stream
+ else:
+ trans.response.set_content_type( "application/x-tar" )
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
+ archive.wsgi_status = trans.response.wsgi_status()
+ archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ return archive.stream
+ elif format == 'uncompressed':
+ if len(lddas) != 1:
+ return 'Wrong request'
+ else:
+ single_dataset = lddas[0]
+ trans.response.set_content_type( single_dataset.get_mime() )
+ fStat = os.stat( ldda.file_name )
+ trans.response.headers[ 'Content-Length' ] = int( fStat.st_size )
+ valid_chars = '.,^_-()[]0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ fname = ldda.name
+ fname = ''.join( c in valid_chars and c or '_' for c in fname )[ 0:150 ]
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s"' % fname
+ try:
+ return open( single_dataset.file_name )
+ except:
+ return 'This dataset contains no content'
+ else:
+ return 'Wrong format';
\ No newline at end of file
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 lib/galaxy/webapps/galaxy/api/libraries.py
--- a/lib/galaxy/webapps/galaxy/api/libraries.py
+++ b/lib/galaxy/webapps/galaxy/api/libraries.py
@@ -49,9 +49,10 @@
trans.model.Library.table.c.id.in_( accessible_restricted_library_ids ) ) )
rval = []
for library in query:
- item = library.to_dict()
+ item = library.to_dict( view='element' )
item['url'] = url_for( route, id=trans.security.encode_id( library.id ) )
- item['id'] = trans.security.encode_id( item['id'] )
+ item['id'] = 'F' + trans.security.encode_id( item['id'] )
+ item['root_folder_id'] = 'F' + trans.security.encode_id( item['root_folder_id'] )
rval.append( item )
return rval
@@ -131,6 +132,9 @@
rval['name'] = name
rval['id'] = encoded_id
return rval
+
+ def edit( self, trans, payload, **kwd ):
+ return "Not implemented yet"
@web.expose_api
def delete( self, trans, id, **kwd ):
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -46,12 +46,6 @@
atexit.register( app.shutdown )
# Create the universe WSGI application
webapp = GalaxyWebApplication( app, session_cookie='galaxysession', name='galaxy' )
- # Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
- webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
- controller='admin_toolshed',
- action='display_image_in_repository',
- repository_id=None,
- image_file=None )
webapp.add_ui_controllers( 'galaxy.webapps.galaxy.controllers', app )
# Force /history to go to /root/history -- needed since the tests assume this
webapp.add_route( '/history', controller='root', action='history' )
@@ -75,22 +69,12 @@
webapp.add_route( '/u/:username/v/:slug', controller='visualization', action='display_by_username_and_slug' )
webapp.add_route( '/search', controller='search', action='index' )
- # Add the web API
+ # ================
+ # ===== API =====
+ # ================
+
webapp.add_api_controllers( 'galaxy.webapps.galaxy.api', app )
- # The /folders section is experimental at this point:
- log.debug( "app.config.api_folders: %s" % app.config.api_folders )
- webapp.mapper.resource( 'folder', 'folders', path_prefix='/api' )
- webapp.mapper.resource( 'content', 'contents',
- controller='folder_contents',
- name_prefix='folder_',
- path_prefix='/api/folders/:folder_id',
- parent_resources=dict( member_name='folder', collection_name='folders' ) )
- webapp.mapper.resource( 'content',
- 'contents',
- controller='library_contents',
- name_prefix='library_',
- path_prefix='/api/libraries/:library_id',
- parent_resources=dict( member_name='library', collection_name='libraries' ) )
+
webapp.mapper.resource( 'content',
'contents',
controller='history_contents',
@@ -102,10 +86,6 @@
controller="datasets",
action="display",
conditions=dict(method=["GET"]))
- webapp.mapper.resource( 'permission',
- 'permissions',
- path_prefix='/api/libraries/:library_id',
- parent_resources=dict( member_name='library', collection_name='libraries' ) )
webapp.mapper.resource( 'user',
'users',
controller='group_users',
@@ -127,11 +107,6 @@
_add_item_tags_controller( webapp,
name_prefix="workflow_",
path_prefix='/api/workflows/:workflow_id' )
-
- _add_item_extended_metadata_controller( webapp,
- name_prefix="library_dataset_",
- path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
-
_add_item_annotation_controller( webapp,
name_prefix="history_content_",
path_prefix='/api/histories/:history_id/contents/:history_content_id' )
@@ -141,7 +116,6 @@
_add_item_annotation_controller( webapp,
name_prefix="workflow_",
path_prefix='/api/workflows/:workflow_id' )
-
_add_item_provenance_controller( webapp,
name_prefix="history_content_",
path_prefix='/api/histories/:history_id/contents/:history_content_id' )
@@ -171,6 +145,39 @@
#webapp.mapper.connect( 'run_workflow', '/api/workflow/{workflow_id}/library/{library_id}', controller='workflows', action='run', workflow_id=None, library_id=None, conditions=dict(method=["GET"]) )
webapp.mapper.resource( 'search', 'search', path_prefix='/api' )
+ # =======================
+ # ===== LIBRARY API =====
+ # =======================
+
+ # The /folders section is experimental at this point:
+ log.debug( "app.config.api_folders: %s" % app.config.api_folders )
+
+ webapp.mapper.connect( 'show_lda_item', '/api/libraries/datasets/:id', controller='lda_datasets', action='show', conditions=dict(method=["GET"]) )
+ webapp.mapper.connect( 'download_lda_items', '/api/libraries/datasets/download/:format', controller='lda_datasets', action='download', conditions=dict(method=["POST"]) )
+
+ webapp.mapper.resource_with_deleted( 'library', 'libraries', path_prefix='/api' )
+ webapp.mapper.resource( 'folder', 'folders', path_prefix='/api' )
+
+ webapp.mapper.resource( 'content', 'contents',
+ controller='folder_contents',
+ name_prefix='folder_',
+ path_prefix='/api/folders/:folder_id',
+ parent_resources=dict( member_name='folder', collection_name='folders' ) )
+ webapp.mapper.resource( 'content',
+ 'contents',
+ controller='library_contents',
+ name_prefix='library_',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
+ webapp.mapper.resource( 'permission',
+ 'permissions',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
+ _add_item_extended_metadata_controller( webapp,
+ name_prefix="library_dataset_",
+ path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
+
+
# add as a non-ATOM API call to support the notion of a 'current/working' history unique to the history resource
webapp.mapper.connect( "set_as_current", "/api/histories/{id}/set_as_current",
controller="histories", action="set_as_current", conditions=dict( method=["POST"] ) )
@@ -188,6 +195,18 @@
webapp.mapper.connect("workflow_dict", '/api/workflows/{workflow_id}/download', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
# Preserve the following download route for now for dependent applications -- deprecate at some point
webapp.mapper.connect("workflow_dict", '/api/workflows/download/{workflow_id}', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
+
+ # ====================
+ # ===== TOOLSHED =====
+ # ====================
+
+ # Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
+ webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
+ controller='admin_toolshed',
+ action='display_image_in_repository',
+ repository_id=None,
+ image_file=None )
+
# Galaxy API for tool shed features.
webapp.mapper.resource( 'tool_shed_repository',
'tool_shed_repositories',
@@ -201,6 +220,7 @@
path_prefix='/api',
new={ 'install_repository_revision' : 'POST' },
parent_resources=dict( member_name='tool_shed_repository', collection_name='tool_shed_repositories' ) )
+
# Connect logger from app
if app.trace_logger:
webapp.trace_logger = app.trace_logger
@@ -221,7 +241,7 @@
galaxy.model.mapping.metadata.engine.connection_provider._pool.dispose()
except:
pass
- # Close any pooled database connections before forking
+ # Close any pooled database connections before forking
try:
galaxy.model.tool_shed_install.mapping.metadata.engine.connection_provider._pool.dispose()
except:
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 lib/galaxy/webapps/galaxy/controllers/library.py
--- a/lib/galaxy/webapps/galaxy/controllers/library.py
+++ b/lib/galaxy/webapps/galaxy/controllers/library.py
@@ -76,6 +76,17 @@
library_list_grid = LibraryListGrid()
+
+ @web.expose
+ def list( self, trans, **kwd ):
+ params = util.Params( kwd )
+ # define app configuration for generic mako template
+ app = {
+ 'jscript' : "galaxy.library"
+ }
+ # fill template
+ return trans.fill_template('galaxy.panels.mako', config = {'app' : app})
+
@web.expose
def index( self, trans, **kwd ):
params = util.Params( kwd )
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 static/scripts/galaxy.library.js
--- /dev/null
+++ b/static/scripts/galaxy.library.js
@@ -0,0 +1,860 @@
+// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+// === GALAXY LIBRARY MODULE ====
+// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+
+// global variables
+var view = null;
+var library_router = null;
+var responses = [];
+
+
+// load required libraries
+// require([
+// // load js libraries
+// 'utils/galaxy.css',
+// ], function(css){
+// // load css
+// css.load_file("static/style/library.css");
+// });
+
+// dependencies
+define(["galaxy.modal", "galaxy.masthead", "utils/galaxy.utils"], function(mod_modal, mod_masthead, mod_utils) {
+
+// MMMMMMMMMMMMMMM
+// === Models ====
+// MMMMMMMMMMMMMMM
+
+ // LIBRARY
+ var Library = Backbone.Model.extend({
+ urlRoot: '/api/libraries'
+ });
+
+ // LIBRARIES
+ var Libraries = Backbone.Collection.extend({
+ url: '/api/libraries',
+ model: Library
+ });
+
+ // ITEM
+ var Item = Backbone.Model.extend({
+ urlRoot : '/api/libraries/datasets'
+ })
+
+ // FOLDER
+ var Folder = Backbone.Collection.extend({
+ model: Item
+ })
+
+ // CONTAINER for folder contents (folders, items and metadata).
+ var FolderContainer = Backbone.Model.extend({
+ defaults : {
+ folder : new Folder(),
+ full_path : "unknown",
+ urlRoot : "/api/folders/",
+ id : "unknown"
+ },
+ parse : function(obj) {
+ this.full_path = obj[0].full_path;
+ // update the inner collection
+ this.get("folder").reset(obj[1].folder_contents);
+ return obj;
+ }
+ })
+
+ // HISTORY ITEM
+ var HistoryItem = Backbone.Model.extend({
+ urlRoot : '/api/histories/'
+ });
+
+ // HISTORY
+ var GalaxyHistory = Backbone.Model.extend({
+ url : '/api/histories/'
+ });
+
+ // HISTORIES
+ var GalaxyHistories = Backbone.Collection.extend({
+ url : '/api/histories',
+ model : GalaxyHistory
+ });
+
+ //ROUTER
+ var LibraryRouter = Backbone.Router.extend({
+ routes: {
+ "" : "libraries",
+ "folders/:id" : "folder_content",
+ "folders/:folder_id/download/:format" : "download"
+ }
+ });
+
+
+// MMMMMMMMMMMMMM
+// === VIEWS ====
+// MMMMMMMMMMMMMM
+
+// galaxy folder
+var FolderContentView = Backbone.View.extend({
+ // main element definition
+ el : '#center',
+ // progress percentage
+ progress: 0,
+ // progress rate per one item
+ progressStep: 1,
+ // last selected history in modal for UX
+ lastSelectedHistory: '',
+ // self modal
+ modal : null,
+ // loaded folders
+ folders : null,
+
+ // initialize
+ initialize : function(){
+ this.folders = [];
+ this.queue = jQuery.Deferred();
+ this.queue.resolve();
+ },
+
+// MMMMMMMMMMMMMMMMMM
+// === TEMPLATES ====
+// MMMMMMMMMMMMMMMMMM
+
+ // set up
+ templateFolder : function (){
+ var tmpl_array = [];
+
+ // CONTAINER
+ tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; ">');
+ tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');
+
+ // TOOLBAR
+ tmpl_array.push('<div id="library_folder_toolbar" >');
+ tmpl_array.push(' <button title="Create New Folder" id="toolbtn_create_folder" class="btn btn-primary" type="button"><span class="fa fa-icon-plus"></span><span class="fa fa-icon-folder-close"></span> folder</button>');
+ tmpl_array.push(' <button id="toolbtn_bulk_import" class="btn btn-primary" style="display: none; margin-left: 0.5em;" type="button"><span class="fa fa-icon-external-link"></span> to history</button>');
+
+ tmpl_array.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; display: none; ">');
+ tmpl_array.push(' <button id="drop_toggle" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">');
+ tmpl_array.push(' <span class="fa fa-icon-download"></span> download <span class="caret"></span>');
+ tmpl_array.push(' </button>');
+ tmpl_array.push(' <ul class="dropdown-menu" role="menu">');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');
+ tmpl_array.push(' </ul>');
+ tmpl_array.push(' </div>');
+
+ tmpl_array.push('</div>');
+
+ // BREADCRUMBS
+ tmpl_array.push('<div class="library_breadcrumb">');
+ tmpl_array.push('<a title="Return to the list of libraries" href="#">Libraries</a><b>|</b> ');
+ tmpl_array.push('<% _.each(path, function(path_item) { %>');
+ tmpl_array.push('<% if (path_item[0] != id) { %>');
+ tmpl_array.push('<a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a><b>|</b> ');
+ tmpl_array.push('<% } else { %>');
+ tmpl_array.push('<span title="You are in this folder"><%- path_item[1] %></span>');
+ tmpl_array.push('<% } %>');
+ tmpl_array.push('<% }); %>');
+ tmpl_array.push('</div>');
+
+ // FOLDER CONTENT
+ tmpl_array.push('<table id="folder_table" class="table table-condensed">');
+ tmpl_array.push(' <thead>');
+ tmpl_array.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');
+ tmpl_array.push(' <th class="button_heading">view</th>');
+ tmpl_array.push(' <th>name</th>');
+ tmpl_array.push(' <th>data type</th>');
+ tmpl_array.push(' <th>size</th>');
+ tmpl_array.push(' <th>date</th>');
+ tmpl_array.push(' </thead>');
+ tmpl_array.push(' <tbody>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td><button title="Go to parent folder" type="button" data-id="<%- upper_folder_id %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-arrow-up"></span> .. go up</td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% _.each(items, function(content_item) { %>');
+ tmpl_array.push(' <tr class="folder_row light" id="<%- content_item.id %>">');
+ tmpl_array.push(' <% if (content_item.get("type") === "folder") { %>'); // folder
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td><button title="Open this folder" type="button" data-id="<%- content_item.id %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-folder-open"></span> browse</td>');
+ tmpl_array.push(' <td><%- content_item.get("name") %>');
+ tmpl_array.push(' <% if (content_item.get("item_count") === 0) { %>'); // empty folder
+ tmpl_array.push(' <span class="muted">(empty folder)</span>');
+ tmpl_array.push(' <% } %>');
+ tmpl_array.push(' </td>');
+ tmpl_array.push(' <td>folder</td>'); // data type
+ tmpl_array.push(' <td><%= _.escape(content_item.get("item_count")) %> item(s)</td>'); // size
+ tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');
+ tmpl_array.push(' <td>');
+ tmpl_array.push(' <button title="See details of this dataset" type="button" class="library-dataset btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-eye-open"></span> details');
+ tmpl_array.push(' </button>');
+ tmpl_array.push(' </td>');
+ tmpl_array.push(' <td><%- content_item.get("name") %></td>'); // dataset
+ tmpl_array.push(' <td><%= _.escape(content_item.get("data_type")) %></td>'); // data type
+ tmpl_array.push(' <td><%= _.escape(content_item.get("readable_size")) %></td>'); // size
+ tmpl_array.push(' <% } %> ');
+ tmpl_array.push(' <td><%= _.escape(content_item.get("time_updated")) %></td>'); // time updated
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push(' ');
+ tmpl_array.push(' </tbody>');
+ tmpl_array.push('</table>');
+
+ tmpl_array.push('</div>');
+ return tmpl_array.join('');
+ },
+ templateDatasetModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<div id="dataset_info_modal">');
+ tmpl_array.push(' <table class="table table-striped table-condensed">');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row" id="id_row" data-id="<%= _.escape(item.get("ldda_id")) %>">Name</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("name")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Data type</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("data_type")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Genome build</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("genome_build")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <th scope="row">Size</th>');
+ tmpl_array.push(' <td><%= _.escape(size) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Date uploaded</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("date_uploaded")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Uploaded by</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("uploaded_by")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr scope="row">');
+ tmpl_array.push(' <th scope="row">Data Lines</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_data_lines")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <th scope="row">Comment Lines</th>');
+ tmpl_array.push(' <% if (item.get("metadata_comment_lines") === "") { %>'); //folder
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_comment_lines")) %></td>');
+ tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td scope="row">unknown</td>');
+ tmpl_array.push(' <% } %>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Number of Columns</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_columns")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Column Types</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_column_types")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Miscellaneous information</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("misc_blurb")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' </table>');
+ tmpl_array.push(' <pre class="peek">');
+ tmpl_array.push(' </pre>');
+ tmpl_array.push('</div>');
+
+ return tmpl_array.join('');
+ },
+
+ templateHistorySelectInModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<span id="history_modal_combo" style="width:90%; margin-left: 1em; margin-right: 1em; ">');
+ tmpl_array.push('Select history: ');
+ tmpl_array.push('<select id="dataset_import_single" name="dataset_import_single" style="width:50%; margin-bottom: 1em; "> ');
+ tmpl_array.push(' <% _.each(histories, function(history) { %>'); //history select box
+ tmpl_array.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push('</select>');
+ tmpl_array.push('</span>');
+
+ return tmpl_array.join('');
+ },
+
+ templateBulkImportInModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');
+ tmpl_array.push('Select history: ');
+ tmpl_array.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');
+ tmpl_array.push(' <% _.each(histories, function(history) { %>'); //history select box
+ tmpl_array.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push('</select>');
+ tmpl_array.push('</span>');
+
+ return tmpl_array.join('');
+ },
+
+ // convert size to nice string
+ size_to_string : function (size)
+ {
+ // identify unit
+ var unit = "";
+ if (size >= 100000000000) { size = size / 100000000000; unit = "TB"; } else
+ if (size >= 100000000) { size = size / 100000000; unit = "GB"; } else
+ if (size >= 100000) { size = size / 100000; unit = "MB"; } else
+ if (size >= 100) { size = size / 100; unit = "KB"; } else
+ { size = size * 10; unit = "b"; }
+ // return formatted string
+ return (Math.round(size) / 10) + unit;
+ },
+
+// MMMMMMMMMMMMMMM
+// === EVENTS ====
+// MMMMMMMMMMMMMMM
+
+ // event binding
+ events: {
+ 'click #select-all-checkboxes' : 'selectAll',
+ 'click .folder_row' : 'selectClicked',
+ 'click #toolbtn_bulk_import' : 'modalBulkImport',
+ 'click #toolbtn_dl' : 'bulkDownload',
+ 'click .library-dataset' : 'showDatasetDetails',
+ 'click #toolbtn_create_folder' : 'createFolderModal',
+ 'click .btn_open_folder' : 'navigateToFolder'
+ },
+
+ //render the folder view
+ render: function (options) {
+ //hack to show scrollbars
+ $("#center").css('overflow','auto');
+
+ view = this;
+ var that = this;
+
+ var folderContainer = new FolderContainer({id: options.id});
+ folderContainer.url = folderContainer.attributes.urlRoot + options.id + '/contents';
+
+ folderContainer.fetch({
+ success: function (container) {
+
+ // prepare nice size strings
+ for (var i = 0; i < folderContainer.attributes.folder.models.length; i++) {
+ var model = folderContainer.attributes.folder.models[i]
+ if (model.get('type') === 'file'){
+ model.set('readable_size', that.size_to_string(model.get('file_size')))
+ }
+ };
+
+ // find the upper id
+ var path = folderContainer.full_path;
+ var upper_folder_id;
+ if (path.length === 1){ // library is above us
+ upper_folder_id = 0;
+ } else {
+ upper_folder_id = path[path.length-2][0];
+ }
+
+ var template = _.template(that.templateFolder(), { path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id, upper_folder_id: upper_folder_id });
+ // var template = _.template(that.templateFolder(), { path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id });
+ that.$el.html(template);
+ }
+ })
+ },
+
+ // handles the click on 'open' and 'upper' folder icons
+ navigateToFolder : function(event){
+ var folder_id = $(event.target).attr('data-id');
+ if (typeof folder_id === 'undefined') {
+ return false;
+ } else if (folder_id === '0'){
+ library_router.navigate('#', {trigger: true, replace: true});
+ } else {
+ library_router.navigate('folders/' + folder_id, {trigger: true, replace: true});
+ }
+ },
+
+ //show modal with current dataset info
+ showDatasetDetails : function(event){
+ // prevent default
+ event.preventDefault();
+
+//TODO check whether we already have the data
+
+ //load the ID of the row
+ var id = $(event.target).parent().parent().attr('id');
+
+ //create new item
+ var item = new Item();
+ var histories = new GalaxyHistories();
+ item.id = id;
+ var self = this;
+
+ //fetch the dataset info
+ item.fetch({
+ success: function (item) {
+// TODO can render here already
+ //fetch user histories for import purposes
+ histories.fetch({
+ success: function (histories){self.renderModalAfterFetch(item, histories)}
+ });
+ }
+ });
+ },
+
+ // show the current dataset in a modal
+ renderModalAfterFetch : function(item, histories){
+ var size = this.size_to_string(item.get('file_size'));
+ var template = _.template(this.templateDatasetModal(), { item : item, size : size });
+ this.modal = null;
+ // make modal
+ var self = this;
+ this.modal = new mod_modal.GalaxyModal({
+ title : 'Dataset Details',
+ body : template,
+ buttons : {
+ 'Import' : function() { self.importCurrentIntoHistory() },
+ 'Download' : function() { self.downloadCurrent() },
+ 'Close' : function() { self.modal.hide(); $('.modal').remove(); self.modal = null; } // TODO refill nicely modal with data
+ }
+ });
+ $(".peek").html(item.get("peek"));
+ var history_footer_tmpl = _.template(this.templateHistorySelectInModal(), {histories : histories.models});
+ $(this.modal.elMain).find('.buttons').prepend(history_footer_tmpl);
+
+ // preset last selected history if we know it
+ if (self.lastSelectedHistory.length > 0) {
+ $(this.modal.elMain).find('#dataset_import_single').val(self.lastSelectedHistory);
+ }
+
+ // show the prepared modal
+ this.modal.show();
+ },
+
+ // download dataset shown currently in modal
+ downloadCurrent : function(){
+ //disable the buttons
+ this.modal.disableButton('Import');
+ this.modal.disableButton('Download');
+
+ var library_dataset_id = [];
+ library_dataset_id.push($('#id_row').attr('data-id'));
+ var url = '/api/libraries/datasets/download/uncompressed';
+ var data = {'ldda_ids' : library_dataset_id};
+
+ // we assume the view is existent
+ folderContentView.processDownload(url, data);
+ this.modal.enableButton('Import');
+ this.modal.enableButton('Download');
+ },
+
+ // import dataset shown currently in modal into selected history
+ importCurrentIntoHistory : function(){
+ //disable the buttons
+ this.modal.disableButton('Import');
+ this.modal.disableButton('Download');
+
+ var history_id = $(this.modal.elMain).find('select[name=dataset_import_single] option:selected').val();
+ this.lastSelectedHistory = history_id; //save selected history for further use
+
+ var library_dataset_id = $('#id_row').attr('data-id');
+ var historyItem = new HistoryItem();
+ var self = this;
+ historyItem.url = historyItem.urlRoot + history_id + '/contents';
+
+ // save the dataset into selected history
+ historyItem.save({ content : library_dataset_id, source : 'library' }, { success : function(){
+ self.modal.showNotification('Dataset imported', 3000, 'success');
+ //enable the buttons
+ self.modal.enableButton('Import');
+ self.modal.enableButton('Download');
+ }, error : function(){
+ self.modal.showNotification('An error occured! Dataset not imported. Please try again later.', 5000, 'error');
+ //enable the buttons
+ self.modal.enableButton('Import');
+ self.modal.enableButton('Download');
+ }
+ });
+ },
+
+ // select all datasets
+ selectAll : function (event) {
+ var selected = event.target.checked;
+ // Iterate each checkbox
+ $(':checkbox').each(function () { this.checked = selected; });
+ this.showTools();
+ },
+
+ // click checkbox on folder click
+ selectClicked : function (event) {
+ var checkbox = $("#" + event.target.parentElement.id).find(':checkbox')
+ if (checkbox[0] != undefined) {
+ if (checkbox[0].checked){
+ checkbox[0].checked = '';
+ // $(event.target.parentElement).css('background-color', '').css('color', '');
+ $(event.target.parentElement).removeClass('dark');
+ $(event.target.parentElement).find('a').removeClass('dark');
+ $(event.target.parentElement).addClass('light');
+ $(event.target.parentElement).find('a').addClass('light');
+ } else {
+ checkbox[0].checked = 'selected';
+ $(event.target.parentElement).removeClass('light');
+ $(event.target.parentElement).find('a').removeClass('light');
+ $(event.target.parentElement).addClass('dark');
+ $(event.target.parentElement).find('a').addClass('dark');
+ // $(event.target.parentElement).css('background-color', '#8389a1').css('color', 'white');
+ }
+ }
+ this.showTools();
+ },
+
+ // show toolbar in case something is selected
+ showTools : function(){
+ var checkedValues = $('#folder_table').find(':checked');
+ if(checkedValues.length > 0){
+ $('#toolbtn_bulk_import').show();
+ $('#toolbtn_dl').show();
+ } else {
+ $('#toolbtn_bulk_import').hide();
+ $('#toolbtn_dl').hide();
+ }
+
+ },
+
+ // show bulk import modal
+ modalBulkImport : function(){
+ var self = this;
+ // fetch histories
+ var histories = new GalaxyHistories();
+ histories.fetch({
+ success: function (histories){
+ // make modal
+ var history_modal_tmpl = _.template(self.templateBulkImportInModal(), {histories : histories.models});
+ self.modal = new mod_modal.GalaxyModal({
+ title : 'Import into History',
+ body : history_modal_tmpl,
+ buttons : {
+ 'Import' : function() {self.importAllIntoHistory()},
+ 'Close' : function() {self.modal.hide(); $('.modal').remove(); self.modal = null;}
+ }
+ });
+ // show the prepared modal
+ self.modal.show();
+ }
+ });
+ },
+
+ // import all selected datasets into history
+ importAllIntoHistory : function (){
+ //disable the button
+ this.modal.disableButton('Import');
+
+ var history_id = $("select[name=dataset_import_bulk] option:selected").val();
+ var history_name = $("select[name=dataset_import_bulk] option:selected").text();
+
+ var dataset_ids = [];
+ $('#folder_table').find(':checked').each(function(){
+ if (this.parentElement.parentElement.id != '') {
+ dataset_ids.push(this.parentElement.parentElement.id);
+ }
+ });
+ var progress_bar_tmpl = _.template(this.templateProgressBar(), { history_name : history_name });
+ $(this.modal.elMain).find('.modal-body').html(progress_bar_tmpl);
+
+ // init the progress bar
+ var progressStep = 100 / dataset_ids.length;
+ this.initProgress(progressStep);
+
+ // prepare the dataset objects to be imported
+ var datasets_to_import = [];
+ for (var i = dataset_ids.length - 1; i >= 0; i--) {
+ library_dataset_id = dataset_ids[i];
+ var historyItem = new HistoryItem();
+ var self = this;
+ historyItem.url = historyItem.urlRoot + history_id + '/contents';
+ historyItem.content = library_dataset_id;
+ historyItem.source = 'library';
+ datasets_to_import.push(historyItem);
+ };
+
+ // call the recursive function to call ajax one after each other
+ this.chainCall(datasets_to_import);
+ },
+
+ chainCall: function(history_item_set){
+ var self = this;
+ var popped_item = history_item_set.pop();
+ if (typeof popped_item === "undefined") {
+ self.modal.showNotification('All datasets imported', 3000, 'success');
+ // enable button again
+ self.modal.enableButton('Import');
+ return
+ }
+ var promise = $.when(popped_item.save({content: popped_item.content, source: popped_item.source})).done(function(a1){
+ self.updateProgress();
+ responses.push(a1);
+ self.chainCall(history_item_set);
+ });
+ },
+
+ initProgress: function(progressStep){
+ this.progress = 0;
+ this.progressStep = progressStep;
+ },
+ updateProgress: function(){
+ this.progress += this.progressStep;
+ $('.progress-bar').width(Math.round(this.progress) + '%');
+ txt_representation = Math.round(this.progress) + '% Complete';
+ $('.completion_span').text(txt_representation);
+ },
+
+ // progress bar
+ templateProgressBar : function (){
+ var tmpl_array = [];
+
+ tmpl_array.push('<div class="import_text">');
+ tmpl_array.push('Importing selected datasets to history <b><%= _.escape(history_name) %></b>');
+ tmpl_array.push('</div>');
+ tmpl_array.push('<div class="progress">');
+ tmpl_array.push(' <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');
+ tmpl_array.push(' <span class="completion_span">0% Complete</span>');
+ tmpl_array.push(' </div>');
+ tmpl_array.push('</div>');
+ tmpl_array.push('');
+
+ return tmpl_array.join('');
+ },
+
+ // download selected datasets
+ download : function(folder_id, format){
+ var dataset_ids = [];
+ $('#folder_table').find(':checked').each(function(){
+ if (this.parentElement.parentElement.id != '') {
+ dataset_ids.push(this.parentElement.parentElement.id);
+ }
+ });
+
+ var url = '/api/libraries/datasets/download/' + format;
+ var data = {'ldda_ids' : dataset_ids};
+ this.processDownload(url, data);
+ },
+
+ // create hidden form and submit through POST to initialize download
+ processDownload: function(url, data, method){
+ //url and data options required
+ if( url && data ){
+ //data can be string of parameters or array/object
+ data = typeof data == 'string' ? data : $.param(data);
+ //split params into form inputs
+ var inputs = '';
+ $.each(data.split('&'), function(){
+ var pair = this.split('=');
+ inputs+='<input type="hidden" name="'+ pair[0] +'" value="'+ pair[1] +'" />';
+ });
+ //send request
+ $('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
+ .appendTo('body').submit().remove();
+ };
+ },
+
+ // shows modal for creating folder
+ createFolderModal: function(){
+ alert('creating folder');
+ }
+
+ });
+
+// galaxy library view
+var GalaxyLibraryview = Backbone.View.extend({
+ el: '#center',
+
+ events: {
+ 'click #create_new_library_btn' : 'show_library_modal'
+ },
+
+ // initialize
+ initialize : function(){
+ },
+
+ // template
+ template_library_list : function (){
+ tmpl_array = [];
+ tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; overflow: auto !important; ">');
+
+ tmpl_array.push('');
+ tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');
+ tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary icon-file ">New Library</a>');
+ tmpl_array.push('<table class="table table-condensed">');
+ tmpl_array.push(' <thead>');
+ tmpl_array.push(' <th class="button_heading"></th>');
+ tmpl_array.push(' <th>name</th>');
+ tmpl_array.push(' <th>description</th>');
+ tmpl_array.push(' <th>synopsis</th> ');
+ tmpl_array.push(' <th>model type</th> ');
+ tmpl_array.push(' </thead>');
+ tmpl_array.push(' <tbody>');
+ tmpl_array.push(' <% _.each(libraries, function(library) { %>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <td><button title="Open this library" type="button" data-id="<%- library.get("root_folder_id") %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-icon-folder-open"></span> browse</td>');
+ tmpl_array.push(' <td><%- library.get("name") %></td>');
+ tmpl_array.push(' <td><%= _.escape(library.get("description")) %></td>');
+ tmpl_array.push(' <td><%= _.escape(library.get("synopsis")) %></td>');
+ tmpl_array.push(' <td><%= _.escape(library.get("model_class")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push(' </tbody>');
+ tmpl_array.push('</table>');
+
+ tmpl_array.push('</div>');
+ return tmpl_array.join('');
+ },
+
+ // render
+ render: function () {
+ //hack to show scrollbars
+ $("#center").css('overflow','auto');
+ var that = this;
+ // if (typeof libraries === "undefined") {
+ libraries = new Libraries();
+ // }
+ libraries.fetch({
+ success: function (libraries) {
+ var template = _.template(that.template_library_list(), { libraries : libraries.models });
+ that.$el.html(template);
+ }
+ })
+ },
+
+ // own modal
+ modal : null,
+
+ // show/hide create library modal
+ show_library_modal : function (event){
+ event.preventDefault();
+ event.stopPropagation();
+
+ // create modal
+ var self = this;
+ this.modal = new mod_modal.GalaxyModal(
+ {
+ title : 'Create New Library',
+ body : this.template_new_library(),
+ buttons : {
+ 'Create' : function() {self.create_new_library_event()},
+ 'Close' : function() {self.modal.hide()}
+ }
+ });
+
+ // show prepared modal
+ this.modal.show();
+ },
+
+ // create the new library from modal
+ create_new_library_event: function(){
+ var libraryDetails = this.serialize_new_library();
+ var valid = this.validate_new_library(libraryDetails);
+ var library = new Library();
+ var self = this;
+ library.save(libraryDetails, {
+ success: function (library) {
+ self.modal.hide();
+ self.clear_library_modal();
+ self.render();
+ },
+ error: function(){
+ self.modal.showNotification('An error occured', 5000, 'error');
+ }
+ });
+ return false;
+ },
+
+ // clear the library modal once saved
+ clear_library_modal : function(){
+ $("input[name='Name']").val('');
+ $("input[name='Description']").val('');
+ $("input[name='Synopsis']").val('');
+ },
+
+ // serialize data from the form
+ serialize_new_library : function(){
+ return {
+ name: $("input[name='Name']").val(),
+ description: $("input[name='Description']").val(),
+ synopsis: $("input[name='Synopsis']").val()
+ };
+ },
+
+ validate_new_library: function(library){
+
+ },
+
+
+ // template for new library modal
+ template_new_library: function()
+ {
+ tmpl_array = [];
+
+ tmpl_array.push('<div id="new_library_modal">');
+ tmpl_array.push('<form>');
+ tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');
+ tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');
+ tmpl_array.push('<input type="text" name="Synopsis" value="" placeholder="Synopsis">');
+ tmpl_array.push('</form>');
+ tmpl_array.push('</div>');
+
+ return tmpl_array.join('');
+ }
+});
+
+// galaxy library wrapper View
+var GalaxyLibrary = Backbone.View.extend({
+ folderContentView : null,
+ galaxyLibraryview : null,
+ initialize : function(){
+
+ folderContentView = new FolderContentView();
+ galaxyLibraryview = new GalaxyLibraryview();
+
+ library_router = new LibraryRouter();
+
+ library_router.on('route:libraries', function() {
+ // render libraries list
+ galaxyLibraryview.render();
+ });
+
+ library_router.on('route:folder_content', function(id) {
+ // render folder's contents
+ folderContentView.render({id: id});
+ });
+
+ library_router.on('route:download', function(folder_id, format) {
+ // send download stream
+ if (typeof folderContentView === 'undefined'){
+ alert('you cant touch this!');
+ // } else if (folderContentView.modal !== null){
+ // folderContentView.download(folder_id, format);
+ } else if ($('#center').find(':checked').length === 0) { // coming from outside of the library app
+ library_router.navigate('folders/' + folder_id, {trigger: true, replace: true});
+ // TODO show message of redirection
+ } else {
+ folderContentView.download(folder_id, format);
+ library_router.navigate('folders/' + folder_id, {trigger: false, replace: true});
+ }
+
+ });
+
+Backbone.history.start();
+
+return this
+}
+});
+
+// return
+return {
+ GalaxyApp: GalaxyLibrary
+};
+
+});
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 static/scripts/galaxy.menu.js
--- a/static/scripts/galaxy.menu.js
+++ b/static/scripts/galaxy.menu.js
@@ -57,7 +57,12 @@
tab_shared.add({
title : "Data Libraries",
- content : "library/index",
+ content : "library/index"
+ });
+
+ tab_shared.add({
+ title : "New Libraries",
+ content : "library/list",
divider : true
});
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -1,6 +1,3 @@
-/*
- galaxy modal
-*/
// dependencies
define([], function() {
@@ -25,8 +22,51 @@
// initialize
initialize : function(options) {
+ self = this;
if (options)
this.create(options);
+
+ this.bindClick(event, self);
+ },
+
+ // bind the click-to-hide function
+ bindClick: function(event, that) {
+ // bind the ESC key to hide() function
+ $(document).on('keyup', function(event){
+ if (event.keyCode == 27) { self.hide(); }
+ })
+ // bind the click anywhere to hide() function...
+ $('html').on('click', function(event){
+ that.hide();
+ })
+ // ...but don't hide if the click is on modal content
+ $('.modal-content').on('click', function(event){
+ event.stopPropagation();
+ })
+ },
+
+ // unbind the click-to-hide function
+ unbindClick: function(event, that){
+ // bind the ESC key to hide() function
+ $(document).off('keyup', function(event){
+ if (event.keyCode == 27) { that.hide(); }
+ })
+ // unbind the click anywhere to hide() function...
+ $('html').off('click', function(event){
+ that.hide();
+ })
+ // ...but don't hide if the click is on modal content
+ $('.modal-content').off('click', function(event){
+ event.stopPropagation();
+ })
+ },
+
+
+ // destroy
+ destroy : function(){
+ this.hide();
+ this.unbindClick();
+ $('.modal').remove();
},
// adds and displays a new frame/window
@@ -87,6 +127,7 @@
this.$footer = (this.$el).find('.modal-footer');
this.$buttons = (this.$el).find('.buttons');
this.$backdrop = (this.$el).find('.modal-backdrop');
+ this.$notification = (this.$el).find('.notification-modal');
// append body
this.$body.html(this.options.body);
@@ -120,6 +161,47 @@
this.$buttons.find('#' + String(name).toLowerCase()).prop('disabled', true);
},
+ // hide buttons
+ hideButton: function(name) {
+ this.$buttons.find('#' + String(name).toLowerCase()).hide();
+ },
+ // show buttons
+ showButton: function(name) {
+ this.$buttons.find('#' + String(name).toLowerCase()).show();
+ },
+
+ // show notification
+ showNotification : function(message, duration, type) {
+ // defaults
+ var duration = typeof duration !== 'undefined' ? duration : 1500;
+ // var bgColor = typeof bgColor !== 'undefined' ? bgColor : "#F4E0E1";
+ // var txtColor = typeof txtColor !== 'undefined' ? txtColor : "#A42732";
+ var bgColor;
+ var txtColor;
+
+ if (type === 'error'){
+ bgColor = '#f4e0e1';
+ txtColor = '#a42732';
+ // } else if (type === 'success'){
+ } else { // success is default
+ bgColor = '#e1f4e0';
+ txtColor = '#32a427';
+ }
+
+ var HTMLmessage = "<div class='notification-message' style='text-align:center; line-height:16px; '> " + message + " </div>";
+ this.$notification.html("<div id='notification-bar' style='display:none; float: right; height: 16px; width:100%; background-color: " + bgColor + "; z-index: 100; color: " + txtColor + ";border-bottom: 1px solid " + txtColor + ";'>" + HTMLmessage + "</div>");
+
+ var self = this;
+
+ /*animate the bar*/
+ $('#notification-bar').slideDown(function() {
+ setTimeout(function() {
+ $('#notification-bar').slideUp(function() {self.$notification.html('');});
+ }, duration);
+ });
+
+ },
+
// returns scroll top for body element
scrollTop: function()
{
@@ -139,7 +221,8 @@
'<div class="modal-header">' +
'<button type="button" class="close" style="display: none;">×</button>' +
'<h4 class="title">' + title + '</h4>' +
- '</div>' +
+ '<span class="notification-modal"></span>' +
+ '</div>' +
'<div class="modal-body"></div>' +
'<div class="modal-footer">' +
'<div class="buttons" style="float: right;"></div>' +
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 static/scripts/libs/bootstrap.js
--- a/static/scripts/libs/bootstrap.js
+++ b/static/scripts/libs/bootstrap.js
@@ -575,3 +575,158 @@
}
}(window.jQuery);
+
+/* ========================================================================
+ * Bootstrap: dropdown.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#dropdowns
+ * ========================================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // DROPDOWN CLASS DEFINITION
+ // =========================
+
+ var backdrop = '.dropdown-backdrop'
+ var toggle = '[data-toggle=dropdown]'
+ var Dropdown = function (element) {
+ var $el = $(element).on('click.bs.dropdown', this.toggle)
+ }
+
+ Dropdown.prototype.toggle = function (e) {
+ var $this = $(this)
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ clearMenus()
+
+ if (!isActive) {
+ if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
+ // if mobile we we use a backdrop because click events don't delegate
+ $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
+ }
+
+ $parent.trigger(e = $.Event('show.bs.dropdown'))
+
+ if (e.isDefaultPrevented()) return
+
+ $parent
+ .toggleClass('open')
+ .trigger('shown.bs.dropdown')
+
+ $this.focus()
+ }
+
+ return false
+ }
+
+ Dropdown.prototype.keydown = function (e) {
+ if (!/(38|40|27)/.test(e.keyCode)) return
+
+ var $this = $(this)
+
+ e.preventDefault()
+ e.stopPropagation()
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ if (!isActive || (isActive && e.keyCode == 27)) {
+ if (e.which == 27) $parent.find(toggle).focus()
+ return $this.click()
+ }
+
+ var $items = $('[role=menu] li:not(.divider):visible a', $parent)
+
+ if (!$items.length) return
+
+ var index = $items.index($items.filter(':focus'))
+
+ if (e.keyCode == 38 && index > 0) index-- // up
+ if (e.keyCode == 40 && index < $items.length - 1) index++ // down
+ if (!~index) index=0
+
+ $items.eq(index).focus()
+ }
+
+ function clearMenus() {
+ $(backdrop).remove()
+ $(toggle).each(function (e) {
+ var $parent = getParent($(this))
+ if (!$parent.hasClass('open')) return
+ $parent.trigger(e = $.Event('hide.bs.dropdown'))
+ if (e.isDefaultPrevented()) return
+ $parent.removeClass('open').trigger('hidden.bs.dropdown')
+ })
+ }
+
+ function getParent($this) {
+ var selector = $this.attr('data-target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ var $parent = selector && $(selector)
+
+ return $parent && $parent.length ? $parent : $this.parent()
+ }
+
+
+ // DROPDOWN PLUGIN DEFINITION
+ // ==========================
+
+ var old = $.fn.dropdown
+
+ $.fn.dropdown = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('dropdown')
+
+ if (!data) $this.data('dropdown', (data = new Dropdown(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ $.fn.dropdown.Constructor = Dropdown
+
+
+ // DROPDOWN NO CONFLICT
+ // ====================
+
+ $.fn.dropdown.noConflict = function () {
+ $.fn.dropdown = old
+ return this
+ }
+
+
+ // APPLY TO STANDARD DROPDOWN ELEMENTS
+ // ===================================
+
+ $(document)
+ .on('click.bs.dropdown.data-api', clearMenus)
+ .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+ .on('click.bs.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
+ .on('keydown.bs.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
+
+}(window.jQuery);
\ No newline at end of file
diff -r 206055969b7cedcbfcac36c19428b9547cd5cb5d -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 static/style/src/less/library.less
--- a/static/style/src/less/library.less
+++ b/static/style/src/less/library.less
@@ -29,6 +29,50 @@
background-color: @table-bg-accent;
}
+
+tr.light td
+{
+ background-color: white;
+ color: black;
+}
+tr.light:hover td
+{
+ background-color: #f5f5f5;
+ color: #8389a1;
+}
+tr.dark td
+{
+ background-color: #8389a1;
+ color: white;
+}
+tr.dark:hover td
+{
+ background-color: #bbbfd0;
+ color: white;
+}
+a.dark:hover
+{
+ color: yellow;
+ // text-decoration: none;
+}
+a.dark
+{
+ color: white;
+ // text-decoration: none;
+}
+th.button_heading
+{
+ width: 7em;
+}
+div.library_breadcrumb{
+ padding-top: 0.8em;
+ padding-bottom: 0.8em;
+}
+div.library_breadcrumb a:hover{
+ color:green;
+}
+
+
img.expanderIcon {
padding-right: 4px;
}
https://bitbucket.org/galaxy/galaxy-central/commits/caba1617084b/
Changeset: caba1617084b
User: martenson
Date: 2013-12-11 20:24:58
Summary: cleaning zip logic, jmchilton pull rq #208
Affected #: 1 file
diff -r ff102054eec10f7bd1ad9f9663e1884dc7c75223 -r caba1617084b4a0d38f1ebce970dda675755cd1d lib/galaxy/webapps/galaxy/api/lda_datasets.py
--- a/lib/galaxy/webapps/galaxy/api/lda_datasets.py
+++ b/lib/galaxy/webapps/galaxy/api/lda_datasets.py
@@ -21,42 +21,6 @@
import logging
log = logging.getLogger( __name__ )
-# Test for available compression types
-# tmpd = tempfile.mkdtemp()
-# comptypes = []
-# for comptype in ( 'gz', 'bz2' ):
-# tmpf = os.path.join( tmpd, 'compression_test.tar.' + comptype )
-# try:
-# archive = tarfile.open( tmpf, 'w:' + comptype )
-# archive.close()
-# comptypes.append( comptype )
-# except tarfile.CompressionError:
-# log.exception( "Compression error when testing %s compression. This option will be disabled for library downloads." % comptype )
-# try:
-# os.unlink( tmpf )
-# except OSError:
-# pass
-ziptype = '32'
-# tmpf = os.path.join( tmpd, 'compression_test.zip' )
-# try:
-# archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True )
-# archive.close()
-# comptypes.append( 'zip' )
-# ziptype = '64'
-# except RuntimeError:
-# log.exception( "Compression error when testing zip compression. This option will be disabled for library downloads." )
-# except (TypeError, zipfile.LargeZipFile):
-# # ZIP64 is only in Python2.5+. Remove TypeError when 2.4 support is dropped
-# log.warning( 'Max zip file size is 2GB, ZIP64 not supported' )
-# comptypes.append( 'zip' )
-# try:
-# os.unlink( tmpf )
-# except OSError:
-# pass
-# os.rmdir( tmpd )
-
-
-
class DatasetsController( BaseAPIController, UsesLibraryMixinItems ):
@web.expose_api
@@ -77,13 +41,13 @@
rval = "Error in dataset API at listing contents: " + str( e )
log.error( rval + ": %s" % str(e), exc_info=True )
trans.response.status = 500
-
+
rval['id'] = trans.security.encode_id(rval['id']);
rval['ldda_id'] = trans.security.encode_id(rval['ldda_id']);
rval['folder_id'] = 'f' + trans.security.encode_id(rval['folder_id'])
-
+
return rval
-
+
@web.expose
def download( self, trans, format, **kwd ):
"""
@@ -94,9 +58,9 @@
lddas = []
# is_admin = trans.user_is_admin()
# current_user_roles = trans.get_current_user_roles()
-
+
datasets_to_download = kwd['ldda_ids%5B%5D']
-
+
if ( datasets_to_download != None ):
datasets_to_download = util.listify( datasets_to_download )
for dataset_id in datasets_to_download:
@@ -107,7 +71,7 @@
except:
ldda = None
message += "Invalid library dataset id (%s) specified. " % str( dataset_id )
-
+
if format in [ 'zip','tgz','tbz' ]:
error = False
killme = string.punctuation + string.whitespace
@@ -119,14 +83,10 @@
tmpd = tempfile.mkdtemp()
util.umask_fix_perms( tmpd, trans.app.config.umask, 0777, self.app.config.gid )
tmpf = os.path.join( tmpd, 'library_download.' + format )
- if ziptype == '64' and trans.app.config.upstream_gzip:
+ if trans.app.config.upstream_gzip:
archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED, True )
- elif ziptype == '64':
+ else:
archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True )
- elif trans.app.config.upstream_gzip:
- archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED )
- else:
- archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED )
archive.add = lambda x, y: archive.write( x, y.encode('CP437') )
elif format == 'tgz':
if trans.app.config.upstream_gzip:
@@ -238,4 +198,4 @@
except:
return 'This dataset contains no content'
else:
- return 'Wrong format';
\ No newline at end of file
+ return 'Wrong format';
https://bitbucket.org/galaxy/galaxy-central/commits/c653272fdbb3/
Changeset: c653272fdbb3
User: martenson
Date: 2013-12-11 21:31:20
Summary: trimming whitespace, import library.less into base.less
Affected #: 2 files
diff -r caba1617084b4a0d38f1ebce970dda675755cd1d -r c653272fdbb3e2fcc8e5dd83b2c71df8bd9b5c2a static/scripts/galaxy.library.js
--- a/static/scripts/galaxy.library.js
+++ b/static/scripts/galaxy.library.js
@@ -7,16 +7,6 @@
var library_router = null;
var responses = [];
-
-// load required libraries
-// require([
-// // load js libraries
-// 'utils/galaxy.css',
-// ], function(css){
-// // load css
-// css.load_file("static/style/library.css");
-// });
-
// dependencies
define(["galaxy.modal", "galaxy.masthead", "utils/galaxy.utils"], function(mod_modal, mod_masthead, mod_utils) {
@@ -42,7 +32,7 @@
// FOLDER
var Folder = Backbone.Collection.extend({
- model: Item
+ model: Item
})
// CONTAINER for folder contents (folders, items and metadata).
@@ -78,7 +68,7 @@
});
//ROUTER
- var LibraryRouter = Backbone.Router.extend({
+ var LibraryRouter = Backbone.Router.extend({
routes: {
"" : "libraries",
"folders/:id" : "folder_content",
@@ -98,26 +88,26 @@
// progress percentage
progress: 0,
// progress rate per one item
- progressStep: 1,
+ progressStep: 1,
// last selected history in modal for UX
lastSelectedHistory: '',
// self modal
modal : null,
// loaded folders
folders : null,
-
+
// initialize
initialize : function(){
this.folders = [];
this.queue = jQuery.Deferred();
this.queue.resolve();
- },
+ },
// MMMMMMMMMMMMMMMMMM
// === TEMPLATES ====
// MMMMMMMMMMMMMMMMMM
- // set up
+ // set up
templateFolder : function (){
var tmpl_array = [];
@@ -127,12 +117,12 @@
// TOOLBAR
tmpl_array.push('<div id="library_folder_toolbar" >');
- tmpl_array.push(' <button title="Create New Folder" id="toolbtn_create_folder" class="btn btn-primary" type="button"><span class="fa fa-icon-plus"></span><span class="fa fa-icon-folder-close"></span> folder</button>');
- tmpl_array.push(' <button id="toolbtn_bulk_import" class="btn btn-primary" style="display: none; margin-left: 0.5em;" type="button"><span class="fa fa-icon-external-link"></span> to history</button>');
-
+ tmpl_array.push(' <button title="Create New Folder" id="toolbtn_create_folder" class="btn btn-primary" type="button"><span class="fa fa-plus"></span><span class="fa fa-folder-close"></span> folder</button>');
+ tmpl_array.push(' <button id="toolbtn_bulk_import" class="btn btn-primary" style="display: none; margin-left: 0.5em;" type="button"><span class="fa fa-external-link"></span> to history</button>');
+
tmpl_array.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; display: none; ">');
tmpl_array.push(' <button id="drop_toggle" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">');
- tmpl_array.push(' <span class="fa fa-icon-download"></span> download <span class="caret"></span>');
+ tmpl_array.push(' <span class="fa fa-download"></span> download <span class="caret"></span>');
tmpl_array.push(' </button>');
tmpl_array.push(' <ul class="dropdown-menu" role="menu">');
tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');
@@ -166,20 +156,20 @@
tmpl_array.push(' <th>date</th>');
tmpl_array.push(' </thead>');
tmpl_array.push(' <tbody>');
- tmpl_array.push(' <td></td>');
- tmpl_array.push(' <td><button title="Go to parent folder" type="button" data-id="<%- upper_folder_id %>" class="btn_open_folder btn btn-default btn-xs">');
- tmpl_array.push(' <span class="fa fa-icon-arrow-up"></span> .. go up</td>');
- tmpl_array.push(' <td></td>');
- tmpl_array.push(' <td></td>');
- tmpl_array.push(' <td></td>');
- tmpl_array.push(' <td></td>');
- tmpl_array.push(' </tr>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td><button title="Go to parent folder" type="button" data-id="<%- upper_folder_id %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-arrow-up"></span> .. go up</td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' </tr>');
tmpl_array.push(' <% _.each(items, function(content_item) { %>');
tmpl_array.push(' <tr class="folder_row light" id="<%- content_item.id %>">');
tmpl_array.push(' <% if (content_item.get("type") === "folder") { %>'); // folder
tmpl_array.push(' <td></td>');
tmpl_array.push(' <td><button title="Open this folder" type="button" data-id="<%- content_item.id %>" class="btn_open_folder btn btn-default btn-xs">');
- tmpl_array.push(' <span class="fa fa-icon-folder-open"></span> browse</td>');
+ tmpl_array.push(' <span class="fa fa-folder-open"></span> browse</td>');
tmpl_array.push(' <td><%- content_item.get("name") %>');
tmpl_array.push(' <% if (content_item.get("item_count") === 0) { %>'); // empty folder
tmpl_array.push(' <span class="muted">(empty folder)</span>');
@@ -191,7 +181,7 @@
tmpl_array.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');
tmpl_array.push(' <td>');
tmpl_array.push(' <button title="See details of this dataset" type="button" class="library-dataset btn btn-default btn-xs">');
- tmpl_array.push(' <span class="fa fa-icon-eye-open"></span> details');
+ tmpl_array.push(' <span class="fa fa-eye"></span> details');
tmpl_array.push(' </button>');
tmpl_array.push(' </td>');
tmpl_array.push(' <td><%- content_item.get("name") %></td>'); // dataset
@@ -241,11 +231,11 @@
tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_data_lines")) %></td>');
tmpl_array.push(' </tr>');
tmpl_array.push(' <th scope="row">Comment Lines</th>');
- tmpl_array.push(' <% if (item.get("metadata_comment_lines") === "") { %>'); //folder
+ tmpl_array.push(' <% if (item.get("metadata_comment_lines") === "") { %>'); //folder
tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_comment_lines")) %></td>');
- tmpl_array.push(' <% } else { %>');
- tmpl_array.push(' <td scope="row">unknown</td>');
- tmpl_array.push(' <% } %>');
+ tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td scope="row">unknown</td>');
+ tmpl_array.push(' <% } %>');
tmpl_array.push(' </tr>');
tmpl_array.push(' <tr>');
tmpl_array.push(' <th scope="row">Number of Columns</th>');
@@ -280,7 +270,7 @@
tmpl_array.push('</span>');
return tmpl_array.join('');
- },
+ },
templateBulkImportInModal : function(){
var tmpl_array = [];
@@ -295,7 +285,7 @@
tmpl_array.push('</span>');
return tmpl_array.join('');
- },
+ },
// convert size to nice string
size_to_string : function (size)
@@ -380,12 +370,12 @@
showDatasetDetails : function(event){
// prevent default
event.preventDefault();
-
+
//TODO check whether we already have the data
//load the ID of the row
var id = $(event.target).parent().parent().attr('id');
-
+
//create new item
var item = new Item();
var histories = new GalaxyHistories();
@@ -474,12 +464,12 @@
self.modal.showNotification('An error occured! Dataset not imported. Please try again later.', 5000, 'error');
//enable the buttons
self.modal.enableButton('Import');
- self.modal.enableButton('Download');
+ self.modal.enableButton('Download');
}
});
},
- // select all datasets
+ // select all datasets
selectAll : function (event) {
var selected = event.target.checked;
// Iterate each checkbox
@@ -562,7 +552,7 @@
});
var progress_bar_tmpl = _.template(this.templateProgressBar(), { history_name : history_name });
$(this.modal.elMain).find('.modal-body').html(progress_bar_tmpl);
-
+
// init the progress bar
var progressStep = 100 / dataset_ids.length;
this.initProgress(progressStep);
@@ -610,7 +600,7 @@
$('.completion_span').text(txt_representation);
},
- // progress bar
+ // progress bar
templateProgressBar : function (){
var tmpl_array = [];
@@ -644,14 +634,14 @@
// create hidden form and submit through POST to initialize download
processDownload: function(url, data, method){
//url and data options required
- if( url && data ){
+ if( url && data ){
//data can be string of parameters or array/object
data = typeof data == 'string' ? data : $.param(data);
//split params into form inputs
var inputs = '';
- $.each(data.split('&'), function(){
+ $.each(data.split('&'), function(){
var pair = this.split('=');
- inputs+='<input type="hidden" name="'+ pair[0] +'" value="'+ pair[1] +'" />';
+ inputs+='<input type="hidden" name="'+ pair[0] +'" value="'+ pair[1] +'" />';
});
//send request
$('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
@@ -676,7 +666,7 @@
// initialize
initialize : function(){
- },
+ },
// template
template_library_list : function (){
@@ -685,7 +675,7 @@
tmpl_array.push('');
tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');
- tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary icon-file ">New Library</a>');
+ tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary file ">New Library</a>');
tmpl_array.push('<table class="table table-condensed">');
tmpl_array.push(' <thead>');
tmpl_array.push(' <th class="button_heading"></th>');
@@ -698,7 +688,7 @@
tmpl_array.push(' <% _.each(libraries, function(library) { %>');
tmpl_array.push(' <tr>');
tmpl_array.push(' <td><button title="Open this library" type="button" data-id="<%- library.get("root_folder_id") %>" class="btn_open_folder btn btn-default btn-xs">');
- tmpl_array.push(' <span class="fa fa-icon-folder-open"></span> browse</td>');
+ tmpl_array.push(' <span class="fa fa-folder-open"></span> browse</td>');
tmpl_array.push(' <td><%- library.get("name") %></td>');
tmpl_array.push(' <td><%= _.escape(library.get("description")) %></td>');
tmpl_array.push(' <td><%= _.escape(library.get("synopsis")) %></td>');
@@ -707,7 +697,7 @@
tmpl_array.push(' <% }); %>');
tmpl_array.push(' </tbody>');
tmpl_array.push('</table>');
-
+
tmpl_array.push('</div>');
return tmpl_array.join('');
},
@@ -735,7 +725,7 @@
show_library_modal : function (event){
event.preventDefault();
event.stopPropagation();
-
+
// create modal
var self = this;
this.modal = new mod_modal.GalaxyModal(
@@ -747,7 +737,7 @@
'Close' : function() {self.modal.hide()}
}
});
-
+
// show prepared modal
this.modal.show();
},
@@ -828,7 +818,7 @@
library_router.on('route:folder_content', function(id) {
// render folder's contents
folderContentView.render({id: id});
- });
+ });
library_router.on('route:download', function(folder_id, format) {
// send download stream
@@ -844,9 +834,9 @@
library_router.navigate('folders/' + folder_id, {trigger: false, replace: true});
}
- });
+ });
-Backbone.history.start();
+Backbone.history.start();
return this
}
diff -r caba1617084b4a0d38f1ebce970dda675755cd1d -r c653272fdbb3e2fcc8e5dd83b2c71df8bd9b5c2a static/style/src/less/base.less
--- a/static/style/src/less/base.less
+++ b/static/style/src/less/base.less
@@ -23,6 +23,7 @@
// galaxy sub-components
@import "frame.less";
@import "upload.less";
+@import "library.less";
// Mixins
https://bitbucket.org/galaxy/galaxy-central/commits/267dcfd58a60/
Changeset: 267dcfd58a60
User: martenson
Date: 2013-12-12 20:21:09
Summary: added library notifications, toaster, can be used outside of libraries as require.js module
Affected #: 7 files
diff -r c653272fdbb3e2fcc8e5dd83b2c71df8bd9b5c2a -r 267dcfd58a60d2de706d7c00c6fb6ec426eaaf5b static/scripts/galaxy.library.js
--- a/static/scripts/galaxy.library.js
+++ b/static/scripts/galaxy.library.js
@@ -8,7 +8,7 @@
var responses = [];
// dependencies
-define(["galaxy.modal", "galaxy.masthead", "utils/galaxy.utils"], function(mod_modal, mod_masthead, mod_utils) {
+define(["galaxy.modal", "galaxy.masthead", "utils/galaxy.utils", "libs/toastr"], function(mod_modal, mod_masthead, mod_utils, mod_toastr) {
// MMMMMMMMMMMMMMM
// === Models ====
@@ -706,18 +706,35 @@
render: function () {
//hack to show scrollbars
$("#center").css('overflow','auto');
+
var that = this;
- // if (typeof libraries === "undefined") {
- libraries = new Libraries();
- // }
+ libraries = new Libraries();
+
libraries.fetch({
success: function (libraries) {
var template = _.template(that.template_library_list(), { libraries : libraries.models });
that.$el.html(template);
- }
+ },
+ error: function(model, response){
+ if (response.statusCode().status === 403){
+ //show notification
+ // var toast = mod_toastr;
+ mod_toastr.error('Please log in first. Redirecting to login page in 3s.');
+ setTimeout(that.redirectToLogin, 3000);
+ } else {
+ library_router.navigate('#', {trigger: true, replace: true});
+ }
+ }
})
},
+ redirectToSplash: function(){
+ window.location = '../';
+ },
+ redirectToLogin: function(){
+ window.location = '/user/login';
+ },
+
// own modal
modal : null,
diff -r c653272fdbb3e2fcc8e5dd83b2c71df8bd9b5c2a -r 267dcfd58a60d2de706d7c00c6fb6ec426eaaf5b static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -172,10 +172,7 @@
// show notification
showNotification : function(message, duration, type) {
- // defaults
- var duration = typeof duration !== 'undefined' ? duration : 1500;
- // var bgColor = typeof bgColor !== 'undefined' ? bgColor : "#F4E0E1";
- // var txtColor = typeof txtColor !== 'undefined' ? txtColor : "#A42732";
+ var duration = typeof duration !== 'undefined' ? duration : 3000;
var bgColor;
var txtColor;
diff -r c653272fdbb3e2fcc8e5dd83b2c71df8bd9b5c2a -r 267dcfd58a60d2de706d7c00c6fb6ec426eaaf5b static/scripts/libs/toastr.js
--- /dev/null
+++ b/static/scripts/libs/toastr.js
@@ -0,0 +1,307 @@
+/*
+ * Toastr
+ * Version 2.0.1
+ * Copyright 2012 John Papa and Hans Fjällemark.
+ * All Rights Reserved.
+ * Use, reproduction, distribution, and modification of this code is subject to the terms and
+ * conditions of the MIT license, available at http://www.opensource.org/licenses/mit-license.php
+ *
+ * Author: John Papa and Hans Fjällemark
+ * Project: https://github.com/CodeSeven/toastr
+ */
+; (function (define) {
+ define([], function () {
+ var $ = jQuery;
+ return (function () {
+ var version = '2.0.1';
+ var $container;
+ var listener;
+ var toastId = 0;
+ var toastType = {
+ error: 'error',
+ info: 'info',
+ success: 'success',
+ warning: 'warning'
+ };
+
+ var toastr = {
+ clear: clear,
+ error: error,
+ getContainer: getContainer,
+ info: info,
+ options: {},
+ subscribe: subscribe,
+ success: success,
+ version: version,
+ warning: warning
+ };
+
+ return toastr;
+
+ //#region Accessible Methods
+ function error(message, title, optionsOverride) {
+ return notify({
+ type: toastType.error,
+ iconClass: getOptions().iconClasses.error,
+ message: message,
+ optionsOverride: optionsOverride,
+ title: title
+ });
+ }
+
+ function info(message, title, optionsOverride) {
+ return notify({
+ type: toastType.info,
+ iconClass: getOptions().iconClasses.info,
+ message: message,
+ optionsOverride: optionsOverride,
+ title: title
+ });
+ }
+
+ function subscribe(callback) {
+ listener = callback;
+ }
+
+ function success(message, title, optionsOverride) {
+ return notify({
+ type: toastType.success,
+ iconClass: getOptions().iconClasses.success,
+ message: message,
+ optionsOverride: optionsOverride,
+ title: title
+ });
+ }
+
+ function warning(message, title, optionsOverride) {
+ return notify({
+ type: toastType.warning,
+ iconClass: getOptions().iconClasses.warning,
+ message: message,
+ optionsOverride: optionsOverride,
+ title: title
+ });
+ }
+
+ function clear($toastElement) {
+ var options = getOptions();
+ if (!$container) { getContainer(options); }
+ if ($toastElement && $(':focus', $toastElement).length === 0) {
+ $toastElement[options.hideMethod]({
+ duration: options.hideDuration,
+ easing: options.hideEasing,
+ complete: function () { removeToast($toastElement); }
+ });
+ return;
+ }
+ if ($container.children().length) {
+ $container[options.hideMethod]({
+ duration: options.hideDuration,
+ easing: options.hideEasing,
+ complete: function () { $container.remove(); }
+ });
+ }
+ }
+ //#endregion
+
+ //#region Internal Methods
+
+ function getDefaults() {
+ return {
+ tapToDismiss: true,
+ toastClass: 'toast',
+ containerId: 'toast-container',
+ debug: false,
+
+ showMethod: 'fadeIn', //fadeIn, slideDown, and show are built into jQuery
+ showDuration: 300,
+ showEasing: 'swing', //swing and linear are built into jQuery
+ onShown: undefined,
+ hideMethod: 'fadeOut',
+ hideDuration: 1000,
+ hideEasing: 'swing',
+ onHidden: undefined,
+
+ extendedTimeOut: 1000,
+ iconClasses: {
+ error: 'toast-error',
+ info: 'toast-info',
+ success: 'toast-success',
+ warning: 'toast-warning'
+ },
+ iconClass: 'toast-info',
+ positionClass: 'toast-top-right',
+ timeOut: 5000, // Set timeOut and extendedTimeout to 0 to make it sticky
+ titleClass: 'toast-title',
+ messageClass: 'toast-message',
+ target: 'body',
+ closeHtml: '<button>×</button>',
+ newestOnTop: true
+ };
+ }
+
+ function publish(args) {
+ if (!listener) {
+ return;
+ }
+ listener(args);
+ }
+
+ function notify(map) {
+ var
+ options = getOptions(),
+ iconClass = map.iconClass || options.iconClass;
+
+ if (typeof (map.optionsOverride) !== 'undefined') {
+ options = $.extend(options, map.optionsOverride);
+ iconClass = map.optionsOverride.iconClass || iconClass;
+ }
+
+ toastId++;
+
+ $container = getContainer(options);
+ var
+ intervalId = null,
+ $toastElement = $('<div/>'),
+ $titleElement = $('<div/>'),
+ $messageElement = $('<div/>'),
+ $closeElement = $(options.closeHtml),
+ response = {
+ toastId: toastId,
+ state: 'visible',
+ startTime: new Date(),
+ options: options,
+ map: map
+ };
+
+ if (map.iconClass) {
+ $toastElement.addClass(options.toastClass).addClass(iconClass);
+ }
+
+ if (map.title) {
+ $titleElement.append(map.title).addClass(options.titleClass);
+ $toastElement.append($titleElement);
+ }
+
+ if (map.message) {
+ $messageElement.append(map.message).addClass(options.messageClass);
+ $toastElement.append($messageElement);
+ }
+
+ if (options.closeButton) {
+ $closeElement.addClass('toast-close-button');
+ $toastElement.prepend($closeElement);
+ }
+
+ $toastElement.hide();
+ if (options.newestOnTop) {
+ $container.prepend($toastElement);
+ } else {
+ $container.append($toastElement);
+ }
+
+
+ $toastElement[options.showMethod](
+ { duration: options.showDuration, easing: options.showEasing, complete: options.onShown }
+ );
+ if (options.timeOut > 0) {
+ intervalId = setTimeout(hideToast, options.timeOut);
+ }
+
+ $toastElement.hover(stickAround, delayedhideToast);
+ if (!options.onclick && options.tapToDismiss) {
+ $toastElement.click(hideToast);
+ }
+ if (options.closeButton && $closeElement) {
+ $closeElement.click(function (event) {
+ event.stopPropagation();
+ hideToast(true);
+ });
+ }
+
+ if (options.onclick) {
+ $toastElement.click(function () {
+ options.onclick();
+ hideToast();
+ });
+ }
+
+ publish(response);
+
+ if (options.debug && console) {
+ console.log(response);
+ }
+
+ return $toastElement;
+
+ function hideToast(override) {
+ if ($(':focus', $toastElement).length && !override) {
+ return;
+ }
+ return $toastElement[options.hideMethod]({
+ duration: options.hideDuration,
+ easing: options.hideEasing,
+ complete: function () {
+ removeToast($toastElement);
+ if (options.onHidden) {
+ options.onHidden();
+ }
+ response.state = 'hidden';
+ response.endTime = new Date(),
+ publish(response);
+ }
+ });
+ }
+
+ function delayedhideToast() {
+ if (options.timeOut > 0 || options.extendedTimeOut > 0) {
+ intervalId = setTimeout(hideToast, options.extendedTimeOut);
+ }
+ }
+
+ function stickAround() {
+ clearTimeout(intervalId);
+ $toastElement.stop(true, true)[options.showMethod](
+ { duration: options.showDuration, easing: options.showEasing }
+ );
+ }
+ }
+ function getContainer(options) {
+ if (!options) { options = getOptions(); }
+ $container = $('#' + options.containerId);
+ if ($container.length) {
+ return $container;
+ }
+ $container = $('<div/>')
+ .attr('id', options.containerId)
+ .addClass(options.positionClass);
+ $container.appendTo($(options.target));
+ return $container;
+ }
+
+ function getOptions() {
+ return $.extend({}, getDefaults(), toastr.options);
+ }
+
+ function removeToast($toastElement) {
+ if (!$container) { $container = getContainer(); }
+ if ($toastElement.is(':visible')) {
+ return;
+ }
+ $toastElement.remove();
+ $toastElement = null;
+ if ($container.children().length === 0) {
+ $container.remove();
+ }
+ }
+ //#endregion
+
+ })();
+ });
+}(typeof define === 'function' && define.amd ? define : function (deps, factory) {
+ if (typeof module !== 'undefined' && module.exports) { //Node
+ module.exports = factory(require(deps[0]));
+ } else {
+ window['toastr'] = factory(window['jQuery']);
+ }
+}));
diff -r c653272fdbb3e2fcc8e5dd83b2c71df8bd9b5c2a -r 267dcfd58a60d2de706d7c00c6fb6ec426eaaf5b static/scripts/utils/galaxy.uploadbox.js
--- a/static/scripts/utils/galaxy.uploadbox.js
+++ b/static/scripts/utils/galaxy.uploadbox.js
@@ -352,4 +352,4 @@
'compatible' : compatible
};
}
-})(jQuery);
\ No newline at end of file
+})(jQuery);
diff -r c653272fdbb3e2fcc8e5dd83b2c71df8bd9b5c2a -r 267dcfd58a60d2de706d7c00c6fb6ec426eaaf5b static/style/Gruntfile.js
--- a/static/style/Gruntfile.js
+++ b/static/style/Gruntfile.js
@@ -6,7 +6,7 @@
var theme = grunt.option( 'theme', 'blue' );
var out = 'blue'
- var lessFiles = [ 'base', 'autocomplete_tagging', 'embed_item', 'iphone', 'masthead', 'library', 'trackster' ];
+ var lessFiles = [ 'base', 'autocomplete_tagging', 'embed_item', 'iphone', 'masthead', 'library', 'trackster', 'toastr' ];
var _ = grunt.util._;
var fmt = _.sprintf;
diff -r c653272fdbb3e2fcc8e5dd83b2c71df8bd9b5c2a -r 267dcfd58a60d2de706d7c00c6fb6ec426eaaf5b static/style/src/less/base.less
--- a/static/style/src/less/base.less
+++ b/static/style/src/less/base.less
@@ -24,6 +24,7 @@
@import "frame.less";
@import "upload.less";
@import "library.less";
+@import "toastr.less";
// Mixins
diff -r c653272fdbb3e2fcc8e5dd83b2c71df8bd9b5c2a -r 267dcfd58a60d2de706d7c00c6fb6ec426eaaf5b static/style/src/less/toastr.less
--- /dev/null
+++ b/static/style/src/less/toastr.less
@@ -0,0 +1,232 @@
+// Mix-ins
+.borderRadius(@radius) {
+ -moz-border-radius: @radius;
+ -webkit-border-radius: @radius;
+ border-radius: @radius;
+}
+
+.boxShadow(@boxShadow) {
+ -moz-box-shadow: @boxShadow;
+ -webkit-box-shadow: @boxShadow;
+ box-shadow: @boxShadow;
+}
+
+.opacity(@opacity) {
+ @opacityPercent: @opacity * 100;
+ opacity: @opacity;
+ -ms-filter: ~"progid:DXImageTransform.Microsoft.Alpha(Opacity=@{opacityPercent})";
+ filter: ~"alpha(opacity=@{opacityPercent})";
+}
+
+.wordWrap(@wordWrap: break-word) {
+ -ms-word-wrap: @wordWrap;
+ word-wrap: @wordWrap;
+}
+
+// Variables
+@black: #000000;
+@grey: #999999;
+@light-grey: #CCCCCC;
+@white: #FFFFFF;
+@near-black: #030303;
+@green: #51A351;
+@red: #BD362F;
+@blue: #2F96B4;
+@orange: #F89406;
+
+// Styles
+.toast-title {
+ font-weight: bold;
+}
+
+.toast-message {
+ .wordWrap();
+
+ a,
+ label {
+ color: @white;
+ }
+
+ a:hover {
+ color: @light-grey;
+ text-decoration: none;
+ }
+}
+
+.toast-close-button {
+ position: relative;
+ right: -0.3em;
+ top: -0.3em;
+ float: right;
+ font-size: 20px;
+ font-weight: bold;
+ color: @white;
+ -webkit-text-shadow: 0 1px 0 rgba(255,255,255,1);
+ text-shadow: 0 1px 0 rgba(255,255,255,1);
+ .opacity(0.8);
+
+ &:hover,
+ &:focus {
+ color: @black;
+ text-decoration: none;
+ cursor: pointer;
+ .opacity(0.4);
+ }
+}
+
+/*Additional properties for button version
+ iOS requires the button element instead of an anchor tag.
+ If you want the anchor version, it requires `href="#"`.*/
+button.toast-close-button {
+ padding: 0;
+ cursor: pointer;
+ background: transparent;
+ border: 0;
+ -webkit-appearance: none;
+}
+
+//#endregion
+
+.toast-top-full-width {
+ top: 0;
+ right: 0;
+ width: 100%;
+}
+
+.toast-bottom-full-width {
+ bottom: 0;
+ right: 0;
+ width: 100%;
+}
+
+.toast-top-left {
+ top: 12px;
+ left: 12px;
+}
+
+.toast-top-right {
+ top: 12px;
+ right: 12px;
+}
+
+.toast-bottom-right {
+ right: 12px;
+ bottom: 12px;
+}
+
+.toast-bottom-left {
+ bottom: 12px;
+ left: 12px;
+}
+
+#toast-container {
+ position: fixed;
+ z-index: 999999;
+
+ * {
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ }
+
+ > div {
+ margin: 4em 0 6px;
+ padding: 15px 15px 15px 50px;
+ width: 300px;
+ .borderRadius(3px 3px 3px 3px);
+ background-position: 15px center;
+ background-repeat: no-repeat;
+ .boxShadow(0 0 12px @grey);
+ color: @white;
+ .opacity(0.8);
+ }
+
+ > :hover {
+ .boxShadow(0 0 12px @black);
+ .opacity(1);
+ cursor: pointer;
+ }
+
+ > .toast-info {
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=") !important;
+ }
+
+ > .toast-error {
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=") !important;
+ }
+
+ > .toast-success {
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==") !important;
+ }
+
+ > .toast-warning {
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=") !important;
+ }
+
+ /*overrides*/
+ &.toast-top-full-width > div,
+ &.toast-bottom-full-width > div {
+ width: 96%;
+ margin: auto;
+ }
+}
+
+.toast {
+ background-color: @near-black;
+}
+
+.toast-success {
+ background-color: @green;
+}
+
+.toast-error {
+ background-color: @red;
+}
+
+.toast-info {
+ background-color: @blue;
+}
+
+.toast-warning {
+ background-color: @orange;
+}
+
+/*Responsive Design*/
+
+@media all and (max-width: 240px) {
+ #toast-container {
+
+ > div {
+ padding: 8px 8px 8px 50px;
+ width: 11em;
+ }
+
+ & .toast-close-button {
+ right: -0.2em;
+ top: -0.2em;
+ }
+ }
+}
+
+@media all and (min-width: 241px) and (max-width: 480px) {
+ #toast-container {
+ > div {
+ padding: 8px 8px 8px 50px;
+ width: 18em;
+ }
+
+ & .toast-close-button {
+ right: -0.2em;
+ top: -0.2em;
+ }
+ }
+}
+
+@media all and (min-width: 481px) and (max-width: 768px) {
+ #toast-container {
+ > div {
+ padding: 15px 15px 15px 50px;
+ width: 25em;
+ }
+ }
+}
https://bitbucket.org/galaxy/galaxy-central/commits/9e7cd2749bde/
Changeset: 9e7cd2749bde
User: martenson
Date: 2013-12-13 20:33:06
Summary: rewrite of galaxy.library to use global notifications; removed previous notification engine from galaxy.modal.js;
Affected #: 3 files
diff -r 267dcfd58a60d2de706d7c00c6fb6ec426eaaf5b -r 9e7cd2749bdeb36bab9a7c883d95a877f9eaa2ca lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -144,38 +144,6 @@
parent_resources=dict( member_name='datatype', collection_name='datatypes' ) )
#webapp.mapper.connect( 'run_workflow', '/api/workflow/{workflow_id}/library/{library_id}', controller='workflows', action='run', workflow_id=None, library_id=None, conditions=dict(method=["GET"]) )
webapp.mapper.resource( 'search', 'search', path_prefix='/api' )
-
- # =======================
- # ===== LIBRARY API =====
- # =======================
-
- # The /folders section is experimental at this point:
- log.debug( "app.config.api_folders: %s" % app.config.api_folders )
-
- webapp.mapper.connect( 'show_lda_item', '/api/libraries/datasets/:id', controller='lda_datasets', action='show', conditions=dict(method=["GET"]) )
- webapp.mapper.connect( 'download_lda_items', '/api/libraries/datasets/download/:format', controller='lda_datasets', action='download', conditions=dict(method=["POST"]) )
-
- webapp.mapper.resource_with_deleted( 'library', 'libraries', path_prefix='/api' )
- webapp.mapper.resource( 'folder', 'folders', path_prefix='/api' )
-
- webapp.mapper.resource( 'content', 'contents',
- controller='folder_contents',
- name_prefix='folder_',
- path_prefix='/api/folders/:folder_id',
- parent_resources=dict( member_name='folder', collection_name='folders' ) )
- webapp.mapper.resource( 'content',
- 'contents',
- controller='library_contents',
- name_prefix='library_',
- path_prefix='/api/libraries/:library_id',
- parent_resources=dict( member_name='library', collection_name='libraries' ) )
- webapp.mapper.resource( 'permission',
- 'permissions',
- path_prefix='/api/libraries/:library_id',
- parent_resources=dict( member_name='library', collection_name='libraries' ) )
- _add_item_extended_metadata_controller( webapp,
- name_prefix="library_dataset_",
- path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
# add as a non-ATOM API call to support the notion of a 'current/working' history unique to the history resource
@@ -195,6 +163,52 @@
webapp.mapper.connect("workflow_dict", '/api/workflows/{workflow_id}/download', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
# Preserve the following download route for now for dependent applications -- deprecate at some point
webapp.mapper.connect("workflow_dict", '/api/workflows/download/{workflow_id}', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
+
+ # =======================
+ # ===== LIBRARY API =====
+ # =======================
+
+ webapp.mapper.connect( 'show_lda_item',
+ '/api/libraries/datasets/:id',
+ controller='lda_datasets',
+ action='show',
+ conditions=dict( method=[ "GET" ] ) )
+
+ webapp.mapper.connect( 'download_lda_items',
+ '/api/libraries/datasets/download/:format',
+ controller='lda_datasets',
+ action='download',
+ conditions=dict( method=[ "POST", "GET" ] ) )
+
+ webapp.mapper.resource_with_deleted( 'library',
+ 'libraries',
+ path_prefix='/api' )
+ webapp.mapper.resource( 'folder',
+ 'folders',
+ path_prefix='/api' )
+
+ webapp.mapper.resource( 'content',
+ 'contents',
+ controller='folder_contents',
+ name_prefix='folder_',
+ path_prefix='/api/folders/:folder_id',
+ parent_resources=dict( member_name='folder', collection_name='folders' ) )
+
+ webapp.mapper.resource( 'content',
+ 'contents',
+ controller='library_contents',
+ name_prefix='library_',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
+
+ webapp.mapper.resource( 'permission',
+ 'permissions',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
+
+ _add_item_extended_metadata_controller( webapp,
+ name_prefix="library_dataset_",
+ path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
# ====================
# ===== TOOLSHED =====
diff -r 267dcfd58a60d2de706d7c00c6fb6ec426eaaf5b -r 9e7cd2749bdeb36bab9a7c883d95a877f9eaa2ca static/scripts/galaxy.library.js
--- a/static/scripts/galaxy.library.js
+++ b/static/scripts/galaxy.library.js
@@ -8,7 +8,11 @@
var responses = [];
// dependencies
-define(["galaxy.modal", "galaxy.masthead", "utils/galaxy.utils", "libs/toastr"], function(mod_modal, mod_masthead, mod_utils, mod_toastr) {
+define([
+ "galaxy.modal",
+ "galaxy.masthead",
+ "utils/galaxy.utils",
+ "libs/toastr"], function(mod_modal, mod_masthead, mod_utils, mod_toastr) {
// MMMMMMMMMMMMMMM
// === Models ====
@@ -308,7 +312,7 @@
// event binding
events: {
'click #select-all-checkboxes' : 'selectAll',
- 'click .folder_row' : 'selectClicked',
+ 'click .folder_row' : 'selectClickedRow',
'click #toolbtn_bulk_import' : 'modalBulkImport',
'click #toolbtn_dl' : 'bulkDownload',
'click .library-dataset' : 'showDatasetDetails',
@@ -350,6 +354,7 @@
var template = _.template(that.templateFolder(), { path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id, upper_folder_id: upper_folder_id });
// var template = _.template(that.templateFolder(), { path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id });
that.$el.html(template);
+
}
})
},
@@ -456,12 +461,14 @@
// save the dataset into selected history
historyItem.save({ content : library_dataset_id, source : 'library' }, { success : function(){
- self.modal.showNotification('Dataset imported', 3000, 'success');
+ mod_toastr.success('Dataset imported');
+ // self.modal.showNotification('Dataset imported', 3000, 'success');
//enable the buttons
self.modal.enableButton('Import');
self.modal.enableButton('Download');
}, error : function(){
- self.modal.showNotification('An error occured! Dataset not imported. Please try again later.', 5000, 'error');
+ mod_toastr.error('An error occured! Dataset not imported. Please try again.')
+ // self.modal.showNotification('An error occured! Dataset not imported. Please try again later.', 5000, 'error');
//enable the buttons
self.modal.enableButton('Import');
self.modal.enableButton('Download');
@@ -471,37 +478,69 @@
// select all datasets
selectAll : function (event) {
- var selected = event.target.checked;
- // Iterate each checkbox
- $(':checkbox').each(function () { this.checked = selected; });
- this.showTools();
- },
+ var selected = event.target.checked;
+ that = this;
+ // Iterate each checkbox
+ $(':checkbox').each(function () {
+ this.checked = selected;
+ $row = $(this.parentElement.parentElement);
+ // Change color of selected/unselected
+ (selected) ? that.makeDarkRow($row) : that.makeWhiteRow($row);
+ });
+ // Show the tools in menu
+ this.checkTools();
+ },
- // click checkbox on folder click
- selectClicked : function (event) {
- var checkbox = $("#" + event.target.parentElement.id).find(':checkbox')
- if (checkbox[0] != undefined) {
- if (checkbox[0].checked){
- checkbox[0].checked = '';
- // $(event.target.parentElement).css('background-color', '').css('color', '');
- $(event.target.parentElement).removeClass('dark');
- $(event.target.parentElement).find('a').removeClass('dark');
- $(event.target.parentElement).addClass('light');
- $(event.target.parentElement).find('a').addClass('light');
+ // Check checkbox on row itself or row checkbox click
+ selectClickedRow : function (event) {
+ var checkbox = '';
+ var $row;
+ var source;
+ if (event.target.localName === 'input'){
+ checkbox = event.target;
+ $row = $(event.target.parentElement.parentElement);
+ source = 'input';
+ } else if (event.target.localName === 'td') {
+ checkbox = $("#" + event.target.parentElement.id).find(':checkbox')[0];
+ $row = $(event.target.parentElement);
+ source = 'td';
+ }
+ if (checkbox === '') {event.stopPropagation(); return;} // button in row was clicked
+
+ if (checkbox.checked){
+ if (source==='td'){
+ checkbox.checked = '';
+ this.makeWhiteRow($row);
+ } else if (source==='input') {
+ this.makeDarkRow($row);
+ }
} else {
- checkbox[0].checked = 'selected';
- $(event.target.parentElement).removeClass('light');
- $(event.target.parentElement).find('a').removeClass('light');
- $(event.target.parentElement).addClass('dark');
- $(event.target.parentElement).find('a').addClass('dark');
- // $(event.target.parentElement).css('background-color', '#8389a1').css('color', 'white');
+ if (source==='td'){
+ checkbox.checked = 'selected';
+ this.makeDarkRow($row);
+ } else if (source==='input') {
+ this.makeWhiteRow($row);
+ }
}
- }
- this.showTools();
+ this.checkTools();
+ },
+
+ makeDarkRow: function($row){
+ $row.removeClass('light');
+ $row.find('a').removeClass('light');
+ $row.addClass('dark');
+ $row.find('a').addClass('dark');
+ },
+
+ makeWhiteRow: function($row){
+ $row.removeClass('dark');
+ $row.find('a').removeClass('dark');
+ $row.addClass('light');
+ $row.find('a').addClass('light');
},
// show toolbar in case something is selected
- showTools : function(){
+ checkTools : function(){
var checkedValues = $('#folder_table').find(':checked');
if(checkedValues.length > 0){
$('#toolbtn_bulk_import').show();
@@ -577,7 +616,9 @@
var self = this;
var popped_item = history_item_set.pop();
if (typeof popped_item === "undefined") {
- self.modal.showNotification('All datasets imported', 3000, 'success');
+ mod_toastr.success('All datasets imported');
+ this.modal.hide();
+ // self.modal.showNotification('All datasets imported', 3000, 'success');
// enable button again
self.modal.enableButton('Import');
return
@@ -646,12 +687,13 @@
//send request
$('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
.appendTo('body').submit().remove();
+ mod_toastr.info('Your download will begin soon');
};
},
// shows modal for creating folder
createFolderModal: function(){
- alert('creating folder');
+ mod_toastr.info('This will create folder...in the future');
}
});
@@ -704,7 +746,7 @@
// render
render: function () {
- //hack to show scrollbars
+ //hack to show scrollbars due to #center element inheritance
$("#center").css('overflow','auto');
var that = this;
@@ -717,18 +759,16 @@
},
error: function(model, response){
if (response.statusCode().status === 403){
- //show notification
- // var toast = mod_toastr;
mod_toastr.error('Please log in first. Redirecting to login page in 3s.');
setTimeout(that.redirectToLogin, 3000);
} else {
- library_router.navigate('#', {trigger: true, replace: true});
+ mod_toastr.error('An error occured. Please try again.');
}
}
})
},
- redirectToSplash: function(){
+ redirectToHome: function(){
window.location = '../';
},
redirectToLogin: function(){
@@ -762,19 +802,23 @@
// create the new library from modal
create_new_library_event: function(){
var libraryDetails = this.serialize_new_library();
- var valid = this.validate_new_library(libraryDetails);
- var library = new Library();
- var self = this;
- library.save(libraryDetails, {
- success: function (library) {
- self.modal.hide();
- self.clear_library_modal();
- self.render();
- },
- error: function(){
- self.modal.showNotification('An error occured', 5000, 'error');
- }
- });
+ if (this.validate_new_library(libraryDetails)){
+ var library = new Library();
+ var self = this;
+ library.save(libraryDetails, {
+ success: function (library) {
+ self.modal.hide();
+ self.clear_library_modal();
+ self.render();
+ mod_toastr.success('Library created');
+ },
+ error: function(){
+ mod_toastr.error('An error occured :(');
+ }
+ });
+ } else {
+ mod_toastr.error('Library\'s name is missing');
+ }
return false;
},
@@ -794,14 +838,13 @@
};
},
- validate_new_library: function(library){
-
+ // validate new library info
+ validate_new_library: function(libraryDetails){
+ return libraryDetails.name !== '';
},
-
// template for new library modal
- template_new_library: function()
- {
+ template_new_library: function(){
tmpl_array = [];
tmpl_array.push('<div id="new_library_modal">');
@@ -824,34 +867,29 @@
folderContentView = new FolderContentView();
galaxyLibraryview = new GalaxyLibraryview();
-
library_router = new LibraryRouter();
library_router.on('route:libraries', function() {
// render libraries list
galaxyLibraryview.render();
- });
+ });
library_router.on('route:folder_content', function(id) {
// render folder's contents
folderContentView.render({id: id});
- });
+ });
library_router.on('route:download', function(folder_id, format) {
- // send download stream
- if (typeof folderContentView === 'undefined'){
- alert('you cant touch this!');
- // } else if (folderContentView.modal !== null){
- // folderContentView.download(folder_id, format);
- } else if ($('#center').find(':checked').length === 0) { // coming from outside of the library app
+ if ($('#center').find(':checked').length === 0) {
+ // this happens rarely when there is a server/data error and client gets an actual response instead of an attachment
+ // we don't know what was selected so we can't download again, we redirect to the folder provided
library_router.navigate('folders/' + folder_id, {trigger: true, replace: true});
- // TODO show message of redirection
} else {
+ // send download stream
folderContentView.download(folder_id, format);
library_router.navigate('folders/' + folder_id, {trigger: false, replace: true});
}
-
- });
+ });
Backbone.history.start();
diff -r 267dcfd58a60d2de706d7c00c6fb6ec426eaaf5b -r 9e7cd2749bdeb36bab9a7c883d95a877f9eaa2ca static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -26,16 +26,17 @@
if (options)
this.create(options);
- this.bindClick(event, self);
+ // Bind the hiding events
+ this.bindEvents(event, self);
},
// bind the click-to-hide function
- bindClick: function(event, that) {
+ bindEvents: function(event, that) {
// bind the ESC key to hide() function
$(document).on('keyup', function(event){
if (event.keyCode == 27) { self.hide(); }
})
- // bind the click anywhere to hide() function...
+ // bind the 'click anywhere' to hide() function...
$('html').on('click', function(event){
that.hide();
})
@@ -46,26 +47,24 @@
},
// unbind the click-to-hide function
- unbindClick: function(event, that){
+ unbindEvents: function(event, that){
// bind the ESC key to hide() function
$(document).off('keyup', function(event){
if (event.keyCode == 27) { that.hide(); }
})
- // unbind the click anywhere to hide() function...
+ // unbind the 'click anywhere' to hide() function...
$('html').off('click', function(event){
that.hide();
})
- // ...but don't hide if the click is on modal content
$('.modal-content').off('click', function(event){
event.stopPropagation();
})
},
-
// destroy
destroy : function(){
this.hide();
- this.unbindClick();
+ this.unbindEvents();
$('.modal').remove();
},
@@ -103,6 +102,7 @@
// set flag
this.visible = false;
+ this.unbindEvents();
},
// create
@@ -127,7 +127,6 @@
this.$footer = (this.$el).find('.modal-footer');
this.$buttons = (this.$el).find('.buttons');
this.$backdrop = (this.$el).find('.modal-backdrop');
- this.$notification = (this.$el).find('.notification-modal');
// append body
this.$body.html(this.options.body);
@@ -170,35 +169,6 @@
this.$buttons.find('#' + String(name).toLowerCase()).show();
},
- // show notification
- showNotification : function(message, duration, type) {
- var duration = typeof duration !== 'undefined' ? duration : 3000;
- var bgColor;
- var txtColor;
-
- if (type === 'error'){
- bgColor = '#f4e0e1';
- txtColor = '#a42732';
- // } else if (type === 'success'){
- } else { // success is default
- bgColor = '#e1f4e0';
- txtColor = '#32a427';
- }
-
- var HTMLmessage = "<div class='notification-message' style='text-align:center; line-height:16px; '> " + message + " </div>";
- this.$notification.html("<div id='notification-bar' style='display:none; float: right; height: 16px; width:100%; background-color: " + bgColor + "; z-index: 100; color: " + txtColor + ";border-bottom: 1px solid " + txtColor + ";'>" + HTMLmessage + "</div>");
-
- var self = this;
-
- /*animate the bar*/
- $('#notification-bar').slideDown(function() {
- setTimeout(function() {
- $('#notification-bar').slideUp(function() {self.$notification.html('');});
- }, duration);
- });
-
- },
-
// returns scroll top for body element
scrollTop: function()
{
@@ -218,7 +188,6 @@
'<div class="modal-header">' +
'<button type="button" class="close" style="display: none;">×</button>' +
'<h4 class="title">' + title + '</h4>' +
- '<span class="notification-modal"></span>' +
'</div>' +
'<div class="modal-body"></div>' +
'<div class="modal-footer">' +
https://bitbucket.org/galaxy/galaxy-central/commits/0522f7504094/
Changeset: 0522f7504094
User: martenson
Date: 2013-12-13 21:05:26
Summary: switch download to GET, removed toaster from grunt file
Affected #: 2 files
diff -r 9e7cd2749bdeb36bab9a7c883d95a877f9eaa2ca -r 0522f7504094bd5be2be97846e695a0c790bf929 static/scripts/galaxy.library.js
--- a/static/scripts/galaxy.library.js
+++ b/static/scripts/galaxy.library.js
@@ -462,13 +462,11 @@
// save the dataset into selected history
historyItem.save({ content : library_dataset_id, source : 'library' }, { success : function(){
mod_toastr.success('Dataset imported');
- // self.modal.showNotification('Dataset imported', 3000, 'success');
//enable the buttons
self.modal.enableButton('Import');
self.modal.enableButton('Download');
}, error : function(){
mod_toastr.error('An error occured! Dataset not imported. Please try again.')
- // self.modal.showNotification('An error occured! Dataset not imported. Please try again later.', 5000, 'error');
//enable the buttons
self.modal.enableButton('Import');
self.modal.enableButton('Download');
@@ -618,7 +616,6 @@
if (typeof popped_item === "undefined") {
mod_toastr.success('All datasets imported');
this.modal.hide();
- // self.modal.showNotification('All datasets imported', 3000, 'success');
// enable button again
self.modal.enableButton('Import');
return
@@ -669,7 +666,7 @@
var url = '/api/libraries/datasets/download/' + format;
var data = {'ldda_ids' : dataset_ids};
- this.processDownload(url, data);
+ this.processDownload(url, data, 'get');
},
// create hidden form and submit through POST to initialize download
diff -r 9e7cd2749bdeb36bab9a7c883d95a877f9eaa2ca -r 0522f7504094bd5be2be97846e695a0c790bf929 static/style/Gruntfile.js
--- a/static/style/Gruntfile.js
+++ b/static/style/Gruntfile.js
@@ -6,7 +6,7 @@
var theme = grunt.option( 'theme', 'blue' );
var out = 'blue'
- var lessFiles = [ 'base', 'autocomplete_tagging', 'embed_item', 'iphone', 'masthead', 'library', 'trackster', 'toastr' ];
+ var lessFiles = [ 'base', 'autocomplete_tagging', 'embed_item', 'iphone', 'masthead', 'library', 'trackster'];
var _ = grunt.util._;
var fmt = _.sprintf;
https://bitbucket.org/galaxy/galaxy-central/commits/5463295a3981/
Changeset: 5463295a3981
User: martenson
Date: 2013-12-16 00:26:37
Summary: Merged galaxy/galaxy-central into default
Affected #: 139 files
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/app.py
--- a/lib/galaxy/app.py
+++ b/lib/galaxy/app.py
@@ -1,16 +1,10 @@
from __future__ import absolute_import
-import sys, os, atexit
+import sys
+import os
-from galaxy import config, jobs, util, tools, web
-import galaxy.tools.search
-import galaxy.tools.data
-import tool_shed.galaxy_install
-import tool_shed.tool_shed_registry
-from galaxy.web import security
+from galaxy import config, jobs
import galaxy.model
-import galaxy.datatypes.registry
import galaxy.security
-from galaxy.objectstore import build_object_store_from_config
import galaxy.quota
from galaxy.tags.tag_handler import GalaxyTagHandler
from galaxy.visualization.genomes import Genomes
@@ -27,7 +21,8 @@
import logging
log = logging.getLogger( __name__ )
-class UniverseApplication( object ):
+
+class UniverseApplication( object, config.ConfiguresGalaxyMixin ):
"""Encapsulates the state of a Universe application"""
def __init__( self, **kwargs ):
print >> sys.stderr, "python path is: " + ", ".join( sys.path )
@@ -38,92 +33,38 @@
self.config.check()
config.configure_logging( self.config )
self.configure_fluent_log()
- # Determine the database url
- if self.config.database_connection:
- db_url = self.config.database_connection
- else:
- db_url = "sqlite:///%s?isolation_level=IMMEDIATE" % self.config.database
- install_db_url = self.config.install_database_connection
- # TODO: Consider more aggressive check here that this is not the same
- # database file under the hood.
- combined_install_database = not( install_db_url and install_db_url != db_url )
- # Set up the tool sheds registry
- if os.path.isfile( self.config.tool_sheds_config ):
- self.tool_shed_registry = tool_shed.tool_shed_registry.Registry( self.config.root, self.config.tool_sheds_config )
- else:
- self.tool_shed_registry = None
- # Initialize database / check for appropriate schema version. # If this
- # is a new installation, we'll restrict the tool migration messaging.
- from galaxy.model.migrate.check import create_or_verify_database
- create_or_verify_database( db_url, kwargs.get( 'global_conf', {} ).get( '__file__', None ), self.config.database_engine_options, app=self )
- if not combined_install_database:
- from galaxy.model.tool_shed_install.migrate.check import create_or_verify_database as tsi_create_or_verify_database
- tsi_create_or_verify_database( install_db_url, self.config.install_database_engine_options, app=self )
- # Alert the Galaxy admin to tools that have been moved from the distribution to the tool shed.
- from tool_shed.galaxy_install.migrate.check import verify_tools
- verify_tools( self, db_url, kwargs.get( 'global_conf', {} ).get( '__file__', None ), self.config.database_engine_options )
- # Object store manager
- self.object_store = build_object_store_from_config(self.config, fsmon=True)
+ self._configure_tool_shed_registry()
+
+ self._configure_object_store( fsmon=True )
+
# Setup the database engine and ORM
- from galaxy.model import mapping
- self.model = mapping.init( self.config.file_path,
- db_url,
- self.config.database_engine_options,
- map_install_models=combined_install_database,
- database_query_profiling_proxy = self.config.database_query_profiling_proxy,
- object_store = self.object_store,
- trace_logger=self.trace_logger,
- use_pbkdf2=self.config.get_bool( 'use_pbkdf2', True ) )
+ config_file = kwargs.get( 'global_conf', {} ).get( '__file__', None )
+ self._configure_models( check_migrate_databases=True, check_migrate_tools=True, config_file=config_file )
- if combined_install_database:
- log.info("Install database targetting Galaxy's database configuration.")
- self.install_model = self.model
- else:
- from galaxy.model.tool_shed_install import mapping as install_mapping
- install_db_url = self.config.install_database_connection
- log.info("Install database using its own connection %s" % install_db_url)
- install_db_engine_options = self.config.install_database_engine_options
- self.install_model = install_mapping.init( install_db_url,
- install_db_engine_options )
# Manage installed tool shed repositories.
- self.installed_repository_manager = tool_shed.galaxy_install.InstalledRepositoryManager( self )
- # Create an empty datatypes registry.
- self.datatypes_registry = galaxy.datatypes.registry.Registry()
- # Load proprietary datatypes defined in datatypes_conf.xml files in all installed tool shed repositories. We
- # load proprietary datatypes before datatypes in the distribution because Galaxy's default sniffers include some
- # generic sniffers (eg text,xml) which catch anything, so it's impossible for proprietary sniffers to be used.
- # However, if there is a conflict (2 datatypes with the same extension) between a proprietary datatype and a datatype
- # in the Galaxy distribution, the datatype in the Galaxy distribution will take precedence. If there is a conflict
- # between 2 proprietary datatypes, the datatype from the repository that was installed earliest will take precedence.
- self.installed_repository_manager.load_proprietary_datatypes()
- # Load the data types in the Galaxy distribution, which are defined in self.config.datatypes_config.
- self.datatypes_registry.load_datatypes( self.config.root, self.config.datatypes_config )
+ from tool_shed.galaxy_install import installed_repository_manager
+ self.installed_repository_manager = installed_repository_manager.InstalledRepositoryManager( self )
+
+ self._configure_datatypes_registry( self.installed_repository_manager )
galaxy.model.set_datatypes_registry( self.datatypes_registry )
+
# Security helper
- self.security = security.SecurityHelper( id_secret=self.config.id_secret )
+ self._configure_security()
# Tag handler
self.tag_handler = GalaxyTagHandler()
# Genomes
self.genomes = Genomes( self )
# Data providers registry.
self.data_provider_registry = DataProviderRegistry()
- # Initialize tool data tables using the config defined by self.config.tool_data_table_config_path.
- self.tool_data_tables = galaxy.tools.data.ToolDataTableManager( tool_data_path=self.config.tool_data_path,
- config_filename=self.config.tool_data_table_config_path )
- # Load additional entries defined by self.config.shed_tool_data_table_config into tool data tables.
- self.tool_data_tables.load_from_config_file( config_filename=self.config.shed_tool_data_table_config,
- tool_data_path=self.tool_data_tables.tool_data_path,
- from_shed_config=False )
+
+ self._configure_tool_data_tables( from_shed_config=False )
+
# Initialize the job management configuration
self.job_config = jobs.JobConfiguration(self)
- # Initialize the tools, making sure the list of tool configs includes the reserved migrated_tools_conf.xml file.
- tool_configs = self.config.tool_configs
- if self.config.migrated_tools_config not in tool_configs:
- tool_configs.append( self.config.migrated_tools_config )
- self.toolbox = tools.ToolBox( tool_configs, self.config.tool_path, self )
- # Search support for tools
- self.toolbox_search = galaxy.tools.search.ToolBoxSearch( self.toolbox )
+
+ self._configure_toolbox()
+
# Load Data Manager
self.data_managers = DataManagers( self )
# If enabled, poll respective tool sheds to see if updates are available for any installed tool shed repositories.
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -473,6 +473,7 @@
rval[ key ] = value
return rval
+
def configure_logging( config ):
"""
Allow some basic logging configuration to be read from ini file.
@@ -513,3 +514,110 @@
sentry_handler.setLevel( logging.WARN )
root.addHandler( sentry_handler )
+
+class ConfiguresGalaxyMixin:
+ """ Shared code for configuring Galaxy-like app objects.
+ """
+
+ def _configure_toolbox( self ):
+ # Initialize the tools, making sure the list of tool configs includes the reserved migrated_tools_conf.xml file.
+ tool_configs = self.config.tool_configs
+ if self.config.migrated_tools_config not in tool_configs:
+ tool_configs.append( self.config.migrated_tools_config )
+ from galaxy import tools
+ self.toolbox = tools.ToolBox( tool_configs, self.config.tool_path, self )
+ # Search support for tools
+ import galaxy.tools.search
+ self.toolbox_search = galaxy.tools.search.ToolBoxSearch( self.toolbox )
+
+ def _configure_tool_data_tables( self, from_shed_config ):
+ from galaxy.tools.data import ToolDataTableManager
+
+ # Initialize tool data tables using the config defined by self.config.tool_data_table_config_path.
+ self.tool_data_tables = ToolDataTableManager( tool_data_path=self.config.tool_data_path,
+ config_filename=self.config.tool_data_table_config_path )
+ # Load additional entries defined by self.config.shed_tool_data_table_config into tool data tables.
+ self.tool_data_tables.load_from_config_file( config_filename=self.config.shed_tool_data_table_config,
+ tool_data_path=self.tool_data_tables.tool_data_path,
+ from_shed_config=from_shed_config )
+
+ def _configure_datatypes_registry( self, installed_repository_manager=None ):
+ from galaxy.datatypes import registry
+ # Create an empty datatypes registry.
+ self.datatypes_registry = registry.Registry()
+ if installed_repository_manager:
+ # Load proprietary datatypes defined in datatypes_conf.xml files in all installed tool shed repositories. We
+ # load proprietary datatypes before datatypes in the distribution because Galaxy's default sniffers include some
+ # generic sniffers (eg text,xml) which catch anything, so it's impossible for proprietary sniffers to be used.
+ # However, if there is a conflict (2 datatypes with the same extension) between a proprietary datatype and a datatype
+ # in the Galaxy distribution, the datatype in the Galaxy distribution will take precedence. If there is a conflict
+ # between 2 proprietary datatypes, the datatype from the repository that was installed earliest will take precedence.
+ installed_repository_manager.load_proprietary_datatypes()
+ # Load the data types in the Galaxy distribution, which are defined in self.config.datatypes_config.
+ self.datatypes_registry.load_datatypes( self.config.root, self.config.datatypes_config )
+
+ def _configure_object_store( self, **kwds ):
+ from galaxy.objectstore import build_object_store_from_config
+ self.object_store = build_object_store_from_config( self.config, **kwds )
+
+ def _configure_security( self ):
+ from galaxy.web import security
+ self.security = security.SecurityHelper( id_secret=self.config.id_secret )
+
+ def _configure_tool_shed_registry( self ):
+ import tool_shed.tool_shed_registry
+
+ # Set up the tool sheds registry
+ if os.path.isfile( self.config.tool_sheds_config ):
+ self.tool_shed_registry = tool_shed.tool_shed_registry.Registry( self.config.root, self.config.tool_sheds_config )
+ else:
+ self.tool_shed_registry = None
+
+ def _configure_models( self, check_migrate_databases=False, check_migrate_tools=False, config_file=None ):
+ """
+ Preconditions: object_store must be set on self.
+ """
+ if self.config.database_connection:
+ db_url = self.config.database_connection
+ else:
+ db_url = "sqlite:///%s?isolation_level=IMMEDIATE" % self.config.database
+ install_db_url = self.config.install_database_connection
+ # TODO: Consider more aggressive check here that this is not the same
+ # database file under the hood.
+ combined_install_database = not( install_db_url and install_db_url != db_url )
+ install_db_url = install_db_url or db_url
+
+ if check_migrate_databases:
+ # Initialize database / check for appropriate schema version. # If this
+ # is a new installation, we'll restrict the tool migration messaging.
+ from galaxy.model.migrate.check import create_or_verify_database
+ create_or_verify_database( db_url, config_file, self.config.database_engine_options, app=self )
+ if not combined_install_database:
+ from galaxy.model.tool_shed_install.migrate.check import create_or_verify_database as tsi_create_or_verify_database
+ tsi_create_or_verify_database( install_db_url, self.config.install_database_engine_options, app=self )
+
+ if check_migrate_tools:
+ # Alert the Galaxy admin to tools that have been moved from the distribution to the tool shed.
+ from tool_shed.galaxy_install.migrate.check import verify_tools
+ verify_tools( self, install_db_url, config_file, self.config.database_engine_options )
+
+ from galaxy.model import mapping
+ self.model = mapping.init( self.config.file_path,
+ db_url,
+ self.config.database_engine_options,
+ map_install_models=combined_install_database,
+ database_query_profiling_proxy=self.config.database_query_profiling_proxy,
+ object_store=self.object_store,
+ trace_logger=getattr(self, "trace_logger", None),
+ use_pbkdf2=self.config.get_bool( 'use_pbkdf2', True ) )
+
+ if combined_install_database:
+ log.info("Install database targetting Galaxy's database configuration.")
+ self.install_model = self.model
+ else:
+ from galaxy.model.tool_shed_install import mapping as install_mapping
+ install_db_url = self.config.install_database_connection
+ log.info("Install database using its own connection %s" % install_db_url)
+ install_db_engine_options = self.config.install_database_engine_options
+ self.install_model = install_mapping.init( install_db_url,
+ install_db_engine_options )
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/datatypes/registry.py
--- a/lib/galaxy/datatypes/registry.py
+++ b/lib/galaxy/datatypes/registry.py
@@ -598,6 +598,9 @@
tool_xml_text = """
<tool id="__SET_METADATA__" name="Set External Metadata" version="1.0.1" tool_type="set_metadata"><type class="SetMetadataTool" module="galaxy.tools"/>
+ <requirements>
+ <requirement type="package">samtools</requirement>
+ </requirements><action module="galaxy.tools.actions.metadata" class="SetMetadataToolAction"/><command>$__SET_EXTERNAL_METADATA_COMMAND_LINE__</command><inputs>
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/jobs/__init__.py
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -688,10 +688,7 @@
if self.command_line and self.command_line.startswith( 'python' ):
self.galaxy_lib_dir = os.path.abspath( "lib" ) # cwd = galaxy root
# Shell fragment to inject dependencies
- if self.app.config.use_tool_dependencies:
- self.dependency_shell_commands = self.tool.build_dependency_shell_commands()
- else:
- self.dependency_shell_commands = None
+ self.dependency_shell_commands = self.tool.build_dependency_shell_commands()
# We need command_line persisted to the db in order for Galaxy to re-queue the job
# if the server was stopped and restarted before the job finished
job.command_line = self.command_line
@@ -1435,10 +1432,7 @@
if self.command_line and self.command_line.startswith( 'python' ):
self.galaxy_lib_dir = os.path.abspath( "lib" ) # cwd = galaxy root
# Shell fragment to inject dependencies
- if self.app.config.use_tool_dependencies:
- self.dependency_shell_commands = self.tool.build_dependency_shell_commands()
- else:
- self.dependency_shell_commands = None
+ self.dependency_shell_commands = self.tool.build_dependency_shell_commands()
# We need command_line persisted to the db in order for Galaxy to re-queue the job
# if the server was stopped and restarted before the job finished
task.command_line = self.command_line
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/jobs/actions/post.py
--- a/lib/galaxy/jobs/actions/post.py
+++ b/lib/galaxy/jobs/actions/post.py
@@ -12,7 +12,7 @@
form = """
if (pja.action_type == "%s"){
p_str = "<div class='pjaForm toolForm'><span class='action_tag' style='display:none'>"+ pja.action_type + pja.output_name + "</span><div class='toolFormTitle'> %s <br/> on " + pja.output_name + "\
- <div style='float: right;' class='buttons'><img src='/static/images/delete_icon.png'></div></div><div class='toolFormBody'>";
+ <div style='float: right;' class='buttons'><img src='/static/images/history-buttons/delete_icon.png'></div></div><div class='toolFormBody'>";
%s
p_str += "</div><div class='toolParamHelp'>%s</div></div>";
}""" % (action_type, title, content, help)
@@ -20,7 +20,7 @@
form = """
if (pja.action_type == "%s"){
p_str = "<div class='pjaForm toolForm'><span class='action_tag' style='display:none'>"+ pja.action_type + "</span><div class='toolFormTitle'> %s \
- <div style='float: right;' class='buttons'><img src='/static/images/delete_icon.png'></div></div><div class='toolFormBody'>";
+ <div style='float: right;' class='buttons'><img src='/static/images/history-buttons/delete_icon.png'></div></div><div class='toolFormBody'>";
%s
p_str += "</div><div class='toolParamHelp'>%s</div></div>";
}""" % (action_type, title, content, help)
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/jobs/runners/__init__.py
--- a/lib/galaxy/jobs/runners/__init__.py
+++ b/lib/galaxy/jobs/runners/__init__.py
@@ -198,7 +198,7 @@
log.exception( "from_work_dir specified a location not in the working directory: %s, %s" % ( source_file, job_wrapper.working_directory ) )
return output_pairs
- def _handle_metadata_externally(self, job_wrapper):
+ def _handle_metadata_externally( self, job_wrapper, resolve_requirements=False ):
"""
Set metadata externally. Used by the local and lwr job runners where this
shouldn't be attached to command-line to execute.
@@ -212,6 +212,12 @@
tmp_dir=job_wrapper.working_directory,
#we don't want to overwrite metadata that was copied over in init_meta(), as per established behavior
kwds={ 'overwrite' : False } )
+ if resolve_requirements:
+ dependency_shell_commands = self.app.datatypes_registry.set_external_metadata_tool.build_dependency_shell_commands()
+ if dependency_shell_commands:
+ if isinstance( dependency_shell_commands, list ):
+ dependency_shell_commands = "&&".join( dependency_shell_commands )
+ external_metadata_script = "%s&&%s" % ( dependency_shell_commands, external_metadata_script )
log.debug( 'executing external set_meta script for job %d: %s' % ( job_wrapper.job_id, external_metadata_script ) )
external_metadata_proc = subprocess.Popen( args=external_metadata_script,
shell=True,
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/jobs/runners/local.py
--- a/lib/galaxy/jobs/runners/local.py
+++ b/lib/galaxy/jobs/runners/local.py
@@ -110,7 +110,7 @@
job_wrapper.fail( "failure running job", exception=True )
log.exception("failure running job %d" % job_wrapper.job_id)
return
- self._handle_metadata_externally( job_wrapper )
+ self._handle_metadata_externally( job_wrapper, resolve_requirements=True )
# Finish the job!
try:
job_wrapper.finish( stdout, stderr, exit_code )
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/jobs/runners/lwr.py
--- a/lib/galaxy/jobs/runners/lwr.py
+++ b/lib/galaxy/jobs/runners/lwr.py
@@ -204,7 +204,7 @@
log.exception("failure running job %d" % job_wrapper.job_id)
return
if not LwrJobRunner.__remote_metadata( client ):
- self._handle_metadata_externally( job_wrapper )
+ self._handle_metadata_externally( job_wrapper, resolve_requirements=True )
# Finish the job
try:
job_wrapper.finish( stdout, stderr )
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/model/tool_shed_install/__init__.py
--- a/lib/galaxy/model/tool_shed_install/__init__.py
+++ b/lib/galaxy/model/tool_shed_install/__init__.py
@@ -1,11 +1,11 @@
+import logging
import os
-
from galaxy.model.item_attrs import Dictifiable
-
from galaxy.util.bunch import Bunch
from galaxy.util import asbool
+from tool_shed.util import common_util
-from tool_shed.util import common_util
+log = logging.getLogger( __name__ )
class ToolShedRepository( object ):
@@ -61,9 +61,53 @@
def can_reset_metadata( self ):
return self.status == self.installation_status.INSTALLED
- @property
- def can_uninstall( self ):
- return self.status != self.installation_status.UNINSTALLED
+ def can_uninstall( self, app ):
+ # An installed repository cannot be uninstalled if other installed repositories or installed repository
+ # contents (i.e., tool dependencies) require it.
+ if self.status == self.installation_status.UNINSTALLED:
+ return False
+ this_repository_tup = ( str( self.tool_shed ),
+ str( self.name ),
+ str( self.owner ),
+ str( self.installed_changeset_revision ) )
+ irm = app.installed_repository_manager
+ # See if this repository's current dependencies are restricted to a single circular relationship. This
+ # means that this repository has a single repository dependency which itself depends upon this repository.
+ # The repository dependency may have other repository dependencies, but that is not relevant here.
+ single_repository_dependency_tup = None
+ installed_repository_dependency_tups = \
+ irm.installed_repository_dependencies_of_installed_repositories.get( this_repository_tup, [] )
+ # If this repository defines a circular relationship to another repository, then the list of tuples
+ # defining installed repository dependencies will include itself.
+ if len( installed_repository_dependency_tups ) == 2:
+ if this_repository_tup in installed_repository_dependency_tups:
+ # We have a single circular dependency definition, so get the other repository.
+ for installed_repository_dependency_tup in installed_repository_dependency_tups:
+ if installed_repository_dependency_tup != this_repository_tup:
+ single_repository_dependency_tup = installed_repository_dependency_tup
+ break
+ if single_repository_dependency_tup is not None:
+ installed_repository_dependency_tups = \
+ irm.installed_repository_dependencies_of_installed_repositories.get( this_repository_tup, [] )
+ if this_repository_tup in installed_repository_dependency_tups:
+ # This repository is a dependency of the single repository upon which it depends, so we have
+ # a single circular relationship and this repository can be uninstalled.
+ return True
+ # Find other installed repositories that require this repository.
+ installed_dependent_repositories = \
+ irm.installed_dependent_repositories_of_installed_repositories.get( this_repository_tup, [] )
+ if installed_dependent_repositories:
+ # This repository cannot be uninstalled because other installed repositories require it.
+ return False
+ # Find installed tool dependencies that require this repository's installed tool dependencies.
+ installed_tool_dependencies = irm.installed_tool_dependencies_of_installed_repositories.get( this_repository_tup, [] )
+ for td_tup in installed_tool_dependencies:
+ installed_dependent_td_tups = irm.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies.get( td_tup, [] )
+ if installed_dependent_td_tups:
+ # This repository cannot be uninstalled because it contains installed tool dependencies that
+ # are required at run time by other installed tool dependencies.
+ return False
+ return True
@property
def can_deactivate( self ):
@@ -218,7 +262,7 @@
@property
def installed_tool_dependencies( self ):
- """Return the repository's tool dependencies that are currently installed."""
+ """Return the repository's tool dependencies that are currently installed, but possibly in an error state."""
installed_dependencies = []
for tool_dependency in self.tool_dependencies:
if tool_dependency.status in [ ToolDependency.installation_status.INSTALLED,
@@ -313,9 +357,9 @@
required_repositories_missing_or_being_installed = []
for required_repository in self.repository_dependencies:
if required_repository.status in [ self.installation_status.ERROR,
- self.installation_status.INSTALLING,
- self.installation_status.NEVER_INSTALLED,
- self.installation_status.UNINSTALLED ]:
+ self.installation_status.INSTALLING,
+ self.installation_status.NEVER_INSTALLED,
+ self.installation_status.UNINSTALLED ]:
required_repositories_missing_or_being_installed.append( required_repository )
return required_repositories_missing_or_being_installed
@@ -507,7 +551,9 @@
@property
def can_uninstall( self ):
- return self.status in [ self.installation_status.ERROR, self.installation_status.INSTALLED ]
+ # A tool dependency can be uninstalled only if it is currently in an error state. Only the containing
+ # repository can be uninstalled if a tool dependency is properly installed.
+ return self.status in [ self.installation_status.ERROR ]
@property
def can_update( self ):
@@ -516,6 +562,13 @@
self.installation_status.ERROR,
self.installation_status.UNINSTALLED ]
+ def get_env_shell_file_path( self, app ):
+ installation_directory = self.installation_directory( app )
+ file_path = os.path.join( installation_directory, 'env.sh' )
+ if os.path.exists( file_path ):
+ return file_path
+ return None
+
@property
def in_error_state( self ):
return self.status == self.installation_status.ERROR
@@ -536,6 +589,10 @@
self.tool_shed_repository.name,
self.tool_shed_repository.installed_changeset_revision )
+ @property
+ def is_installed( self ):
+ return self.status == self.installation_status.INSTALLED
+
class ToolVersion( object, Dictifiable ):
dict_element_visible_keys = ( 'id', 'tool_shed_repository' )
@@ -549,23 +606,23 @@
def get_previous_version( self, app ):
context = app.install_model.context
tva = context.query( app.install_model.ToolVersionAssociation ) \
- .filter( app.install_model.ToolVersionAssociation.table.c.tool_id == self.id ) \
- .first()
+ .filter( app.install_model.ToolVersionAssociation.table.c.tool_id == self.id ) \
+ .first()
if tva:
return context.query( app.install_model.ToolVersion ) \
- .filter( app.install_model.ToolVersion.table.c.id == tva.parent_id ) \
- .first()
+ .filter( app.install_model.ToolVersion.table.c.id == tva.parent_id ) \
+ .first()
return None
def get_next_version( self, app ):
context = app.install_model.context
tva = context.query( app.install_model.ToolVersionAssociation ) \
- .filter( app.install_model.ToolVersionAssociation.table.c.parent_id == self.id ) \
- .first()
+ .filter( app.install_model.ToolVersionAssociation.table.c.parent_id == self.id ) \
+ .first()
if tva:
return context.query( app.install_model.ToolVersion ) \
- .filter( app.install_model.ToolVersion.table.c.id == tva.tool_id ) \
- .first()
+ .filter( app.install_model.ToolVersion.table.c.id == tva.tool_id ) \
+ .first()
return None
def get_versions( self, app ):
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/model/tool_shed_install/mapping.py
--- a/lib/galaxy/model/tool_shed_install/mapping.py
+++ b/lib/galaxy/model/tool_shed_install/mapping.py
@@ -7,10 +7,8 @@
from galaxy.model.base import ModelMapping
from galaxy.model.orm.engine_factory import build_engine
-
metadata = MetaData()
-
install_model.ToolShedRepository.table = Table( "tool_shed_repository", metadata,
Column( "id", Integer, primary_key=True ),
Column( "create_time", DateTime, default=now ),
@@ -72,7 +70,6 @@
Column( "repository_path", TEXT ),
Column( "version", Integer ) )
-
mapper( install_model.ToolShedRepository, install_model.ToolShedRepository.table,
properties=dict( tool_versions=relation( install_model.ToolVersion,
primaryjoin=( install_model.ToolShedRepository.table.c.id == install_model.ToolVersion.table.c.tool_shed_repository_id ),
@@ -112,17 +109,13 @@
"""Connect mappings to the database"""
# Load the appropriate db module
engine = build_engine( url, engine_options )
-
# Connect the metadata to the database.
metadata.bind = engine
-
- result = ModelMapping([install_model], engine=engine)
-
+ result = ModelMapping( [ install_model ], engine=engine )
# Create tables if needed
if create_tables:
metadata.create_all()
# metadata.engine.commit()
-
result.create_tables = create_tables
#load local galaxy security policy
return result
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -40,7 +40,7 @@
from galaxy.tools.actions import DefaultToolAction
from galaxy.tools.actions.data_source import DataSourceToolAction
from galaxy.tools.actions.data_manager import DataManagerToolAction
-from galaxy.tools.deps import DependencyManager, INDETERMINATE_DEPENDENCY
+from galaxy.tools.deps import build_dependency_manager
from galaxy.tools.deps.requirements import parse_requirements_from_xml
from galaxy.tools.parameters import check_param, params_from_strings, params_to_strings
from galaxy.tools.parameters.basic import (BaseURLToolParameter,
@@ -700,14 +700,7 @@
return stored.latest_workflow
def init_dependency_manager( self ):
- if self.app.config.use_tool_dependencies:
- dependency_manager_kwds = {
- 'default_base_path': self.app.config.tool_dependency_dir,
- 'conf_file': self.app.config.dependency_resolvers_config_file,
- }
- self.dependency_manager = DependencyManager( **dependency_manager_kwds )
- else:
- self.dependency_manager = None
+ self.dependency_manager = build_dependency_manager( self.app.config )
@property
def sa_session( self ):
@@ -1082,6 +1075,22 @@
:returns: galaxy.jobs.JobDestination -- The destination definition and runner parameters.
"""
return self.app.job_config.get_destination(self.__get_job_tool_configuration(job_params=job_params).destination)
+
+ def get_panel_section( self ):
+ for key, item in self.app.toolbox.integrated_tool_panel.items():
+ if item:
+ if key.startswith( 'tool_' ):
+ if item.id == self.id:
+ return '', ''
+ if key.startswith( 'section_' ):
+ section_id = item.id or ''
+ section_name = item.name or ''
+ for section_key, section_item in item.elems.items():
+ if section_key.startswith( 'tool_' ):
+ if section_item:
+ if section_item.id == self.id:
+ return section_id, section_name
+ return None, None
def parse( self, root, guid=None ):
"""
@@ -3017,6 +3026,8 @@
if io_details:
tool_dict[ 'inputs' ] = [ input.to_dict( trans ) for input in self.inputs.values() ]
tool_dict[ 'outputs' ] = [ output.to_dict() for output in self.outputs.values() ]
+
+ tool_dict[ 'panel_section_id' ], tool_dict[ 'panel_section_name' ] = self.get_panel_section()
return tool_dict
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/tools/deps/__init__.py
--- a/lib/galaxy/tools/deps/__init__.py
+++ b/lib/galaxy/tools/deps/__init__.py
@@ -15,6 +15,31 @@
from galaxy.util.submodules import submodules
+def build_dependency_manager( config ):
+ if config.use_tool_dependencies:
+ dependency_manager_kwds = {
+ 'default_base_path': config.tool_dependency_dir,
+ 'conf_file': config.dependency_resolvers_config_file,
+ }
+ dependency_manager = DependencyManager( **dependency_manager_kwds )
+ else:
+ dependency_manager = NullDependencyManager()
+
+ return dependency_manager
+
+
+class NullDependencyManager( object ):
+
+ def uses_tool_shed_dependencies(self):
+ return False
+
+ def dependency_shell_commands( self, requirements, **kwds ):
+ return []
+
+ def find_dep( self, name, version=None, type='package', **kwds ):
+ return INDETERMINATE_DEPENDENCY
+
+
class DependencyManager( object ):
"""
A DependencyManager attempts to resolve named and versioned dependencies by
@@ -76,6 +101,7 @@
return [
ToolShedPackageDependencyResolver(self),
GalaxyPackageDependencyResolver(self),
+ GalaxyPackageDependencyResolver(self, versionless=True),
]
def __parse_resolver_conf_xml(self, root):
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
--- a/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
+++ b/lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py
@@ -265,6 +265,9 @@
if remove_from_disk_checked:
tool_shed_repository.status = trans.install_model.ToolShedRepository.installation_status.UNINSTALLED
tool_shed_repository.error_message = None
+ # Remove the uninstalled repository and any tool dependencies from the in-memory dictionaries in the
+ # installed_repository_manager.
+ trans.app.installed_repository_manager.handle_repository_uninstall( tool_shed_repository )
else:
tool_shed_repository.status = trans.install_model.ToolShedRepository.installation_status.DEACTIVATED
trans.install_model.context.add( tool_shed_repository )
@@ -1564,7 +1567,7 @@
tool_shed_repository = tool_dependencies[ 0 ].tool_shed_repository
if kwd.get( 'uninstall_tool_dependencies_button', False ):
errors = False
- # Filter tool dependencies to only those that are installed.
+ # Filter tool dependencies to only those that are installed but in an error state.
tool_dependencies_for_uninstallation = []
for tool_dependency in tool_dependencies:
if tool_dependency.can_uninstall:
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/webapps/galaxy/controllers/forms.py
--- a/lib/galaxy/webapps/galaxy/controllers/forms.py
+++ b/lib/galaxy/webapps/galaxy/controllers/forms.py
@@ -65,6 +65,9 @@
global_actions = [
grids.GridAction( "Create new form", dict( controller='forms', action='create_form_definition' ) )
]
+
+ def build_initial_query( self, trans, **kwargs ):
+ return trans.sa_session.query( self.model_class ).join (model.FormDefinition, self.model_class.latest_form_id == model.FormDefinition.id)
class Forms( BaseUIController ):
# Empty TextField
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/webapps/galaxy/controllers/history.py
--- a/lib/galaxy/webapps/galaxy/controllers/history.py
+++ b/lib/galaxy/webapps/galaxy/controllers/history.py
@@ -838,8 +838,8 @@
# Set imported history to be user's current history.
trans.set_history( new_history )
return trans.show_ok_message(
- message="""History "%s" has been imported. <br>You can <a href="%s">start using this history</a> or %s."""
- % ( new_history.name, web.url_for( '/' ), referer_message ), use_panels=True )
+ message="""History "%s" has been imported. <br>You can <a href="%s" onclick="parent.window.location='%s';">start using this history</a> or %s."""
+ % ( new_history.name, web.url_for( '/' ), web.url_for( '/' ), referer_message ), use_panels=True )
elif not user_history or not user_history.datasets or confirm:
new_history = import_history.copy()
new_history.name = "imported: " + new_history.name
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/galaxy/webapps/tool_shed/controllers/repository.py
--- a/lib/galaxy/webapps/tool_shed/controllers/repository.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/repository.py
@@ -932,7 +932,7 @@
repository_type = kwd.get( 'repository_type', rt_util.UNRESTRICTED )
if kwd.get( 'create_repository_button', False ):
error = False
- message = repository_maintenance_util.validate_repository_name( name, trans.user )
+ message = repository_maintenance_util.validate_repository_name( trans.app, name, trans.user )
if message:
error = True
if not description:
@@ -2085,7 +2085,7 @@
repository.long_description = long_description
flush_needed = True
if repository.times_downloaded == 0 and repo_name != repository.name:
- message = repository_maintenance_util.validate_repository_name( repo_name, user )
+ message = repository_maintenance_util.validate_repository_name( trans.app, repo_name, user )
if message:
error = True
else:
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/galaxy_install/__init__.py
--- a/lib/tool_shed/galaxy_install/__init__.py
+++ b/lib/tool_shed/galaxy_install/__init__.py
@@ -1,54 +0,0 @@
-"""
-Classes encapsulating the management of repositories installed from Galaxy tool sheds.
-"""
-import os, logging
-import tool_shed.util.shed_util_common
-import tool_shed.util.datatype_util
-from galaxy.model.orm import and_
-from tool_shed.util import xml_util
-
-log = logging.getLogger( __name__ )
-
-class InstalledRepositoryManager( object ):
- def __init__( self, app ):
- self.app = app
- self.install_model = self.app.install_model
- self.context = self.install_model.context
- self.tool_configs = self.app.config.tool_configs
- if self.app.config.migrated_tools_config not in self.tool_configs:
- self.tool_configs.append( self.app.config.migrated_tools_config )
- self.installed_repository_dicts = []
- def get_repository_install_dir( self, tool_shed_repository ):
- for tool_config in self.tool_configs:
- tree, error_message = xml_util.parse_xml( tool_config )
- if tree is None:
- return None
- root = tree.getroot()
- tool_path = root.get( 'tool_path', None )
- if tool_path:
- ts = tool_shed.util.shed_util_common.clean_tool_shed_url( tool_shed_repository.tool_shed )
- relative_path = os.path.join( tool_path,
- ts,
- 'repos',
- tool_shed_repository.owner,
- tool_shed_repository.name,
- tool_shed_repository.installed_changeset_revision )
- if os.path.exists( relative_path ):
- return relative_path
- return None
- def load_proprietary_datatypes( self ):
- for tool_shed_repository in self.context.query( self.install_model.ToolShedRepository ) \
- .filter( and_( self.install_model.ToolShedRepository.table.c.includes_datatypes==True,
- self.install_model.ToolShedRepository.table.c.deleted==False ) ) \
- .order_by( self.install_model.ToolShedRepository.table.c.id ):
- relative_install_dir = self.get_repository_install_dir( tool_shed_repository )
- if relative_install_dir:
- installed_repository_dict = tool_shed.util.datatype_util.load_installed_datatypes( self.app, tool_shed_repository, relative_install_dir )
- if installed_repository_dict:
- self.installed_repository_dicts.append( installed_repository_dict )
- def load_proprietary_converters_and_display_applications( self, deactivate=False ):
- for installed_repository_dict in self.installed_repository_dicts:
- if installed_repository_dict[ 'converter_path' ]:
- tool_shed.util.datatype_util.load_installed_datatype_converters( self.app, installed_repository_dict, deactivate=deactivate )
- if installed_repository_dict[ 'display_path' ]:
- tool_shed.util.datatype_util.load_installed_display_applications( self.app, installed_repository_dict, deactivate=deactivate )
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/galaxy_install/grids/admin_toolshed_grids.py
--- a/lib/tool_shed/galaxy_install/grids/admin_toolshed_grids.py
+++ b/lib/tool_shed/galaxy_install/grids/admin_toolshed_grids.py
@@ -31,6 +31,13 @@
latest_revision_tip_str = ''
return '<img src="%s/june_2007_style/blue/ok_small.png" %s/>' % ( url_for( '/static' ), latest_revision_tip_str )
+def generate_repository_can_be_uninstalled_img_str( include_mouse_over=False ):
+ if include_mouse_over:
+ can_be_uninstalled_tip_str = 'class="icon-button" title="This repository can be uninstalled"'
+ else:
+ can_be_uninstalled_tip_str = ''
+ return '<img src="%s/images/fugue/toggle-bw.png" %s/>' % ( url_for( '/static' ), can_be_uninstalled_tip_str )
+
def generate_revision_updates_img_str( include_mouse_over=False ):
if include_mouse_over:
revision_updates_tip_str = 'class="icon-button" title="Updates are available in the Tool Shed for this revision"'
@@ -73,6 +80,8 @@
tool_shed_status_str += generate_includes_workflows_img_str( include_mouse_over=True )
else:
tool_shed_status_str = generate_unknown_img_str( include_mouse_over=True )
+ if tool_shed_repository.can_uninstall( trans.app ):
+ tool_shed_status_str += generate_repository_can_be_uninstalled_img_str( include_mouse_over=True )
return tool_shed_status_str
@@ -210,6 +219,7 @@
legend_str += '%s This repository is deprecated in the Tool Shed<br/>' % generate_deprecated_repository_img_str()
legend_str += '%s This repository contains exported workflows<br/>' % generate_includes_workflows_img_str()
legend_str += '%s Unable to get information from the Tool Shed<br/>' % generate_unknown_img_str()
+ legend_str += '%s This repository can be uninstalled<br/>' % generate_repository_can_be_uninstalled_img_str()
return legend_str
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/galaxy_install/installed_repository_manager.py
--- /dev/null
+++ b/lib/tool_shed/galaxy_install/installed_repository_manager.py
@@ -0,0 +1,368 @@
+"""
+Class encapsulating the management of repositories installed from Galaxy tool sheds.
+"""
+import logging
+import os
+import tool_shed.util.shed_util_common as suc
+from tool_shed.util import datatype_util
+from tool_shed.util import repository_dependency_util
+from tool_shed.util import tool_dependency_util
+from tool_shed.util import xml_util
+from galaxy.model.orm import and_
+
+log = logging.getLogger( __name__ )
+
+
+class InstalledRepositoryManager( object ):
+
+ def __init__( self, app ):
+ """
+ Among other things, keep in in-memory sets of tuples defining installed repositories and tool dependencies along with
+ the relationships between each of them. This will allow for quick discovery of those repositories or components that
+ can be uninstalled. The feature allowing a Galaxy administrator to uninstall a repository should not be available to
+ repositories or tool dependency packages that are required by other repositories or their contents (packages). The
+ uninstall feature should be available only at the repository hierarchy level where every dependency will be uninstalled.
+ The exception for this is if an item (repository or tool dependency package) is not in an INSTALLED state - in these
+ cases, the specific item can be uninstalled in order to attempt re-installation.
+ """
+ self.app = app
+ self.install_model = self.app.install_model
+ self.context = self.install_model.context
+ self.tool_configs = self.app.config.tool_configs
+ if self.app.config.migrated_tools_config not in self.tool_configs:
+ self.tool_configs.append( self.app.config.migrated_tools_config )
+ self.installed_repository_dicts = []
+ # Keep an in-memory dictionary whose keys are tuples defining tool_shed_repository objects (whose status is 'Installed')
+ # and whose values are a list of tuples defining tool_shed_repository objects (whose status can be anything) required by
+ # the key. The value defines the entire repository dependency tree.
+ self.repository_dependencies_of_installed_repositories = {}
+ # Keep an in-memory dictionary whose keys are tuples defining tool_shed_repository objects (whose status is 'Installed')
+ # and whose values are a list of tuples defining tool_shed_repository objects (whose status is 'Installed') required by
+ # the key. The value defines the entire repository dependency tree.
+ self.installed_repository_dependencies_of_installed_repositories = {}
+ # Keep an in-memory dictionary whose keys are tuples defining tool_shed_repository objects (whose status is 'Installed')
+ # and whose values are a list of tuples defining tool_shed_repository objects (whose status is 'Installed') that require
+ # the key.
+ self.installed_dependent_repositories_of_installed_repositories = {}
+ # Keep an in-memory dictionary whose keys are tuples defining tool_shed_repository objects (whose status is 'Installed')
+ # and whose values are a list of tuples defining its immediate tool_dependency objects (whose status can be anything).
+ # The value defines only the immediate tool dependencies of the repository and does not include any dependencies of the
+ # tool dependencies.
+ self.tool_dependencies_of_installed_repositories = {}
+ # Keep an in-memory dictionary whose keys are tuples defining tool_shed_repository objects (whose status is 'Installed')
+ # and whose values are a list of tuples defining its immediate tool_dependency objects (whose status is 'Installed').
+ # The value defines only the immediate tool dependencies of the repository and does not include any dependencies of the
+ # tool dependencies.
+ self.installed_tool_dependencies_of_installed_repositories = {}
+ # Keep an in-memory dictionary whose keys are tuples defining tool_dependency objects (whose status is 'Installed') and
+ # whose values are a list of tuples defining tool_dependency objects (whose status can be anything) required by the
+ # installed tool dependency at runtime. The value defines the entire tool dependency tree.
+ self.runtime_tool_dependencies_of_installed_tool_dependencies = {}
+ # Keep an in-memory dictionary whose keys are tuples defining tool_dependency objects (whose status is 'Installed') and
+ # whose values are a list of tuples defining tool_dependency objects (whose status is 'Installed') that require the key
+ # at runtime. The value defines the entire tool dependency tree.
+ self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies = {}
+ # Load defined dependency relationships for installed tool shed repositories and their contents.
+ self.load_dependency_relationships()
+
+ def add_entry_to_installed_repository_dependencies_of_installed_repositories( self, repository ):
+ """
+ Add an entry to self.installed_repository_dependencies_of_installed_repositories. A side-effect of this method
+ is the population of self.installed_dependent_repositories_of_installed_repositories. Since this method discovers
+ all repositories required by the received repository, it can use the list to add entries to the reverse dictionary.
+ """
+ repository_tup = repository_dependency_util.get_repository_tuple_for_installed_repository_manager( repository )
+ tool_shed, name, owner, installed_changeset_revision = repository_tup
+ # Get the list of repository dependencies for this repository.
+ status = self.install_model.ToolShedRepository.installation_status.INSTALLED
+ repository_dependency_tups = \
+ repository_dependency_util.get_repository_dependency_tups_for_repository( self.app, repository, status=status )
+ # Add an entry to self.installed_repository_dependencies_of_installed_repositories.
+ if repository_tup not in self.installed_repository_dependencies_of_installed_repositories:
+ debug_msg = "Adding an entry for revision %s of repository %s owned by %s " % ( installed_changeset_revision, name, owner )
+ debug_msg += "to installed_repository_dependencies_of_installed_repositories."
+ log.debug( debug_msg )
+ self.installed_repository_dependencies_of_installed_repositories[ repository_tup ] = repository_dependency_tups
+ # Use the repository_dependency_tups to add entries to the reverse dictionary
+ # self.installed_dependent_repositories_of_installed_repositories.
+ for required_repository_tup in repository_dependency_tups:
+ debug_msg = "Appending revision %s of repository %s owned by %s " % ( installed_changeset_revision, name, owner )
+ debug_msg += "to all dependent repositories in installed_dependent_repositories_of_installed_repositories."
+ log.debug( debug_msg )
+ if required_repository_tup in self.installed_dependent_repositories_of_installed_repositories:
+ self.installed_dependent_repositories_of_installed_repositories[ required_repository_tup ].append( repository_tup )
+ else:
+ self.installed_dependent_repositories_of_installed_repositories[ required_repository_tup ] = [ repository_tup ]
+
+ def add_entry_to_installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies( self, tool_dependency ):
+ """Add an entry to self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies."""
+ tool_dependency_tup = tool_dependency_util.get_tool_dependency_tuple_for_installed_repository_manager( tool_dependency )
+ if tool_dependency_tup not in self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies:
+ tool_shed_repository_id, name, version, type = tool_dependency_tup
+ debug_msg = "Adding an entry for version %s of %s %s " % ( version, type, name )
+ debug_msg += "to installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies."
+ log.debug( debug_msg )
+ status = self.install_model.ToolDependency.installation_status.INSTALLED
+ installed_runtime_dependent_tool_dependency_tups = \
+ tool_dependency_util.get_runtime_dependent_tool_dependency_tuples( self.app, tool_dependency, status=status )
+ self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies[ tool_dependency_tup ] = \
+ installed_runtime_dependent_tool_dependency_tups
+
+ def add_entry_to_installed_tool_dependencies_of_installed_repositories( self, repository ):
+ """Add an entry to self.installed_tool_dependencies_of_installed_repositories."""
+ repository_tup = repository_dependency_util.get_repository_tuple_for_installed_repository_manager( repository )
+ if repository_tup not in self.installed_tool_dependencies_of_installed_repositories:
+ tool_shed, name, owner, installed_changeset_revision = repository_tup
+ debug_msg = "Adding an entry for revision %s of repository %s owned by %s " % ( installed_changeset_revision, name, owner )
+ debug_msg += "to installed_tool_dependencies_of_installed_repositories."
+ log.debug( debug_msg )
+ installed_tool_dependency_tups = []
+ for tool_dependency in repository.tool_dependencies:
+ if tool_dependency.status == self.app.install_model.ToolDependency.installation_status.INSTALLED:
+ tool_dependency_tup = tool_dependency_util.get_tool_dependency_tuple_for_installed_repository_manager( tool_dependency )
+ installed_tool_dependency_tups.append( tool_dependency_tup )
+ self.installed_tool_dependencies_of_installed_repositories[ repository_tup ] = installed_tool_dependency_tups
+
+ def add_entry_to_repository_dependencies_of_installed_repositories( self, repository ):
+ """Add an entry to self.repository_dependencies_of_installed_repositories."""
+ repository_tup = repository_dependency_util.get_repository_tuple_for_installed_repository_manager( repository )
+ if repository_tup not in self.repository_dependencies_of_installed_repositories:
+ tool_shed, name, owner, installed_changeset_revision = repository_tup
+ debug_msg = "Adding an entry for revision %s of repository %s owned by %s " % ( installed_changeset_revision, name, owner )
+ debug_msg += "to repository_dependencies_of_installed_repositories."
+ log.debug( debug_msg )
+ repository_dependency_tups = \
+ repository_dependency_util.get_repository_dependency_tups_for_repository( self.app, repository, status=None )
+ self.repository_dependencies_of_installed_repositories[ repository_tup ] = repository_dependency_tups
+
+ def add_entry_to_runtime_tool_dependencies_of_installed_tool_dependencies( self, tool_dependency ):
+ """Add an entry to self.runtime_tool_dependencies_of_installed_tool_dependencies."""
+ tool_dependency_tup = tool_dependency_util.get_tool_dependency_tuple_for_installed_repository_manager( tool_dependency )
+ if tool_dependency_tup not in self.runtime_tool_dependencies_of_installed_tool_dependencies:
+ tool_shed_repository_id, name, version, type = tool_dependency_tup
+ debug_msg = "Adding an entry for version %s of %s %s " % ( version, type, name )
+ debug_msg += "to runtime_tool_dependencies_of_installed_tool_dependencies."
+ log.debug( debug_msg )
+ runtime_dependent_tool_dependency_tups = \
+ tool_dependency_util.get_runtime_dependent_tool_dependency_tuples( self.app, tool_dependency, status=None )
+ self.runtime_tool_dependencies_of_installed_tool_dependencies[ tool_dependency_tup ] = \
+ runtime_dependent_tool_dependency_tups
+
+ def add_entry_to_tool_dependencies_of_installed_repositories( self, repository ):
+ """Add an entry to self.tool_dependencies_of_installed_repositories."""
+ repository_tup = repository_dependency_util.get_repository_tuple_for_installed_repository_manager( repository )
+ if repository_tup not in self.tool_dependencies_of_installed_repositories:
+ tool_shed, name, owner, installed_changeset_revision = repository_tup
+ debug_msg = "Adding an entry for revision %s of repository %s owned by %s " % ( installed_changeset_revision, name, owner )
+ debug_msg += "to tool_dependencies_of_installed_repositories."
+ log.debug( debug_msg )
+ tool_dependency_tups = []
+ for tool_dependency in repository.tool_dependencies:
+ tool_dependency_tup = tool_dependency_util.get_tool_dependency_tuple_for_installed_repository_manager( tool_dependency )
+ tool_dependency_tups.append( tool_dependency_tup )
+ self.tool_dependencies_of_installed_repositories[ repository_tup ] = tool_dependency_tups
+
+ def get_containing_repository_for_tool_dependency( self, tool_dependency_tup ):
+ tool_shed_repository_id, name, version, type = tool_dependency_tup
+ return self.app.install_model.context.query( self.app.install_model.ToolShedRepository ).get( tool_shed_repository_id )
+
+ def get_repository_install_dir( self, tool_shed_repository ):
+ for tool_config in self.tool_configs:
+ tree, error_message = xml_util.parse_xml( tool_config )
+ if tree is None:
+ return None
+ root = tree.getroot()
+ tool_path = root.get( 'tool_path', None )
+ if tool_path:
+ ts = suc.clean_tool_shed_url( tool_shed_repository.tool_shed )
+ relative_path = os.path.join( tool_path,
+ ts,
+ 'repos',
+ str( tool_shed_repository.owner ),
+ str( tool_shed_repository.name ),
+ str( tool_shed_repository.installed_changeset_revision ) )
+ if os.path.exists( relative_path ):
+ return relative_path
+ return None
+
+ def handle_repository_install( self, repository ):
+ """Load the dependency relationships for a repository that was just installed or reinstalled."""
+ # Populate self.repository_dependencies_of_installed_repositories.
+ self.add_entry_to_repository_dependencies_of_installed_repositories( repository )
+ # Populate self.installed_repository_dependencies_of_installed_repositories.
+ self.add_entry_to_installed_repository_dependencies_of_installed_repositories( repository )
+ # Populate self.tool_dependencies_of_installed_repositories.
+ self.add_entry_to_tool_dependencies_of_installed_repositories( repository )
+ # Populate self.installed_tool_dependencies_of_installed_repositories.
+ self.add_entry_to_installed_tool_dependencies_of_installed_repositories( repository )
+ for tool_dependency in repository.tool_dependencies:
+ # Populate self.runtime_tool_dependencies_of_installed_tool_dependencies.
+ self.add_entry_to_runtime_tool_dependencies_of_installed_tool_dependencies( tool_dependency )
+ # Populate self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies.
+ self.add_entry_to_installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies( tool_dependency )
+
+ def handle_repository_uninstall( self, repository ):
+ """Remove the dependency relationships for a repository that was just uninstalled."""
+ for tool_dependency in repository.tool_dependencies:
+ tool_dependency_tup = tool_dependency_util.get_tool_dependency_tuple_for_installed_repository_manager( tool_dependency )
+ # Remove this tool_dependency from all values in
+ # self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies
+ altered_installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies = {}
+ for td_tup, installed_runtime_dependent_tool_dependency_tups in \
+ self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies.items():
+ if tool_dependency_tup in installed_runtime_dependent_tool_dependency_tups:
+ # Remove the tool_dependency from the list.
+ installed_runtime_dependent_tool_dependency_tups.remove( tool_dependency_tup )
+ # Add the possibly altered list to the altered dictionary.
+ altered_installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies[ td_tup ] = \
+ installed_runtime_dependent_tool_dependency_tups
+ self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies = \
+ altered_installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies
+ # Remove the entry for this tool_dependency from self.runtime_tool_dependencies_of_installed_tool_dependencies.
+ self.remove_entry_from_runtime_tool_dependencies_of_installed_tool_dependencies( tool_dependency )
+ # Remove the entry for this tool_dependency from
+ # self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies.
+ self.remove_entry_from_installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies( tool_dependency )
+ # Remove this repository's entry from self.installed_tool_dependencies_of_installed_repositories.
+ self.remove_entry_from_installed_tool_dependencies_of_installed_repositories( repository )
+ # Remove this repository's entry from self.tool_dependencies_of_installed_repositories
+ self.remove_entry_from_tool_dependencies_of_installed_repositories( repository )
+ # Remove this repository's entry from self.installed_repository_dependencies_of_installed_repositories.
+ self.remove_entry_from_installed_repository_dependencies_of_installed_repositories( repository )
+ # Remove this repository's entry from self.repository_dependencies_of_installed_repositories.
+ self.remove_entry_from_repository_dependencies_of_installed_repositories( repository )
+
+ def handle_tool_dependency_install( self, repository, tool_dependency ):
+ """Load the dependency relationships for a tool dependency that was just installed independently of its containing repository."""
+ # The received repository must have a status of 'Installed'. The value of tool_dependency.status will either be
+ # 'Installed' or 'Error', but we only need to change the in-memory dictionaries if it is 'Installed'.
+ if tool_dependency.is_installed:
+ # Populate self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies.
+ self.add_entry_to_installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies( tool_dependency )
+ # Populate self.installed_tool_dependencies_of_installed_repositories.
+ repository_tup = repository_dependency_util.get_repository_tuple_for_installed_repository_manager( repository )
+ tool_dependency_tup = tool_dependency_util.get_tool_dependency_tuple_for_installed_repository_manager( tool_dependency )
+ if repository_tup in self.installed_tool_dependencies_of_installed_repositories:
+ self.installed_tool_dependencies_of_installed_repositories[ repository_tup ].append( tool_dependency_tup )
+ else:
+ self.installed_tool_dependencies_of_installed_repositories[ repository_tup ] = [ tool_dependency_tup ]
+
+ def load_dependency_relationships( self ):
+ """Load relationships for all installed repositories and tool dependencies into in-memnory dictionaries."""
+ # Get the list of installed tool shed repositories.
+ for repository in self.context.query( self.app.install_model.ToolShedRepository ) \
+ .filter( self.app.install_model.ToolShedRepository.table.c.status ==
+ self.app.install_model.ToolShedRepository.installation_status.INSTALLED ):
+ # Populate self.repository_dependencies_of_installed_repositories.
+ self.add_entry_to_repository_dependencies_of_installed_repositories( repository )
+ # Populate self.installed_repository_dependencies_of_installed_repositories.
+ self.add_entry_to_installed_repository_dependencies_of_installed_repositories( repository )
+ # Populate self.tool_dependencies_of_installed_repositories.
+ self.add_entry_to_tool_dependencies_of_installed_repositories( repository )
+ # Populate self.installed_tool_dependencies_of_installed_repositories.
+ self.add_entry_to_installed_tool_dependencies_of_installed_repositories( repository )
+ # Get the list of installed tool dependencies.
+ for tool_dependency in self.context.query( self.app.install_model.ToolDependency ) \
+ .filter( self.app.install_model.ToolDependency.table.c.status ==
+ self.app.install_model.ToolDependency.installation_status.INSTALLED ):
+ # Populate self.runtime_tool_dependencies_of_installed_tool_dependencies.
+ self.add_entry_to_runtime_tool_dependencies_of_installed_tool_dependencies( tool_dependency )
+ # Populate self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies.
+ self.add_entry_to_installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies( tool_dependency )
+
+ def load_proprietary_datatypes( self ):
+ for tool_shed_repository in self.context.query( self.install_model.ToolShedRepository ) \
+ .filter( and_( self.install_model.ToolShedRepository.table.c.includes_datatypes==True,
+ self.install_model.ToolShedRepository.table.c.deleted==False ) ) \
+ .order_by( self.install_model.ToolShedRepository.table.c.id ):
+ relative_install_dir = self.get_repository_install_dir( tool_shed_repository )
+ if relative_install_dir:
+ installed_repository_dict = datatype_util.load_installed_datatypes( self.app, tool_shed_repository, relative_install_dir )
+ if installed_repository_dict:
+ self.installed_repository_dicts.append( installed_repository_dict )
+
+ def load_proprietary_converters_and_display_applications( self, deactivate=False ):
+ for installed_repository_dict in self.installed_repository_dicts:
+ if installed_repository_dict[ 'converter_path' ]:
+ datatype_util.load_installed_datatype_converters( self.app, installed_repository_dict, deactivate=deactivate )
+ if installed_repository_dict[ 'display_path' ]:
+ datatype_util.load_installed_display_applications( self.app, installed_repository_dict, deactivate=deactivate )
+
+ def remove_entry_from_installed_repository_dependencies_of_installed_repositories( self, repository ):
+ """
+ Remove an entry from self.installed_repository_dependencies_of_installed_repositories. A side-effect of this method
+ is removal of appropriate value items from self.installed_dependent_repositories_of_installed_repositories.
+ """
+ # Remove tuples defining this repository from value lists in self.installed_dependent_repositories_of_installed_repositories.
+ repository_tup = repository_dependency_util.get_repository_tuple_for_installed_repository_manager( repository )
+ tool_shed, name, owner, installed_changeset_revision = repository_tup
+ altered_installed_dependent_repositories_of_installed_repositories = {}
+ for r_tup, v_tups in self.installed_dependent_repositories_of_installed_repositories.items():
+ if repository_tup in v_tups:
+ debug_msg = "Removing entry for revision %s of repository %s owned by %s " % \
+ ( installed_changeset_revision, name, owner )
+ r_tool_shed, r_name, r_owner, r_installed_changeset_revision = r_tup
+ debug_msg += "from the dependent list for revision %s of repository %s owned by %s " % \
+ ( r_installed_changeset_revision, r_name, r_owner )
+ debug_msg += "in installed_repository_dependencies_of_installed_repositories."
+ log.debug( debug_msg )
+ v_tups.remove( repository_tup )
+ altered_installed_dependent_repositories_of_installed_repositories[ r_tup ] = v_tups
+ self.installed_dependent_repositories_of_installed_repositories = \
+ altered_installed_dependent_repositories_of_installed_repositories
+ # Remove this repository's entry from self.installed_repository_dependencies_of_installed_repositories.
+ if repository_tup in self.installed_repository_dependencies_of_installed_repositories:
+ debug_msg = "Removing entry for revision %s of repository %s owned by %s " % ( installed_changeset_revision, name, owner )
+ debug_msg += "from installed_repository_dependencies_of_installed_repositories."
+ log.debug( debug_msg )
+ del self.installed_repository_dependencies_of_installed_repositories[ repository_tup ]
+
+ def remove_entry_from_installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies( self, tool_dependency ):
+ """Remove an entry from self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies."""
+ tool_dependency_tup = tool_dependency_util.get_tool_dependency_tuple_for_installed_repository_manager( tool_dependency )
+ if tool_dependency_tup in self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies:
+ tool_shed_repository_id, name, version, type = tool_dependency_tup
+ debug_msg = "Removing entry for version %s of %s %s " % ( version, type, name )
+ debug_msg += "from installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies."
+ log.debug( debug_msg )
+ del self.installed_runtime_dependent_tool_dependencies_of_installed_tool_dependencies[ tool_dependency_tup ]
+
+ def remove_entry_from_installed_tool_dependencies_of_installed_repositories( self, repository ):
+ """Remove an entry from self.installed_tool_dependencies_of_installed_repositories."""
+ repository_tup = repository_dependency_util.get_repository_tuple_for_installed_repository_manager( repository )
+ if repository_tup in self.installed_tool_dependencies_of_installed_repositories:
+ tool_shed, name, owner, installed_changeset_revision = repository_tup
+ debug_msg = "Removing entry for revision %s of repository %s owned by %s " % ( installed_changeset_revision, name, owner )
+ debug_msg += "from installed_tool_dependencies_of_installed_repositories."
+ log.debug( debug_msg )
+ del self.installed_tool_dependencies_of_installed_repositories[ repository_tup ]
+
+ def remove_entry_from_repository_dependencies_of_installed_repositories( self, repository ):
+ """Remove an entry from self.repository_dependencies_of_installed_repositories."""
+ repository_tup = repository_dependency_util.get_repository_tuple_for_installed_repository_manager( repository )
+ if repository_tup in self.repository_dependencies_of_installed_repositories:
+ tool_shed, name, owner, installed_changeset_revision = repository_tup
+ debug_msg = "Removing entry for revision %s of repository %s owned by %s " % ( installed_changeset_revision, name, owner )
+ debug_msg += "from repository_dependencies_of_installed_repositories."
+ log.debug( debug_msg )
+ del self.repository_dependencies_of_installed_repositories[ repository_tup ]
+
+ def remove_entry_from_runtime_tool_dependencies_of_installed_tool_dependencies( self, tool_dependency ):
+ """Remove an entry from self.runtime_tool_dependencies_of_installed_tool_dependencies."""
+ tool_dependency_tup = tool_dependency_util.get_tool_dependency_tuple_for_installed_repository_manager( tool_dependency )
+ if tool_dependency_tup in self.runtime_tool_dependencies_of_installed_tool_dependencies:
+ tool_shed_repository_id, name, version, type = tool_dependency_tup
+ debug_msg = "Removing entry for version %s of %s %s from runtime_tool_dependencies_of_installed_tool_dependencies." % \
+ ( version, type, name )
+ log.debug( debug_msg )
+ del self.runtime_tool_dependencies_of_installed_tool_dependencies[ tool_dependency_tup ]
+
+ def remove_entry_from_tool_dependencies_of_installed_repositories( self, repository ):
+ """Remove an entry from self.tool_dependencies_of_installed_repositories."""
+ repository_tup = repository_dependency_util.get_repository_tuple_for_installed_repository_manager( repository )
+ if repository_tup in self.tool_dependencies_of_installed_repositories:
+ tool_shed, name, owner, installed_changeset_revision = repository_tup
+ debug_msg = "Removing entry for revision %s of repository %s owned by %s from tool_dependencies_of_installed_repositories." % \
+ ( installed_changeset_revision, name, owner )
+ log.debug( debug_msg )
+ del self.tool_dependencies_of_installed_repositories[ repository_tup ]
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/galaxy_install/migrate/common.py
--- a/lib/tool_shed/galaxy_install/migrate/common.py
+++ b/lib/tool_shed/galaxy_install/migrate/common.py
@@ -2,18 +2,10 @@
import os
import sys
import galaxy.config
-import galaxy.datatypes.registry
-from galaxy import tools
-from galaxy.tools.data import ToolDataTableManager
-from galaxy.web import security
-import galaxy.model.mapping
-import galaxy.tools.search
-from galaxy.objectstore import build_object_store_from_config
-import tool_shed.tool_shed_registry
from tool_shed.galaxy_install import install_manager
-class MigrateToolsApplication( object ):
+class MigrateToolsApplication( object, galaxy.config.ConfiguresGalaxyMixin ):
"""Encapsulates the state of a basic Galaxy Universe application in order to initiate the Install Manager"""
def __init__( self, tools_migration_config ):
@@ -33,41 +25,23 @@
for key, value in config_parser.items( "app:main" ):
galaxy_config_dict[ key ] = value
self.config = galaxy.config.Configuration( **galaxy_config_dict )
- if not self.config.database_connection:
- self.config.database_connection = "sqlite:///%s?isolation_level=IMMEDIATE" % self.config.database
+
self.config.update_integrated_tool_panel = True
- self.object_store = build_object_store_from_config( self.config )
- # Security helper
- self.security = security.SecurityHelper( id_secret=self.config.id_secret )
- # Setup the database engine and ORM
- self.model = galaxy.model.mapping.init( self.config.file_path,
- self.config.database_connection,
- engine_options={},
- create_tables=False,
- object_store=self.object_store )
- # Create an empty datatypes registry.
- self.datatypes_registry = galaxy.datatypes.registry.Registry()
- # Load the data types in the Galaxy distribution, which are defined in self.config.datatypes_config.
- self.datatypes_registry.load_datatypes( self.config.root, self.config.datatypes_config )
- # Initialize tool data tables using the config defined by self.config.tool_data_table_config_path.
- self.tool_data_tables = ToolDataTableManager( tool_data_path=self.config.tool_data_path,
- config_filename=self.config.tool_data_table_config_path )
- # Load additional entries defined by self.config.shed_tool_data_table_config into tool data tables.
- self.tool_data_tables.load_from_config_file( config_filename=self.config.shed_tool_data_table_config,
- tool_data_path=self.tool_data_tables.tool_data_path,
- from_shed_config=True )
- # Initialize the tools, making sure the list of tool configs includes the reserved migrated_tools_conf.xml file.
- tool_configs = self.config.tool_configs
- if self.config.migrated_tools_config not in tool_configs:
- tool_configs.append( self.config.migrated_tools_config )
- self.toolbox = tools.ToolBox( tool_configs, self.config.tool_path, self )
- # Search support for tools
- self.toolbox_search = galaxy.tools.search.ToolBoxSearch( self.toolbox )
- # Set up the tool sheds registry.
- if os.path.isfile( self.config.tool_sheds_config ):
- self.tool_shed_registry = tool_shed.tool_shed_registry.Registry( self.config.root, self.config.tool_sheds_config )
- else:
- self.tool_shed_registry = None
+
+ self._configure_object_store()
+
+ self._configure_security()
+
+ self._configure_models()
+
+ self._configure_datatypes_registry( )
+
+ self._configure_tool_data_tables( from_shed_config=True )
+
+ self._configure_toolbox()
+
+ self._configure_tool_shed_registry()
+
# Get the latest tool migration script number to send to the Install manager.
latest_migration_script_number = int( tools_migration_config.split( '_' )[ 0 ] )
# The value of migrated_tools_config is migrated_tools_conf.xml, and is reserved for containing only those tools that have been
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/galaxy_install/repository_util.py
--- a/lib/tool_shed/galaxy_install/repository_util.py
+++ b/lib/tool_shed/galaxy_install/repository_util.py
@@ -504,8 +504,8 @@
query = trans.install_model.context.query( trans.install_model.ToolShedRepository ).filter( or_( *clause_list ) )
return encoded_kwd, query, tool_shed_repositories, encoded_repository_ids
-def install_tool_shed_repository( trans, tool_shed_repository, repo_info_dict, tool_panel_section_key, shed_tool_conf, tool_path, install_tool_dependencies,
- reinstalling=False ):
+def install_tool_shed_repository( trans, tool_shed_repository, repo_info_dict, tool_panel_section_key, shed_tool_conf, tool_path,
+ install_tool_dependencies, reinstalling=False ):
if tool_panel_section_key:
try:
tool_section = trans.app.toolbox.tool_panel[ tool_panel_section_key ]
@@ -579,7 +579,11 @@
tool_dependencies_config=tool_dependencies_config,
tool_dependencies=tool_shed_repository.tool_dependencies )
suc.remove_dir( work_dir )
- suc.update_tool_shed_repository_status( trans.app, tool_shed_repository, trans.install_model.ToolShedRepository.installation_status.INSTALLED )
+ suc.update_tool_shed_repository_status( trans.app,
+ tool_shed_repository,
+ trans.install_model.ToolShedRepository.installation_status.INSTALLED )
+ # Add the installed repository and any tool dependencies to the in-memory dictionaries in the installed_repository_manager.
+ trans.app.installed_repository_manager.handle_repository_install( tool_shed_repository )
else:
# An error occurred while cloning the repository, so reset everything necessary to enable another attempt.
set_repository_attributes( trans,
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/galaxy_install/update_manager.py
--- a/lib/tool_shed/galaxy_install/update_manager.py
+++ b/lib/tool_shed/galaxy_install/update_manager.py
@@ -30,7 +30,7 @@
# repository. This information includes items like newer installable repository revisions, current revision updates, whether
# the repository revision is the latest installable revision, and whether the repository has been deprecated in the tool shed.
for repository in self.context.query( self.app.install_model.ToolShedRepository ) \
- .filter( self.app.install_model.ToolShedRepository.table.c.deleted == False ):
+ .filter( self.app.install_model.ToolShedRepository.table.c.deleted == False ):
tool_shed_status_dict = suc.get_tool_shed_status_for_installed_repository( self.app, repository )
if tool_shed_status_dict:
if tool_shed_status_dict != repository.tool_shed_status:
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/scripts/check_repositories_for_functional_tests.py
--- a/lib/tool_shed/scripts/check_repositories_for_functional_tests.py
+++ b/lib/tool_shed/scripts/check_repositories_for_functional_tests.py
@@ -253,6 +253,11 @@
# a test_environment entry. If we use it we need to temporarily eliminate it from the list of tool_test_results_dicts
# since it will be re-inserted later.
tool_test_results_dict = tool_test_results_dicts.pop( 0 )
+ elif len( tool_test_results_dict ) == 2 and \
+ 'test_environment' in tool_test_results_dict and 'missing_test_components' in tool_test_results_dict:
+ # We can re-use tool_test_results_dict if its only entries are "test_environment" and "missing_test_components".
+ # In this case, some tools are missing tests components while others are not.
+ tool_test_results_dict = tool_test_results_dicts.pop( 0 )
else:
# The latest tool_test_results_dict has been populated with the results of a test run, so it cannot be used.
tool_test_results_dict = {}
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/scripts/check_tool_dependency_definition_repositories.py
--- a/lib/tool_shed/scripts/check_tool_dependency_definition_repositories.py
+++ b/lib/tool_shed/scripts/check_tool_dependency_definition_repositories.py
@@ -41,6 +41,7 @@
class RepositoriesApplication( object ):
"""Encapsulates the state of a Universe application"""
+
def __init__( self, config ):
if config.database_connection is False:
config.database_connection = "sqlite:///%s?isolation_level=IMMEDIATE" % config.database
@@ -133,11 +134,12 @@
# Do not check metadata records that have an entry in the skip_tool_tests table, since they won't be tested anyway.
skip_metadata_ids = select( [ app.model.SkipToolTest.table.c.repository_metadata_id ] )
# Get the list of metadata records to check, restricting it to records that have not been flagged do_not_test.
- for repository_metadata in app.sa_session.query( app.model.RepositoryMetadata ) \
- .filter( and_( app.model.RepositoryMetadata.table.c.downloadable == True,
- app.model.RepositoryMetadata.table.c.do_not_test == False,
- app.model.RepositoryMetadata.table.c.repository_id.in_( tool_dependency_defintion_repository_ids ),
- not_( app.model.RepositoryMetadata.table.c.id.in_( skip_metadata_ids ) ) ) ):
+ for repository_metadata in \
+ app.sa_session.query( app.model.RepositoryMetadata ) \
+ .filter( and_( app.model.RepositoryMetadata.table.c.downloadable == True,
+ app.model.RepositoryMetadata.table.c.do_not_test == False,
+ app.model.RepositoryMetadata.table.c.repository_id.in_( tool_dependency_defintion_repository_ids ),
+ not_( app.model.RepositoryMetadata.table.c.id.in_( skip_metadata_ids ) ) ) ):
records_checked += 1
# Check the next repository revision.
changeset_revision = str( repository_metadata.changeset_revision )
@@ -161,9 +163,31 @@
invalid_metadata += 1
if not info_only:
# Create the tool_test_results_dict dictionary, using the dictionary from the previous test run if available.
- if repository_metadata.tool_test_results:
- tool_test_results_dict = repository_metadata.tool_test_results
+ if repository_metadata.tool_test_results is not None:
+ # We'll listify the column value in case it uses the old approach of storing the results of only a single test run.
+ tool_test_results_dicts = listify( repository_metadata.tool_test_results )
else:
+ tool_test_results_dicts = []
+ if tool_test_results_dicts:
+ # Inspect the tool_test_results_dict for the last test run in case it contains only a test_environment
+ # entry. This will occur with multiple runs of this script without running the associated
+ # install_and_test_tool_sed_repositories.sh script which will further populate the tool_test_results_dict.
+ tool_test_results_dict = tool_test_results_dicts[ 0 ]
+ if len( tool_test_results_dict ) <= 1:
+ # We can re-use the mostly empty tool_test_results_dict for this run because it is either empty or it contains only
+ # a test_environment entry. If we use it we need to temporarily eliminate it from the list of tool_test_results_dicts
+ # since it will be re-inserted later.
+ tool_test_results_dict = tool_test_results_dicts.pop( 0 )
+ elif len( tool_test_results_dict ) == 2 and \
+ 'test_environment' in tool_test_results_dict and 'missing_test_components' in tool_test_results_dict:
+ # We can re-use tool_test_results_dict if its only entries are "test_environment" and "missing_test_components".
+ # In this case, some tools are missing tests components while others are not.
+ tool_test_results_dict = tool_test_results_dicts.pop( 0 )
+ else:
+ # The latest tool_test_results_dict has been populated with the results of a test run, so it cannot be used.
+ tool_test_results_dict = {}
+ else:
+ # Create a new dictionary for the most recent test run.
tool_test_results_dict = {}
# Initialize the tool_test_results_dict dictionary with the information about the current test environment.
test_environment_dict = tool_test_results_dict.get( 'test_environment', {} )
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/util/common_install_util.py
--- a/lib/tool_shed/util/common_install_util.py
+++ b/lib/tool_shed/util/common_install_util.py
@@ -71,8 +71,8 @@
def get_dependencies_for_repository( trans, tool_shed_url, repo_info_dict, includes_tool_dependencies ):
"""
- Return dictionaries containing the sets of installed and missing tool dependencies and repository dependencies associated with the repository defined
- by the received repo_info_dict.
+ Return dictionaries containing the sets of installed and missing tool dependencies and repository dependencies associated
+ with the repository defined by the received repo_info_dict.
"""
repository = None
installed_rd = {}
@@ -102,7 +102,8 @@
# Discover all repository dependencies and retrieve information for installing them.
all_repo_info_dict = get_required_repo_info_dicts( trans, tool_shed_url, util.listify( repo_info_dict ) )
has_repository_dependencies = all_repo_info_dict.get( 'has_repository_dependencies', False )
- has_repository_dependencies_only_if_compiling_contained_td = all_repo_info_dict.get( 'has_repository_dependencies_only_if_compiling_contained_td', False )
+ has_repository_dependencies_only_if_compiling_contained_td = \
+ all_repo_info_dict.get( 'has_repository_dependencies_only_if_compiling_contained_td', False )
includes_tools_for_display_in_tool_panel = all_repo_info_dict.get( 'includes_tools_for_display_in_tool_panel', False )
includes_tool_dependencies = all_repo_info_dict.get( 'includes_tool_dependencies', False )
includes_tools = all_repo_info_dict.get( 'includes_tools', False )
@@ -138,7 +139,8 @@
# We have a single repository with (possibly) no defined repository dependencies.
all_repo_info_dict = get_required_repo_info_dicts( trans, tool_shed_url, util.listify( repo_info_dict ) )
has_repository_dependencies = all_repo_info_dict.get( 'has_repository_dependencies', False )
- has_repository_dependencies_only_if_compiling_contained_td = all_repo_info_dict.get( 'has_repository_dependencies_only_if_compiling_contained_td', False )
+ has_repository_dependencies_only_if_compiling_contained_td = \
+ all_repo_info_dict.get( 'has_repository_dependencies_only_if_compiling_contained_td', False )
includes_tools_for_display_in_tool_panel = all_repo_info_dict.get( 'includes_tools_for_display_in_tool_panel', False )
includes_tool_dependencies = all_repo_info_dict.get( 'includes_tool_dependencies', False )
includes_tools = all_repo_info_dict.get( 'includes_tools', False )
@@ -202,8 +204,8 @@
# Get the description from the metadata in case it has a value.
repository_dependencies = metadata.get( 'repository_dependencies', {} )
description = repository_dependencies.get( 'description', None )
- # We need to add a root_key entry to one or both of installed_repository_dependencies dictionary and the missing_repository_dependencies
- # dictionaries for proper display parsing.
+ # We need to add a root_key entry to one or both of installed_repository_dependencies dictionary and the
+ # missing_repository_dependencies dictionaries for proper display parsing.
root_key = container_util.generate_repository_dependencies_key_for_repository( repository.tool_shed,
repository.name,
repository.owner,
@@ -460,7 +462,7 @@
if tool_dependency.can_install:
# The database record is currently in a state that allows us to install the package on the file system.
try:
- dependencies_ignored = app.toolbox.dependency_manager and not app.toolbox.dependency_manager.uses_tool_shed_dependencies()
+ dependencies_ignored = not app.toolbox.dependency_manager.uses_tool_shed_dependencies()
if dependencies_ignored:
log.debug( "Skipping package %s because tool shed dependency resolver not enabled." % str( package_name ) )
# Tool dependency resolves have been configured and they do not include the tool shed. Do not install package.
@@ -489,6 +491,8 @@
if tool_dependency and tool_dependency.status in [ app.install_model.ToolDependency.installation_status.INSTALLED,
app.install_model.ToolDependency.installation_status.ERROR ]:
installed_tool_dependencies.append( tool_dependency )
+ # Add the tool_dependency to the in-memory dictionaries in the installed_repository_manager.
+ app.installed_repository_manager.handle_tool_dependency_install( tool_shed_repository, tool_dependency )
elif elem.tag == 'set_environment':
# <set_environment version="1.0">
# <environment_variable name="R_SCRIPT_PATH"action="set_to">$REPOSITORY_INSTALL_DIR</environment_variable>
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/util/container_util.py
--- a/lib/tool_shed/util/container_util.py
+++ b/lib/tool_shed/util/container_util.py
@@ -5,7 +5,6 @@
from galaxy.web.framework.helpers import time_ago
from tool_shed.util import common_util
from tool_shed.util import readme_util
-from tool_shed.util.tool_dependency_util import tool_dependency_is_orphan
import tool_shed.util.shed_util_common as suc
log = logging.getLogger( __name__ )
@@ -269,7 +268,7 @@
"""Tool dependency object"""
def __init__( self, id=None, name=None, version=None, type=None, readme=None, installation_status=None, repository_id=None,
- tool_dependency_id=None, is_orphan=None ):
+ tool_dependency_id=None ):
self.id = id
self.name = name
self.version = version
@@ -278,15 +277,6 @@
self.installation_status = installation_status
self.repository_id = repository_id
self.tool_dependency_id = tool_dependency_id
- # The designation of a ToolDependency into the "orphan" category has evolved over time, and is significantly restricted since the
- # introduction of the TOOL_DEPENDENCY_DEFINITION repository type. This designation is still critical, however, in that it handles
- # the case where a repository contains both tools and a tool_dependencies.xml file, but the definition in the tool_dependencies.xml
- # file is in no way related to anything defined by any of the contained tool's requirements tag sets. This is important in that it
- # is often a result of a typo (e.g., dependency name or version) that differs between the tool dependency definition within the
- # tool_dependencies.xml file and what is defined in the tool config's <requirements> tag sets. In these cases, the user should be
- # presented with a warning message, and this warning message is is in fact displayed if the following is_orphan attribute is True.
- # This is tricky because in some cases it may be intentional, and tool dependencies that are categorized as "orphan" are in fact valid.
- self.is_orphan = is_orphan
@property
def listify( self ):
@@ -318,42 +308,6 @@
self.repository_metadata_id = repository_metadata_id
self.repository_id = repository_id
-def add_orphan_settings_to_tool_dependencies( tool_dependencies, tools ):
- """Inspect all received tool dependencies and label those that are orphans within the repository."""
- #orphan_env_dependencies = orphan_tool_dependencies.get( 'set_environment', None )
- new_tool_dependencies = {}
- for td_key, requirements_dict in tool_dependencies.items():
- if td_key in [ 'set_environment' ]:
- # "set_environment": [{"name": "R_SCRIPT_PATH", "type": "set_environment"}]
- new_set_environment_dict_list = []
- for env_requirements_dict in requirements_dict:
- try:
- name = env_requirements_dict[ 'name' ]
- type = env_requirements_dict[ 'type' ]
- if tool_dependency_is_orphan( type, name, None, tools ):
- env_requirements_dict[ 'is_orphan' ] = True
- except Exception, e:
- name = str( e )
- type = 'unknown'
- is_orphan = 'unknown'
- new_set_environment_dict_list.append( env_requirements_dict )
- new_tool_dependencies[ td_key ] = new_set_environment_dict_list
- else:
- # {"R/2.15.1": {"name": "R", "readme": "some string", "type": "package", "version": "2.15.1"}
- try:
- name = requirements_dict[ 'name' ]
- type = requirements_dict[ 'type' ]
- version = requirements_dict[ 'version']
- if tool_dependency_is_orphan( type, name, version, tools ):
- requirements_dict[ 'is_orphan' ] = True
- except Exception, e:
- name = str( e )
- type = 'unknown'
- version = 'unknown'
- is_orphan = 'unknown'
- new_tool_dependencies[ td_key ] = requirements_dict
- return new_tool_dependencies
-
def build_data_managers_folder( trans, folder_id, data_managers, label=None ):
"""Return a folder hierarchy containing Data Managers."""
if data_managers:
@@ -849,8 +803,6 @@
if 'tools' not in exclude:
tools = metadata.get( 'tools', [] )
tools.extend( metadata.get( 'invalid_tools', [] ) )
- if tools:
- tool_dependencies = add_orphan_settings_to_tool_dependencies( tool_dependencies, tools )
folder_id, tool_dependencies_root_folder = build_tool_dependencies_folder( trans,
folder_id,
tool_dependencies,
@@ -1055,13 +1007,6 @@
tool_dependency_id += 1
if dependency_key in [ 'set_environment' ]:
for set_environment_dict in requirements_dict:
- if trans.webapp.name == 'tool_shed':
- is_orphan = set_environment_dict.get( 'is_orphan', False )
- else:
- # This is probably not necessary to display in Galaxy.
- is_orphan = False
- if is_orphan:
- folder.description = not_used_by_local_tools_description
try:
name = set_environment_dict.get( 'name', None )
type = set_environment_dict[ 'type' ]
@@ -1086,16 +1031,9 @@
readme=None,
installation_status=installation_status,
repository_id=repository_id,
- tool_dependency_id=td_id,
- is_orphan=is_orphan )
+ tool_dependency_id=td_id )
folder.tool_dependencies.append( tool_dependency )
else:
- if trans.webapp.name == 'tool_shed':
- is_orphan = requirements_dict.get( 'is_orphan', False )
- else:
- is_orphan = False
- if is_orphan:
- folder.description = not_used_by_local_tools_description
try:
name = requirements_dict[ 'name' ]
version = requirements_dict[ 'version' ]
@@ -1122,8 +1060,7 @@
readme=None,
installation_status=installation_status,
repository_id=repository_id,
- tool_dependency_id=td_id,
- is_orphan=is_orphan )
+ tool_dependency_id=td_id )
folder.tool_dependencies.append( tool_dependency )
else:
tool_dependencies_root_folder = None
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/util/metadata_util.py
--- a/lib/tool_shed/util/metadata_util.py
+++ b/lib/tool_shed/util/metadata_util.py
@@ -940,7 +940,12 @@
for required_file in ttb.required_files:
value, extra = required_file
required_files.append( ( value ) )
- inputs = [ input for input in ttb.inputs ]
+ inputs = []
+ for input_field, values in ttb.inputs.iteritems():
+ if len( values ) == 1:
+ inputs.append( ( input_field, values[0] ) )
+ else:
+ inputs.append( ( input_field, values ) )
outputs = []
for output in ttb.outputs:
name, file_name, extra = output
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/util/repository_dependency_util.py
--- a/lib/tool_shed/util/repository_dependency_util.py
+++ b/lib/tool_shed/util/repository_dependency_util.py
@@ -355,14 +355,53 @@
handled_key_rd_dicts,
circular_repository_dependencies )
elif key_rd_dicts_to_be_processed:
- handle_next_repository_dependency( trans, key_rd_dicts_to_be_processed, all_repository_dependencies, handled_key_rd_dicts, circular_repository_dependencies )
+ handle_next_repository_dependency( trans,
+ key_rd_dicts_to_be_processed,
+ all_repository_dependencies,
+ handled_key_rd_dicts,
+ circular_repository_dependencies )
elif key_rd_dicts_to_be_processed:
- handle_next_repository_dependency( trans, key_rd_dicts_to_be_processed, all_repository_dependencies, handled_key_rd_dicts, circular_repository_dependencies )
+ handle_next_repository_dependency( trans,
+ key_rd_dicts_to_be_processed,
+ all_repository_dependencies,
+ handled_key_rd_dicts,
+ circular_repository_dependencies )
elif key_rd_dicts_to_be_processed:
- handle_next_repository_dependency( trans, key_rd_dicts_to_be_processed, all_repository_dependencies, handled_key_rd_dicts, circular_repository_dependencies )
+ handle_next_repository_dependency( trans,
+ key_rd_dicts_to_be_processed,
+ all_repository_dependencies,
+ handled_key_rd_dicts,
+ circular_repository_dependencies )
all_repository_dependencies = prune_invalid_repository_dependencies( all_repository_dependencies )
return all_repository_dependencies
+def get_repository_dependency_tups_for_repository( app, repository, dependency_tups=None, status=None ):
+ """
+ Return a list of of tuples defining tool_shed_repository objects (whose status can be anything) required by the
+ received repository. The returned list defines the entire repository dependency tree.
+ """
+ if dependency_tups is None:
+ dependency_tups = []
+ repository_tup = get_repository_tuple_for_installed_repository_manager( repository )
+ for rrda in repository.required_repositories:
+ repository_dependency = rrda.repository_dependency
+ required_repository = repository_dependency.repository
+ if status is None or required_repository.status == status:
+ required_repository_tup = get_repository_tuple_for_installed_repository_manager( required_repository )
+ if required_repository_tup == repository_tup:
+ # We have a circular repository dependency relationship, skip this entry.
+ continue
+ if required_repository_tup not in dependency_tups:
+ dependency_tups.append( required_repository_tup )
+ return get_repository_dependency_tups_for_repository( app, required_repository, dependency_tups=dependency_tups )
+ return dependency_tups
+
+def get_repository_tuple_for_installed_repository_manager( repository ):
+ return ( str( repository.tool_shed ),
+ str( repository.name ),
+ str( repository.owner ),
+ str( repository.installed_changeset_revision ) )
+
def get_updated_changeset_revisions_for_repository_dependencies( trans, key_rd_dicts ):
updated_key_rd_dicts = []
for key_rd_dict in key_rd_dicts:
@@ -373,9 +412,10 @@
if suc.tool_shed_is_this_tool_shed( rd_toolshed ):
repository = suc.get_repository_by_name_and_owner( trans.app, rd_name, rd_owner )
if repository:
- repository_metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans,
- trans.security.encode_id( repository.id ),
- rd_changeset_revision )
+ repository_metadata = \
+ metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans,
+ trans.security.encode_id( repository.id ),
+ rd_changeset_revision )
if repository_metadata:
# The repository changeset_revision is installable, so no updates are available.
new_key_rd_dict = {}
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/util/repository_maintenance_util.py
--- a/lib/tool_shed/util/repository_maintenance_util.py
+++ b/lib/tool_shed/util/repository_maintenance_util.py
@@ -137,7 +137,7 @@
results_message += 'This Tool Shed does not have the category <b>%s</b> so it will not be associated with this repository.' % \
str( category_name )
else:
- category_ids.append( trans.security.encode_id( category.id ) )
+ category_ids.append( trans.security.encode_id( category.id ) )
# Create the repository record in the database.
repository, create_message = create_repository( trans,
name,
@@ -157,15 +157,18 @@
import_results_tups.append( ( ( str( name ), str( username ) ), results_message ) )
return import_results_tups
-def validate_repository_name( name, user ):
+def validate_repository_name( app, name, user ):
# Repository names must be unique for each user, must be at least four characters
# in length and must contain only lower-case letters, numbers, and the '_' character.
if name in [ 'None', None, '' ]:
return 'Enter the required repository name.'
if name in [ 'repos' ]:
return "The term <b>%s</b> is a reserved word in the tool shed, so it cannot be used as a repository name." % name
- for repository in user.active_repositories:
- if repository.name == name:
+ check_existing = suc.get_repository_by_name_and_owner( app, name, user.username )
+ if check_existing is not None:
+ if check_existing.deleted:
+ return 'You have a deleted repository named <b>%s</b>, so choose a different name.' % name
+ else:
return "You already have a repository named <b>%s</b>, so choose a different name." % name
if len( name ) < 4:
return "Repository names must be at least 4 characters in length."
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a lib/tool_shed/util/tool_dependency_util.py
--- a/lib/tool_shed/util/tool_dependency_util.py
+++ b/lib/tool_shed/util/tool_dependency_util.py
@@ -4,6 +4,7 @@
from galaxy import eggs
from galaxy import util
from galaxy.model.orm import and_
+from galaxy.model.orm import or_
import tool_shed.util.shed_util_common as suc
import tool_shed.repository_types.util as rt_util
from tool_shed.util import xml_util
@@ -13,9 +14,10 @@
def add_installation_directories_to_tool_dependencies( trans, tool_dependencies ):
"""
- Determine the path to the installation directory for each of the received tool dependencies. This path will be displayed within the tool dependencies
- container on the select_tool_panel_section or reselect_tool_panel_section pages when installing or reinstalling repositories that contain tools with the
- defined tool dependencies. The list of tool dependencies may be associated with more than a single repository.
+ Determine the path to the installation directory for each of the received tool dependencies. This path will be
+ displayed within the tool dependencies container on the select_tool_panel_section or reselect_tool_panel_section
+ pages when installing or reinstalling repositories that contain tools with the defined tool dependencies. The
+ list of tool dependencies may be associated with more than a single repository.
"""
for dependency_key, requirements_dict in tool_dependencies.items():
if dependency_key in [ 'set_environment' ]:
@@ -64,7 +66,10 @@
return tool_dependency
def create_tool_dependency_objects( app, tool_shed_repository, relative_install_dir, set_status=True ):
- """Create or update a ToolDependency for each entry in tool_dependencies_config. This method is called when installing a new tool_shed_repository."""
+ """
+ Create or update a ToolDependency for each entry in tool_dependencies_config. This method is called when
+ installing a new tool_shed_repository.
+ """
tool_dependency_objects = []
shed_config_dict = tool_shed_repository.get_shed_config_dict( app )
if shed_config_dict.get( 'tool_path' ):
@@ -82,12 +87,13 @@
name = elem.get( 'name', None )
version = elem.get( 'version', None )
if name and version:
+ status = app.install_model.ToolDependency.installation_status.NEVER_INSTALLED
tool_dependency = create_or_update_tool_dependency( app,
tool_shed_repository,
name=name,
version=version,
type=tool_dependency_type,
- status=app.install_model.ToolDependency.installation_status.NEVER_INSTALLED,
+ status=status,
set_status=set_status )
tool_dependency_objects.append( tool_dependency )
elif tool_dependency_type == 'set_environment':
@@ -96,12 +102,13 @@
name = env_elem.get( 'name', None )
action = env_elem.get( 'action', None )
if name and action:
+ status = app.install_model.ToolDependency.installation_status.NEVER_INSTALLED
tool_dependency = create_or_update_tool_dependency( app,
tool_shed_repository,
name=name,
version=None,
type=tool_dependency_type,
- status=app.install_model.ToolDependency.installation_status.NEVER_INSTALLED,
+ status=status,
set_status=set_status )
tool_dependency_objects.append( tool_dependency )
return tool_dependency_objects
@@ -185,12 +192,14 @@
def generate_message_for_repository_type_change( trans, repository ):
message = ''
if repository.can_change_type_to( trans.app, rt_util.TOOL_DEPENDENCY_DEFINITION ):
- tool_dependency_definition_type_class = trans.app.repository_types_registry.get_class_by_label( rt_util.TOOL_DEPENDENCY_DEFINITION )
- message += "This repository currently contains a single file named <b>%s</b>. If additional files will " % suc.TOOL_DEPENDENCY_DEFINITION_FILENAME
- message += "not be added to this repository, then it's type should be set to <b>%s</b>.<br/>" % tool_dependency_definition_type_class.label
+ tool_dependency_definition_type_class = \
+ trans.app.repository_types_registry.get_class_by_label( rt_util.TOOL_DEPENDENCY_DEFINITION )
+ message += "This repository currently contains a single file named <b>%s</b>. If additional files will " % \
+ suc.TOOL_DEPENDENCY_DEFINITION_FILENAME
+ message += "not be added to this repository, then it's type should be set to <b>%s</b>.<br/>" % \
+ tool_dependency_definition_type_class.label
return message
-
-
+
def get_download_url_for_platform( url_templates, platform_info_dict ):
'''
Compare the dict returned by get_platform_info() with the values specified in the url_template element. Return
@@ -289,6 +298,31 @@
env_sh_file_path = os.path.join( env_sh_file_dir, 'env.sh' )
return env_sh_file_path
+def get_runtime_dependent_tool_dependency_tuples( app, tool_dependency, status=None ):
+ """
+ Return the list of tool dependency objects that require the received tool dependency at run time. The returned
+ list will be filtered by the received status if it is not None. This method is called only from Galaxy.
+ """
+ runtime_dependent_tool_dependency_tups = []
+ required_env_shell_file_path = tool_dependency.get_env_shell_file_path( app )
+ if required_env_shell_file_path:
+ required_env_shell_file_path = os.path.abspath( required_env_shell_file_path )
+ if required_env_shell_file_path is not None:
+ for td in app.install_model.context.query( app.install_model.ToolDependency ):
+ if status is None or td.status == status:
+ env_shell_file_path = td.get_env_shell_file_path( app )
+ if env_shell_file_path is not None:
+ try:
+ contents = open( env_shell_file_path, 'r' ).read()
+ except Exception, e:
+ contents = None
+ log.debug( 'Error reading file %s, so cannot determine if package %s requires package %s at run time: %s' % \
+ ( str( env_shell_file_path ), str( td.name ), str( tool_dependency.name ), str( e ) ) )
+ if contents is not None and contents.find( required_env_shell_file_path ) >= 0:
+ td_tuple = get_tool_dependency_tuple_for_installed_repository_manager( td )
+ runtime_dependent_tool_dependency_tups.append( td_tuple )
+ return runtime_dependent_tool_dependency_tups
+
def get_tool_dependency( trans, id ):
"""Get a tool_dependency from the database via id"""
return trans.install_model.context.query( trans.install_model.ToolDependency ).get( trans.security.decode_id( id ) )
@@ -353,6 +387,13 @@
repository_name,
repository_changeset_revision ) )
+def get_tool_dependency_tuple_for_installed_repository_manager( tool_dependency ):
+ if tool_dependency.type is None:
+ type = None
+ else:
+ type = str( tool_dependency.type )
+ return ( tool_dependency.tool_shed_repository_id, str( tool_dependency.name ), str( tool_dependency.version ), type )
+
def handle_tool_dependency_installation_error( app, tool_dependency, error_message, remove_installation_path=False ):
# Since there was an installation error, remove the installation directory because the install_package method uses
# this: "if os.path.exists( install_dir ):". Setting remove_installation_path to True should rarely occur. It is
@@ -479,6 +520,7 @@
return installed_tool_dependencies, missing_tool_dependencies
def remove_tool_dependency( app, tool_dependency ):
+ """The received tool_dependency must be in an error state."""
context = app.install_model.context
dependency_install_dir = tool_dependency.installation_directory( app )
removed, error_message = remove_tool_dependency_installation_directory( dependency_install_dir )
@@ -487,6 +529,9 @@
tool_dependency.error_message = None
context.add( tool_dependency )
context.flush()
+ # Since the received tool_dependency is in an error state, nothing will need to be changed in any
+ # of the in-memory dictionaries in the installed_repository_manager because changing the state from
+ # error to uninstalled requires no in-memory changes..
return removed, error_message
def remove_tool_dependency_installation_directory( dependency_install_dir ):
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a static/scripts/galaxy.workflow_editor.canvas.js
--- a/static/scripts/galaxy.workflow_editor.canvas.js
+++ b/static/scripts/galaxy.workflow_editor.canvas.js
@@ -360,7 +360,7 @@
workflow.has_changes = true;
canvas_manager.draw_overview();
})))
- .tooltip({delay:500, title: "Flag this as a workflow output. All non-flagged outputs will be hidden." });
+ .tooltip({delay:500, title: "Mark dataset as a workflow output. All unmarked datasets will be hidden." });
callout.css({
top: '50%',
margin:'-8px 0px 0px 0px',
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a static/scripts/mvc/base-mvc.js
--- a/static/scripts/mvc/base-mvc.js
+++ b/static/scripts/mvc/base-mvc.js
@@ -173,6 +173,7 @@
//==============================================================================
var HiddenUntilActivatedViewMixin = /** @lends hiddenUntilActivatedMixin# */{
+//TODO: since this is a mixin, consider moving toggle, hidden into HUAVOptions
/** */
hiddenUntilActivated : function( $activator, options ){
@@ -184,7 +185,9 @@
showSpeed : 'fast'
};
_.extend( this.HUAVOptions, options || {});
+ /** has this been shown already (and onshowFirstTime called)? */
this.HUAVOptions.hasBeenShown = this.HUAVOptions.$elementShown.is( ':visible' );
+ this.hidden = this.isHidden();
if( $activator ){
var mixin = this;
@@ -194,21 +197,36 @@
}
},
+ isHidden : function(){
+ return ( this.HUAVOptions.$elementShown.is( ':hidden' ) );
+ },
+
/** */
toggle : function(){
// can be called manually as well with normal toggle arguments
- if( this.HUAVOptions.$elementShown.is( ':hidden' ) ){
+ //TODO: better as a callback (when the show/hide is actually done)
+ // show
+ if( this.hidden ){
// fire the optional fns on the first/each showing - good for render()
if( !this.HUAVOptions.hasBeenShown ){
if( _.isFunction( this.HUAVOptions.onshowFirstTime ) ){
this.HUAVOptions.hasBeenShown = true;
this.HUAVOptions.onshowFirstTime.call( this );
}
- } else {
- if( _.isFunction( this.HUAVOptions.onshow ) ){
- this.HUAVOptions.onshow.call( this );
- }
}
+ if( _.isFunction( this.HUAVOptions.onshow ) ){
+ this.HUAVOptions.onshow.call( this );
+ this.trigger( 'hiddenUntilActivated:shown', this );
+ }
+ this.hidden = false;
+
+ // hide
+ } else {
+ if( _.isFunction( this.HUAVOptions.onhide ) ){
+ this.HUAVOptions.onhide.call( this );
+ this.trigger( 'hiddenUntilActivated:hidden', this );
+ }
+ this.hidden = true;
}
return this.HUAVOptions.showFn.apply( this.HUAVOptions.$elementShown, arguments );
}
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 5463295a3981c8487effbbbaa127fe028737491a static/scripts/mvc/dataset/hda-base.js
--- a/static/scripts/mvc/dataset/hda-base.js
+++ b/static/scripts/mvc/dataset/hda-base.js
@@ -46,11 +46,11 @@
/** is the view currently in selection mode? */
this.selectable = attributes.selectable || false;
/** is the view currently selected? */
- this.selected = attributes.selected || false;
- /** is the body of this hda view expanded/not. */
- this.expanded = attributes.expanded || false;
- /** is the body of this hda view expanded/not. */
- this.draggable = attributes.draggable || false;
+ this.selected = attributes.selected || false;
+ /** is the body of this hda view expanded/not? */
+ this.expanded = attributes.expanded || false;
+ /** is the body of this hda view expanded/not? */
+ this.draggable = attributes.draggable || false;
this._setUpListeners();
},
@@ -423,7 +423,7 @@
toggleBodyVisibility : function( event, expand ){
// bail (with propagation) if keydown and not space or enter
var KEYCODE_SPACE = 32, KEYCODE_RETURN = 13;
- if( ( event.type === 'keydown' )
+ if( event && ( event.type === 'keydown' )
&& !( event.keyCode === KEYCODE_SPACE || event.keyCode === KEYCODE_RETURN ) ){
return true;
}
This diff is so big that we needed to truncate the remainder.
https://bitbucket.org/galaxy/galaxy-central/commits/70aca9eee9d4/
Changeset: 70aca9eee9d4
User: martenson
Date: 2013-12-16 00:22:01
Summary: activation link tweak: show proper message when trying to activate already active account
Affected #: 1 file
diff -r 0522f7504094bd5be2be97846e695a0c790bf929 -r 70aca9eee9d442a88e49dc99e08ff3b6bbea73d0 lib/galaxy/webapps/galaxy/controllers/user.py
--- a/lib/galaxy/webapps/galaxy/controllers/user.py
+++ b/lib/galaxy/webapps/galaxy/controllers/user.py
@@ -826,19 +826,22 @@
if email is None or activation_token is None:
# We don't have the email or activation_token, show error.
- return trans.show_error_message( "You are using wrong activation link. Try to log-in and we will send you a new activation email.<br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller="root", action="index" )
+ return trans.show_error_message( "You are using wrong activation link. Try to log-in and we will send you a new activation email. <br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller="root", action="index" )
else:
# Find the user
user = trans.sa_session.query( trans.app.model.User ).filter( trans.app.model.User.table.c.email==email ).first()
+ # If the user is active already don't try to activate
+ if user.active == True:
+ return trans.show_ok_message( "Your account is already active. Nothing has changed. <br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
if user.activation_token == activation_token:
user.activation_token = None
user.active = True
trans.sa_session.add(user)
trans.sa_session.flush()
- return trans.show_ok_message( "Your account has been successfully activated!<br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
+ return trans.show_ok_message( "Your account has been successfully activated! <br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
else:
# Tokens don't match. Activation is denied.
- return trans.show_error_message( "You are using wrong activation link. Try to log in and we will send you a new activation email.<br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
+ return trans.show_error_message( "You are using wrong activation link. Try to log in and we will send you a new activation email. <br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
return
def __get_user_type_form_definition( self, trans, user=None, **kwd ):
https://bitbucket.org/galaxy/galaxy-central/commits/d5be36722eb3/
Changeset: d5be36722eb3
Branch: data_library
User: martenson
Date: 2013-12-16 17:14:59
Summary: Closed branch data_library
Affected #: 0 files
https://bitbucket.org/galaxy/galaxy-central/commits/2b83e7980707/
Changeset: 2b83e7980707
User: martenson
Date: 2013-12-16 17:42:28
Summary: closing data_library branch
Affected #: 0 files
https://bitbucket.org/galaxy/galaxy-central/commits/0b2f53ad1a20/
Changeset: 0b2f53ad1a20
User: martenson
Date: 2013-12-16 18:01:35
Summary: Merge own default 2nd head
Affected #: 1 file
diff -r 2b83e798070736ea7279d5e4e33194f66f8ef7df -r 0b2f53ad1a2025dbfd7c15a76f329fc5e8a859ef lib/galaxy/webapps/galaxy/controllers/user.py
--- a/lib/galaxy/webapps/galaxy/controllers/user.py
+++ b/lib/galaxy/webapps/galaxy/controllers/user.py
@@ -826,19 +826,22 @@
if email is None or activation_token is None:
# We don't have the email or activation_token, show error.
- return trans.show_error_message( "You are using wrong activation link. Try to log-in and we will send you a new activation email.<br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller="root", action="index" )
+ return trans.show_error_message( "You are using wrong activation link. Try to log-in and we will send you a new activation email. <br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller="root", action="index" )
else:
# Find the user
user = trans.sa_session.query( trans.app.model.User ).filter( trans.app.model.User.table.c.email==email ).first()
+ # If the user is active already don't try to activate
+ if user.active == True:
+ return trans.show_ok_message( "Your account is already active. Nothing has changed. <br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
if user.activation_token == activation_token:
user.activation_token = None
user.active = True
trans.sa_session.add(user)
trans.sa_session.flush()
- return trans.show_ok_message( "Your account has been successfully activated!<br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
+ return trans.show_ok_message( "Your account has been successfully activated! <br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
else:
# Tokens don't match. Activation is denied.
- return trans.show_error_message( "You are using wrong activation link. Try to log in and we will send you a new activation email.<br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
+ return trans.show_error_message( "You are using wrong activation link. Try to log in and we will send you a new activation email. <br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
return
def __get_user_type_form_definition( self, trans, user=None, **kwd ):
https://bitbucket.org/galaxy/galaxy-central/commits/16d53da082b8/
Changeset: 16d53da082b8
User: martenson
Date: 2013-12-17 19:22:55
Summary: added security checks when downloading lddas in archives
Affected #: 1 file
diff -r 0b2f53ad1a2025dbfd7c15a76f329fc5e8a859ef -r 16d53da082b89db1318a2a688aa8a18470b02f59 lib/galaxy/webapps/galaxy/api/lda_datasets.py
--- a/lib/galaxy/webapps/galaxy/api/lda_datasets.py
+++ b/lib/galaxy/webapps/galaxy/api/lda_datasets.py
@@ -13,15 +13,17 @@
import urllib
import urllib2
import zipfile
+from paste.httpexceptions import HTTPBadRequest
+from galaxy.exceptions import ItemAccessibilityException, MessageException, ItemDeletionException
from galaxy.security import Action
from galaxy import util, web
from galaxy.util.streamball import StreamBall
-from galaxy.web.base.controller import BaseAPIController, UsesLibraryMixinItems
+from galaxy.web.base.controller import BaseAPIController, UsesVisualizationMixin
import logging
log = logging.getLogger( __name__ )
-class DatasetsController( BaseAPIController, UsesLibraryMixinItems ):
+class LibraryDatasetsController( BaseAPIController, UsesVisualizationMixin ):
@web.expose_api
def show( self, trans, id, **kwd ):
@@ -31,7 +33,7 @@
"""
# Get dataset.
try:
- dataset = self.get_library_dataset( trans, id = id )
+ dataset = self.get_library_dataset( trans, id = id, check_ownership=False, check_accessible=True )
except Exception, e:
return str( e )
try:
@@ -65,12 +67,22 @@
datasets_to_download = util.listify( datasets_to_download )
for dataset_id in datasets_to_download:
try:
- ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( dataset_id ) )
- assert not ldda.dataset.purged
+ ldda = self.get_hda_or_ldda( trans, hda_ldda='ldda', dataset_id=dataset_id )
lddas.append( ldda )
- except:
- ldda = None
- message += "Invalid library dataset id (%s) specified. " % str( dataset_id )
+ except ItemAccessibilityException:
+ trans.response.status = 403
+ return 'Insufficient rights to access library dataset with id: (%s)' % str( dataset_id )
+ except MessageException:
+ trans.response.status = 400
+ return 'Wrong library dataset id: (%s)' % str( dataset_id )
+ except ItemDeletionException:
+ trans.response.status = 400
+ return 'The item with library dataset id: (%s) is deleted' % str( dataset_id )
+ except HTTPBadRequest, e:
+ return 'http bad request' + str( e.err_msg )
+ except Exception, e:
+ trans.response.status = 500
+ return 'error of unknown kind' + str( e )
if format in [ 'zip','tgz','tbz' ]:
error = False
https://bitbucket.org/galaxy/galaxy-central/commits/2b4877fc1024/
Changeset: 2b4877fc1024
User: martenson
Date: 2013-12-17 19:35:08
Summary: exception catching rewritten, it is now behaving better, not good but better than before
Affected #: 1 file
diff -r 16d53da082b89db1318a2a688aa8a18470b02f59 -r 2b4877fc1024180fadb5128187d447c0b84c7cd9 lib/galaxy/webapps/galaxy/api/lda_datasets.py
--- a/lib/galaxy/webapps/galaxy/api/lda_datasets.py
+++ b/lib/galaxy/webapps/galaxy/api/lda_datasets.py
@@ -1,5 +1,5 @@
"""
-API operations on the dataset from library.
+API operations on the datasets from library.
"""
import glob
import logging
@@ -35,6 +35,7 @@
try:
dataset = self.get_library_dataset( trans, id = id, check_ownership=False, check_accessible=True )
except Exception, e:
+ trans.response.status = 500
return str( e )
try:
# Default: return dataset as dict.
@@ -43,24 +44,22 @@
rval = "Error in dataset API at listing contents: " + str( e )
log.error( rval + ": %s" % str(e), exc_info=True )
trans.response.status = 500
+ return "Error in dataset API at listing contents: " + str( e )
rval['id'] = trans.security.encode_id(rval['id']);
rval['ldda_id'] = trans.security.encode_id(rval['ldda_id']);
rval['folder_id'] = 'f' + trans.security.encode_id(rval['folder_id'])
-
+ trans.response.status = 200
return rval
@web.expose
def download( self, trans, format, **kwd ):
"""
- POST /api/libraries/datasets/download/{format}
- POST data: ldda_ids = []
- Downloads dataset(s) in the requested format.
+ GET /api/libraries/datasets/download/{format}
+ GET multiple params: ldda_ids = []
+ Downloads dataset(s) in the requested format or plain.
"""
lddas = []
-# is_admin = trans.user_is_admin()
-# current_user_roles = trans.get_current_user_roles()
-
datasets_to_download = kwd['ldda_ids%5B%5D']
if ( datasets_to_download != None ):
@@ -111,15 +110,13 @@
archive = StreamBall( 'w|bz2' )
outext = 'tbz2'
except ( OSError, zipfile.BadZipfile ):
- error = True
log.exception( "Unable to create archive for download" )
- message = "Unable to create archive for download, please report this error"
- status = 'error'
+ trans.response.status = 500
+ return "Unable to create archive for download, please report this error"
except:
- error = True
log.exception( "Unexpected error %s in create archive for download" % sys.exc_info()[0] )
- message = "Unable to create archive for download, please report - %s" % sys.exc_info()[0]
- status = 'error'
+ trans.response.status = 500
+ return "Unable to create archive for download, please report - %s" % sys.exc_info()[0]
if not error:
composite_extensions = trans.app.datatypes_registry.get_composite_extensions()
seen = []
@@ -150,11 +147,9 @@
try:
archive.add(ldda.dataset.file_name,zpath) # add the primary of a composite set
except IOError:
- error = True
log.exception( "Unable to add composite parent %s to temporary library download archive" % ldda.dataset.file_name)
- message = "Unable to create archive for download, please report this error"
- status = 'error'
- continue
+ trans.response.status = 500
+ return "Unable to create archive for download, please report this error"
flist = glob.glob(os.path.join(ldda.dataset.extra_files_path,'*.*')) # glob returns full paths
for fpath in flist:
efp,fname = os.path.split(fpath)
@@ -163,19 +158,16 @@
try:
archive.add( fpath,fname )
except IOError:
- error = True
log.exception( "Unable to add %s to temporary library download archive %s" % (fname,outfname))
- message = "Unable to create archive for download, please report this error"
- status = 'error'
- continue
+ trans.response.status = 500
+ return "Unable to create archive for download, please report this error"
else: # simple case
try:
archive.add( ldda.dataset.file_name, path )
except IOError:
- error = True
log.exception( "Unable to write %s to temporary library download archive" % ldda.dataset.file_name)
- message = "Unable to create archive for download, please report this error"
- status = 'error'
+ trans.response.status = 500
+ return "Unable to create archive for download, please report this error"
if not error:
lname = 'selected_dataset'
fname = lname.replace( ' ', '_' ) + '_files'
@@ -186,15 +178,18 @@
archive = util.streamball.ZipBall(tmpf, tmpd)
archive.wsgi_status = trans.response.wsgi_status()
archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ trans.response.status = 200
return archive.stream
else:
trans.response.set_content_type( "application/x-tar" )
trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
archive.wsgi_status = trans.response.wsgi_status()
archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ trans.response.status = 200
return archive.stream
elif format == 'uncompressed':
if len(lddas) != 1:
+ trans.response.status = 400
return 'Wrong request'
else:
single_dataset = lddas[0]
@@ -206,8 +201,11 @@
fname = ''.join( c in valid_chars and c or '_' for c in fname )[ 0:150 ]
trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s"' % fname
try:
+ trans.response.status = 200
return open( single_dataset.file_name )
except:
+ trans.response.status = 500
return 'This dataset contains no content'
else:
- return 'Wrong format';
+ trans.response.status = 400
+ return 'Wrong format parameter specified';
https://bitbucket.org/galaxy/galaxy-central/commits/6f89cbaeb29c/
Changeset: 6f89cbaeb29c
User: martenson
Date: 2013-12-17 21:58:40
Summary: fix for errors thrown when requested underlying file of dataset does not exist; even better exception handling for ldda-archive downloading
Affected #: 2 files
diff -r 2b4877fc1024180fadb5128187d447c0b84c7cd9 -r 6f89cbaeb29c33ae8d662d84965bf3bdee7f179d lib/galaxy/util/streamball.py
--- a/lib/galaxy/util/streamball.py
+++ b/lib/galaxy/util/streamball.py
@@ -3,6 +3,7 @@
"""
import os
import logging, tarfile
+from galaxy.exceptions import ObjectNotFound
log = logging.getLogger( __name__ )
@@ -14,8 +15,12 @@
self.mode = mode
self.wsgi_status = None
self.wsgi_headeritems = None
- def add( self, file, relpath ):
- self.members[file] = relpath
+ def add( self, file, relpath, check_file=False):
+ if check_file and len(file)>0:
+ if not os.path.isfile(file):
+ raise ObjectNotFound
+ else:
+ self.members[file] = relpath
def stream( self, environ, start_response ):
response_write = start_response( self.wsgi_status, self.wsgi_headeritems )
class tarfileobj:
diff -r 2b4877fc1024180fadb5128187d447c0b84c7cd9 -r 6f89cbaeb29c33ae8d662d84965bf3bdee7f179d lib/galaxy/webapps/galaxy/api/lda_datasets.py
--- a/lib/galaxy/webapps/galaxy/api/lda_datasets.py
+++ b/lib/galaxy/webapps/galaxy/api/lda_datasets.py
@@ -14,7 +14,7 @@
import urllib2
import zipfile
from paste.httpexceptions import HTTPBadRequest
-from galaxy.exceptions import ItemAccessibilityException, MessageException, ItemDeletionException
+from galaxy.exceptions import ItemAccessibilityException, MessageException, ItemDeletionException, ObjectNotFound
from galaxy.security import Action
from galaxy import util, web
from galaxy.util.streamball import StreamBall
@@ -84,7 +84,7 @@
return 'error of unknown kind' + str( e )
if format in [ 'zip','tgz','tbz' ]:
- error = False
+ # error = False
killme = string.punctuation + string.whitespace
trantab = string.maketrans(killme,'_'*len(killme))
try:
@@ -117,12 +117,12 @@
log.exception( "Unexpected error %s in create archive for download" % sys.exc_info()[0] )
trans.response.status = 500
return "Unable to create archive for download, please report - %s" % sys.exc_info()[0]
- if not error:
+ if True:
composite_extensions = trans.app.datatypes_registry.get_composite_extensions()
seen = []
for ldda in lddas:
- if ldda.dataset.state in [ 'new', 'upload', 'queued', 'running', 'empty', 'discarded' ]:
- continue
+ # if ldda.dataset.state in [ 'new', 'upload', 'queued', 'running', 'empty', 'discarded' ]:
+ # continue
ext = ldda.extension
is_composite = ext in composite_extensions
path = ""
@@ -135,6 +135,7 @@
path = os.path.join( parent_folder.name, path )
parent_folder = parent_folder.parent
path += ldda.name
+
while path in seen:
path += '_'
seen.append( path )
@@ -145,30 +146,61 @@
if zpathext == '':
zpath = '%s.html' % zpath # fake the real nature of the html file
try:
- archive.add(ldda.dataset.file_name,zpath) # add the primary of a composite set
+ if format=='zip':
+ archive.add( ldda.dataset.file_name, zpath ) # add the primary of a composite set
+ else:
+ archive.add( ldda.dataset.file_name, zpath, check_file=True ) # add the primary of a composite set
except IOError:
log.exception( "Unable to add composite parent %s to temporary library download archive" % ldda.dataset.file_name)
trans.response.status = 500
return "Unable to create archive for download, please report this error"
+ except ObjectNotFound:
+ log.exception( "Requested dataset %s does not exist on the host." % ldda.dataset.file_name )
+ trans.response.status = 500
+ return "Requested dataset does not exist on the host."
+ except:
+ trans.response.status = 500
+ return "Unknown error, please report this error"
flist = glob.glob(os.path.join(ldda.dataset.extra_files_path,'*.*')) # glob returns full paths
for fpath in flist:
efp,fname = os.path.split(fpath)
if fname > '':
fname = fname.translate(trantab)
try:
- archive.add( fpath,fname )
+ if format=='zip':
+ archive.add( fpath,fname )
+ else:
+ archive.add( fpath,fname, check_file=True )
except IOError:
log.exception( "Unable to add %s to temporary library download archive %s" % (fname,outfname))
trans.response.status = 500
return "Unable to create archive for download, please report this error"
+ except ObjectNotFound:
+ log.exception( "Requested dataset %s does not exist on the host." % fpath )
+ trans.response.status = 500
+ return "Requested dataset does not exist on the host."
+ except:
+ trans.response.status = 500
+ return "Unknown error, please report this error"
else: # simple case
try:
- archive.add( ldda.dataset.file_name, path )
+ if format=='zip':
+ archive.add( ldda.dataset.file_name, path )
+ else:
+ archive.add( ldda.dataset.file_name, path, check_file=True )
except IOError:
log.exception( "Unable to write %s to temporary library download archive" % ldda.dataset.file_name)
trans.response.status = 500
return "Unable to create archive for download, please report this error"
- if not error:
+ except ObjectNotFound:
+ log.exception( "Requested dataset %s does not exist on the host." % ldda.dataset.file_name )
+ trans.response.status = 500
+ return "Requested dataset does not exist on the host."
+ except:
+ trans.response.status = 500
+ return "Unknown error, please report this error"
+
+ if True:
lname = 'selected_dataset'
fname = lname.replace( ' ', '_' ) + '_files'
if format == 'zip':
https://bitbucket.org/galaxy/galaxy-central/commits/8090554de504/
Changeset: 8090554de504
User: martenson
Date: 2013-12-18 17:41:24
Summary: minor cosmetic changes
Affected #: 2 files
diff -r 6f89cbaeb29c33ae8d662d84965bf3bdee7f179d -r 8090554de504ffa05e744fdca051b9350aeae93f static/scripts/utils/galaxy.uploadbox.js
--- a/static/scripts/utils/galaxy.uploadbox.js
+++ b/static/scripts/utils/galaxy.uploadbox.js
@@ -353,3 +353,4 @@
};
}
})(jQuery);
+
diff -r 6f89cbaeb29c33ae8d662d84965bf3bdee7f179d -r 8090554de504ffa05e744fdca051b9350aeae93f static/style/Gruntfile.js
--- a/static/style/Gruntfile.js
+++ b/static/style/Gruntfile.js
@@ -6,7 +6,7 @@
var theme = grunt.option( 'theme', 'blue' );
var out = 'blue'
- var lessFiles = [ 'base', 'autocomplete_tagging', 'embed_item', 'iphone', 'masthead', 'library', 'trackster'];
+ var lessFiles = [ 'base', 'autocomplete_tagging', 'embed_item', 'iphone', 'masthead', 'library', 'trackster' ];
var _ = grunt.util._;
var fmt = _.sprintf;
https://bitbucket.org/galaxy/galaxy-central/commits/00fb5740d971/
Changeset: 00fb5740d971
User: martenson
Date: 2013-12-19 18:44:34
Summary: refactor + docs
Affected #: 2 files
diff -r 8090554de504ffa05e744fdca051b9350aeae93f -r 00fb5740d971318d4384e69fc2fcc6b2318b2d44 doc/source/lib/galaxy.webapps.galaxy.api.rst
--- a/doc/source/lib/galaxy.webapps.galaxy.api.rst
+++ b/doc/source/lib/galaxy.webapps.galaxy.api.rst
@@ -297,6 +297,14 @@
:undoc-members:
:show-inheritance:
+:mod:`lda_datasets` Module
+--------------------------
+
+.. automodule:: galaxy.webapps.galaxy.api.lda_datasets
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
:mod:`libraries` Module
-----------------------
diff -r 8090554de504ffa05e744fdca051b9350aeae93f -r 00fb5740d971318d4384e69fc2fcc6b2318b2d44 lib/galaxy/webapps/galaxy/api/lda_datasets.py
--- a/lib/galaxy/webapps/galaxy/api/lda_datasets.py
+++ b/lib/galaxy/webapps/galaxy/api/lda_datasets.py
@@ -28,8 +28,17 @@
@web.expose_api
def show( self, trans, id, **kwd ):
"""
- GET /api/libraries/datasets/{encoded_dataset_id}
- Displays information about the dataset identified by the lda ID.
+ show( self, trans, id, **kwd )
+ * GET /api/libraries/datasets/{encoded_dataset_id}:
+ Displays information about the dataset identified by the encoded id.
+
+
+ :type id: an encoded id string
+ :param id: the encoded id of the dataset to query
+
+ :rtype: dictionary
+ :returns: detailed dataset information from
+ :func:`galaxy.web.base.controller.UsesVisualizationMixin.get_library_dataset.to_dict()`
"""
# Get dataset.
try:
@@ -55,9 +64,25 @@
@web.expose
def download( self, trans, format, **kwd ):
"""
- GET /api/libraries/datasets/download/{format}
- GET multiple params: ldda_ids = []
- Downloads dataset(s) in the requested format or plain.
+ download( self, trans, format, **kwd )
+ * GET /api/libraries/datasets/download/{format}
+
+ .. code-block::
+ example:
+ GET localhost:8080/api/libraries/datasets/download/tbz?ldda_ids%255B%255D=a0d84b45643a2678&ldda_ids%255B%255D=fe38c84dcd46c828
+
+ :type format: string
+ :param format: string representing requested archive format
+
+ .. note:: supported formats are: zip, tgz, tbz, uncompressed
+
+ :type lddas[]: an array
+ :param lddas[]: an array of encoded ids
+
+ :rtype: file
+ :returns: either archive with the requested datasets packed inside or a single uncompressed dataset
+
+ :raises: MessageException, ItemDeletionException, ItemAccessibilityException, HTTPBadRequest, OSError, IOError, ObjectNotFound
"""
lddas = []
datasets_to_download = kwd['ldda_ids%5B%5D']
@@ -117,108 +142,101 @@
log.exception( "Unexpected error %s in create archive for download" % sys.exc_info()[0] )
trans.response.status = 500
return "Unable to create archive for download, please report - %s" % sys.exc_info()[0]
- if True:
- composite_extensions = trans.app.datatypes_registry.get_composite_extensions()
- seen = []
- for ldda in lddas:
- # if ldda.dataset.state in [ 'new', 'upload', 'queued', 'running', 'empty', 'discarded' ]:
- # continue
- ext = ldda.extension
- is_composite = ext in composite_extensions
- path = ""
- parent_folder = ldda.library_dataset.folder
- while parent_folder is not None:
- # Exclude the now-hidden "root folder"
- if parent_folder.parent is None:
- path = os.path.join( parent_folder.library_root[0].name, path )
- break
- path = os.path.join( parent_folder.name, path )
- parent_folder = parent_folder.parent
- path += ldda.name
-
- while path in seen:
- path += '_'
- seen.append( path )
- zpath = os.path.split(path)[-1] # comes as base_name/fname
- outfname,zpathext = os.path.splitext(zpath)
- if is_composite:
- # need to add all the components from the extra_files_path to the zip
- if zpathext == '':
- zpath = '%s.html' % zpath # fake the real nature of the html file
+ composite_extensions = trans.app.datatypes_registry.get_composite_extensions()
+ seen = []
+ for ldda in lddas:
+ ext = ldda.extension
+ is_composite = ext in composite_extensions
+ path = ""
+ parent_folder = ldda.library_dataset.folder
+ while parent_folder is not None:
+ # Exclude the now-hidden "root folder"
+ if parent_folder.parent is None:
+ path = os.path.join( parent_folder.library_root[0].name, path )
+ break
+ path = os.path.join( parent_folder.name, path )
+ parent_folder = parent_folder.parent
+ path += ldda.name
+ while path in seen:
+ path += '_'
+ seen.append( path )
+ zpath = os.path.split(path)[-1] # comes as base_name/fname
+ outfname,zpathext = os.path.splitext(zpath)
+ if is_composite: # need to add all the components from the extra_files_path to the zip
+ if zpathext == '':
+ zpath = '%s.html' % zpath # fake the real nature of the html file
+ try:
+ if format=='zip':
+ archive.add( ldda.dataset.file_name, zpath ) # add the primary of a composite set
+ else:
+ archive.add( ldda.dataset.file_name, zpath, check_file=True ) # add the primary of a composite set
+ except IOError:
+ log.exception( "Unable to add composite parent %s to temporary library download archive" % ldda.dataset.file_name)
+ trans.response.status = 500
+ return "Unable to create archive for download, please report this error"
+ except ObjectNotFound:
+ log.exception( "Requested dataset %s does not exist on the host." % ldda.dataset.file_name )
+ trans.response.status = 500
+ return "Requested dataset does not exist on the host."
+ except:
+ trans.response.status = 500
+ return "Unknown error, please report this error"
+ flist = glob.glob(os.path.join(ldda.dataset.extra_files_path,'*.*')) # glob returns full paths
+ for fpath in flist:
+ efp,fname = os.path.split(fpath)
+ if fname > '':
+ fname = fname.translate(trantab)
try:
if format=='zip':
- archive.add( ldda.dataset.file_name, zpath ) # add the primary of a composite set
- else:
- archive.add( ldda.dataset.file_name, zpath, check_file=True ) # add the primary of a composite set
+ archive.add( fpath,fname )
+ else:
+ archive.add( fpath,fname, check_file=True )
except IOError:
- log.exception( "Unable to add composite parent %s to temporary library download archive" % ldda.dataset.file_name)
+ log.exception( "Unable to add %s to temporary library download archive %s" % (fname,outfname))
trans.response.status = 500
return "Unable to create archive for download, please report this error"
except ObjectNotFound:
- log.exception( "Requested dataset %s does not exist on the host." % ldda.dataset.file_name )
+ log.exception( "Requested dataset %s does not exist on the host." % fpath )
trans.response.status = 500
- return "Requested dataset does not exist on the host."
- except:
- trans.response.status = 500
- return "Unknown error, please report this error"
- flist = glob.glob(os.path.join(ldda.dataset.extra_files_path,'*.*')) # glob returns full paths
- for fpath in flist:
- efp,fname = os.path.split(fpath)
- if fname > '':
- fname = fname.translate(trantab)
- try:
- if format=='zip':
- archive.add( fpath,fname )
- else:
- archive.add( fpath,fname, check_file=True )
- except IOError:
- log.exception( "Unable to add %s to temporary library download archive %s" % (fname,outfname))
- trans.response.status = 500
- return "Unable to create archive for download, please report this error"
- except ObjectNotFound:
- log.exception( "Requested dataset %s does not exist on the host." % fpath )
- trans.response.status = 500
- return "Requested dataset does not exist on the host."
- except:
- trans.response.status = 500
- return "Unknown error, please report this error"
- else: # simple case
- try:
- if format=='zip':
- archive.add( ldda.dataset.file_name, path )
- else:
- archive.add( ldda.dataset.file_name, path, check_file=True )
- except IOError:
- log.exception( "Unable to write %s to temporary library download archive" % ldda.dataset.file_name)
- trans.response.status = 500
- return "Unable to create archive for download, please report this error"
- except ObjectNotFound:
- log.exception( "Requested dataset %s does not exist on the host." % ldda.dataset.file_name )
- trans.response.status = 500
- return "Requested dataset does not exist on the host."
+ return "Requested dataset does not exist on the host."
except:
trans.response.status = 500
return "Unknown error, please report this error"
-
- if True:
- lname = 'selected_dataset'
- fname = lname.replace( ' ', '_' ) + '_files'
- if format == 'zip':
- archive.close()
- trans.response.set_content_type( "application/octet-stream" )
- trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
- archive = util.streamball.ZipBall(tmpf, tmpd)
- archive.wsgi_status = trans.response.wsgi_status()
- archive.wsgi_headeritems = trans.response.wsgi_headeritems()
- trans.response.status = 200
- return archive.stream
- else:
- trans.response.set_content_type( "application/x-tar" )
- trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
- archive.wsgi_status = trans.response.wsgi_status()
- archive.wsgi_headeritems = trans.response.wsgi_headeritems()
- trans.response.status = 200
- return archive.stream
+ else: # simple case
+ try:
+ if format=='zip':
+ archive.add( ldda.dataset.file_name, path )
+ else:
+ archive.add( ldda.dataset.file_name, path, check_file=True )
+ except IOError:
+ log.exception( "Unable to write %s to temporary library download archive" % ldda.dataset.file_name)
+ trans.response.status = 500
+ return "Unable to create archive for download, please report this error"
+ except ObjectNotFound:
+ log.exception( "Requested dataset %s does not exist on the host." % ldda.dataset.file_name )
+ trans.response.status = 500
+ return "Requested dataset does not exist on the host."
+ except:
+ trans.response.status = 500
+ return "Unknown error, please report this error"
+ lname = 'selected_dataset'
+ fname = lname.replace( ' ', '_' ) + '_files'
+ if format == 'zip':
+ archive.close()
+ trans.response.set_content_type( "application/octet-stream" )
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
+ archive = util.streamball.ZipBall(tmpf, tmpd)
+ archive.wsgi_status = trans.response.wsgi_status()
+ archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ trans.response.status = 200
+ return archive.stream
+ else:
+ trans.response.set_content_type( "application/x-tar" )
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
+ archive.wsgi_status = trans.response.wsgi_status()
+ archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ trans.response.status = 200
+ return archive.stream
elif format == 'uncompressed':
if len(lddas) != 1:
trans.response.status = 400
https://bitbucket.org/galaxy/galaxy-central/commits/c1963ee06cb0/
Changeset: c1963ee06cb0
User: martenson
Date: 2013-12-19 18:48:22
Summary: Merged in martenson/galaxy-central-marten (pull request #273)
Initial Data Libraries
Affected #: 24 files
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b doc/source/lib/galaxy.webapps.galaxy.api.rst
--- a/doc/source/lib/galaxy.webapps.galaxy.api.rst
+++ b/doc/source/lib/galaxy.webapps.galaxy.api.rst
@@ -302,6 +302,14 @@
:undoc-members:
:show-inheritance:
+:mod:`lda_datasets` Module
+--------------------------
+
+.. automodule:: galaxy.webapps.galaxy.api.lda_datasets
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
:mod:`libraries` Module
-----------------------
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -1827,7 +1827,7 @@
class Library( object, Dictifiable ):
permitted_actions = get_permitted_actions( filter='LIBRARY' )
dict_collection_visible_keys = ( 'id', 'name' )
- dict_element_visible_keys = ( 'id', 'deleted', 'name', 'description', 'synopsis' )
+ dict_element_visible_keys = ( 'id', 'deleted', 'name', 'description', 'synopsis', 'root_folder_id' )
def __init__( self, name=None, description=None, synopsis=None, root_folder=None ):
self.name = name or "Unnamed library"
self.description = description
@@ -1894,7 +1894,7 @@
return name
class LibraryFolder( object, Dictifiable ):
- dict_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build' )
+ dict_element_visible_keys = ( 'id', 'parent_id', 'name', 'description', 'item_count', 'genome_build', 'update_time' )
def __init__( self, name=None, description=None, item_count=0, order_id=None ):
self.name = name or "Unnamed folder"
self.description = description
@@ -2060,6 +2060,7 @@
genome_build = ldda.dbkey,
misc_info = ldda.info,
misc_blurb = ldda.blurb,
+ peek = ( lambda ldda: ldda.display_peek() if ldda.peek and ldda.peek != 'no peek' else None )( ldda ),
template_data = template_data )
if ldda.dataset.uuid is None:
rval['uuid'] = None
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -1852,8 +1852,9 @@
table = self.table
trans = conn.begin()
try:
- next_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
- table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid + 1 ) )
+ current_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
+ next_hid = current_hid + 1
+ table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid ) )
trans.commit()
return next_hid
except:
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b lib/galaxy/util/streamball.py
--- a/lib/galaxy/util/streamball.py
+++ b/lib/galaxy/util/streamball.py
@@ -3,6 +3,7 @@
"""
import os
import logging, tarfile
+from galaxy.exceptions import ObjectNotFound
log = logging.getLogger( __name__ )
@@ -14,8 +15,12 @@
self.mode = mode
self.wsgi_status = None
self.wsgi_headeritems = None
- def add( self, file, relpath ):
- self.members[file] = relpath
+ def add( self, file, relpath, check_file=False):
+ if check_file and len(file)>0:
+ if not os.path.isfile(file):
+ raise ObjectNotFound
+ else:
+ self.members[file] = relpath
def stream( self, environ, start_response ):
response_write = start_response( self.wsgi_status, self.wsgi_headeritems )
class tarfileobj:
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b lib/galaxy/webapps/galaxy/api/datasets.py
--- a/lib/galaxy/webapps/galaxy/api/datasets.py
+++ b/lib/galaxy/webapps/galaxy/api/datasets.py
@@ -1,5 +1,5 @@
"""
-API operations on the contents of a dataset.
+API operations on the contents of a history dataset.
"""
from galaxy import web
from galaxy.visualization.data_providers.genome import FeatureLocationIndexDataProvider
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b lib/galaxy/webapps/galaxy/api/folder_contents.py
--- a/lib/galaxy/webapps/galaxy/api/folder_contents.py
+++ b/lib/galaxy/webapps/galaxy/api/folder_contents.py
@@ -1,5 +1,5 @@
"""
-API operations on the contents of a library.
+API operations on the contents of a folder.
"""
import logging, os, string, shutil, urllib, re, socket
from cgi import escape, FieldStorage
@@ -11,67 +11,122 @@
log = logging.getLogger( __name__ )
class FolderContentsController( BaseAPIController, UsesLibraryMixin, UsesLibraryMixinItems ):
+ """
+ Class controls retrieval, creation and updating of folder contents.
+ """
+
+ def load_folder_contents( self, trans, folder ):
+ """
+ Loads all contents of the folder (folders and data sets) but only in the first level.
+ """
+ current_user_roles = trans.get_current_user_roles()
+ is_admin = trans.user_is_admin()
+ content_items = []
+ for subfolder in folder.active_folders:
+ if not is_admin:
+ can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder )
+ if (is_admin or can_access) and not subfolder.deleted:
+ subfolder.api_type = 'folder'
+ content_items.append( subfolder )
+ for dataset in folder.datasets:
+ if not is_admin:
+ can_access = trans.app.security_agent.can_access_dataset( current_user_roles, dataset.library_dataset_dataset_association.dataset )
+ if (is_admin or can_access) and not dataset.deleted:
+ dataset.api_type = 'file'
+ content_items.append( dataset )
+ return content_items
@web.expose_api
def index( self, trans, folder_id, **kwd ):
"""
GET /api/folders/{encoded_folder_id}/contents
Displays a collection (list) of a folder's contents (files and folders).
- The /api/library_contents/{encoded_library_id}/contents
- lists everything in a library recursively, which is not what
- we want here. We could add a parameter to use the recursive
- style, but this is meant to act similar to an "ls" directory listing.
+ Encoded folder ID is prepended with 'F' if it is a folder as opposed to a data set which does not have it.
+ Full path is provided as a separate object in response providing data for breadcrumb path building.
"""
- rval = []
+ folder_container = []
current_user_roles = trans.get_current_user_roles()
- def traverse( folder ):
- admin = trans.user_is_admin()
- rval = []
- for subfolder in folder.active_folders:
- if not admin:
- can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, subfolder )
- if (admin or can_access) and not subfolder.deleted:
- subfolder.api_type = 'folder'
- rval.append( subfolder )
- for ld in folder.datasets:
- if not admin:
- can_access = trans.app.security_agent.can_access_dataset( current_user_roles, ld.library_dataset_dataset_association.dataset )
- if (admin or can_access) and not ld.deleted:
- ld.api_type = 'file'
- rval.append( ld )
- return rval
-
- try:
- decoded_folder_id = trans.security.decode_id( folder_id[-16:] )
- except TypeError:
- trans.response.status = 400
- return "Malformed folder id ( %s ) specified, unable to decode." % str( folder_id )
+ if ( folder_id.startswith( 'F' ) ):
+ try:
+ decoded_folder_id = trans.security.decode_id( folder_id[1:] )
+ except TypeError:
+ trans.response.status = 400
+ return "Malformed folder id ( %s ) specified, unable to decode." % str( folder_id )
try:
folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( decoded_folder_id )
- parent_library = folder.parent_library
except:
folder = None
- log.error( "FolderContentsController.index: Unable to retrieve folder %s"
- % folder_id )
+ log.error( "FolderContentsController.index: Unable to retrieve folder with ID: %s" % folder_id )
- # TODO: Find the API's path to this folder if necessary.
- # This was needed in recursive descent, but it's not needed
- # for "ls"-style content checking:
- if not folder or not ( trans.user_is_admin() or trans.app.security_agent.can_access_library_item( current_user_roles, folder, trans.user ) ):
+ # We didn't find the folder or user does not have an access to it.
+ if not folder:
trans.response.status = 400
return "Invalid folder id ( %s ) specified." % str( folder_id )
+
+ if not ( trans.user_is_admin() or trans.app.security_agent.can_access_library_item( current_user_roles, folder, trans.user ) ):
+ log.warning( "SECURITY: User (id: %s) without proper access rights is trying to load folder with ID of %s" % ( trans.user.id, folder.id ) )
+ trans.response.status = 400
+ return "Invalid folder id ( %s ) specified." % str( folder_id )
+
+ path_to_root = []
+ def build_path ( folder ):
+ """
+ Search the path upwards recursively and load the whole route of names and ids for breadcrumb purposes.
+ """
+ path_to_root = []
+ # We are almost in root
+ if folder.parent_id is None:
+ path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
+ else:
+ # We add the current folder and traverse up one folder.
+ path_to_root.append( ( 'F' + trans.security.encode_id( folder.id ), folder.name ) )
+ upper_folder = trans.sa_session.query( trans.app.model.LibraryFolder ).get( folder.parent_id )
+ path_to_root.extend( build_path( upper_folder ) )
+ return path_to_root
+
+ # Return the reversed path so it starts with the library node.
+ full_path = build_path( folder )[::-1]
+ folder_container.append( dict( full_path = full_path ) )
+
+ folder_contents = []
+ time_updated = ''
+ time_created = ''
+ # Go through every item in the folder and include its meta-data.
+ for content_item in self.load_folder_contents( trans, folder ):
+# rval = content_item.to_dict()
+ return_item = {}
+ encoded_id = trans.security.encode_id( content_item.id )
+ time_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
+ time_created = content_item.create_time.strftime( "%Y-%m-%d %I:%M %p" )
+
+ # For folder return also hierarchy values
+ if content_item.api_type == 'folder':
+ encoded_id = 'F' + encoded_id
+# time_updated = content_item.update_time.strftime( "%Y-%m-%d %I:%M %p" )
+ return_item.update ( dict ( item_count = content_item.item_count ) )
- for content in traverse( folder ):
- encoded_id = trans.security.encode_id( content.id )
- if content.api_type == 'folder':
- encoded_id = 'F' + encoded_id
- rval.append( dict( id = encoded_id,
- type = content.api_type,
- name = content.name,
- url = url_for( 'folder_contents', folder_id=encoded_id ) ) )
- return rval
+ if content_item.api_type == 'file':
+ library_dataset_dict = content_item.to_dict()
+ library_dataset_dict['data_type']
+ library_dataset_dict['file_size']
+ library_dataset_dict['date_uploaded']
+ return_item.update ( dict ( data_type = library_dataset_dict['data_type'],
+ file_size = library_dataset_dict['file_size'],
+ date_uploaded = library_dataset_dict['date_uploaded'] ) )
+
+ # For every item return also the default meta-data
+ return_item.update( dict( id = encoded_id,
+ type = content_item.api_type,
+ name = content_item.name,
+ time_updated = time_updated,
+ time_created = time_created
+ ) )
+ folder_contents.append( return_item )
+ # Put the data in the container
+ folder_container.append( dict( folder_contents = folder_contents ) )
+ return folder_container
@web.expose_api
def show( self, trans, id, library_id, **kwd ):
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b lib/galaxy/webapps/galaxy/api/lda_datasets.py
--- /dev/null
+++ b/lib/galaxy/webapps/galaxy/api/lda_datasets.py
@@ -0,0 +1,261 @@
+"""
+API operations on the datasets from library.
+"""
+import glob
+import logging
+import operator
+import os
+import os.path
+import string
+import sys
+import tarfile
+import tempfile
+import urllib
+import urllib2
+import zipfile
+from paste.httpexceptions import HTTPBadRequest
+from galaxy.exceptions import ItemAccessibilityException, MessageException, ItemDeletionException, ObjectNotFound
+from galaxy.security import Action
+from galaxy import util, web
+from galaxy.util.streamball import StreamBall
+from galaxy.web.base.controller import BaseAPIController, UsesVisualizationMixin
+
+import logging
+log = logging.getLogger( __name__ )
+
+class LibraryDatasetsController( BaseAPIController, UsesVisualizationMixin ):
+
+ @web.expose_api
+ def show( self, trans, id, **kwd ):
+ """
+ show( self, trans, id, **kwd )
+ * GET /api/libraries/datasets/{encoded_dataset_id}:
+ Displays information about the dataset identified by the encoded id.
+
+
+ :type id: an encoded id string
+ :param id: the encoded id of the dataset to query
+
+ :rtype: dictionary
+ :returns: detailed dataset information from
+ :func:`galaxy.web.base.controller.UsesVisualizationMixin.get_library_dataset.to_dict()`
+ """
+ # Get dataset.
+ try:
+ dataset = self.get_library_dataset( trans, id = id, check_ownership=False, check_accessible=True )
+ except Exception, e:
+ trans.response.status = 500
+ return str( e )
+ try:
+ # Default: return dataset as dict.
+ rval = dataset.to_dict()
+ except Exception, e:
+ rval = "Error in dataset API at listing contents: " + str( e )
+ log.error( rval + ": %s" % str(e), exc_info=True )
+ trans.response.status = 500
+ return "Error in dataset API at listing contents: " + str( e )
+
+ rval['id'] = trans.security.encode_id(rval['id']);
+ rval['ldda_id'] = trans.security.encode_id(rval['ldda_id']);
+ rval['folder_id'] = 'f' + trans.security.encode_id(rval['folder_id'])
+ trans.response.status = 200
+ return rval
+
+ @web.expose
+ def download( self, trans, format, **kwd ):
+ """
+ download( self, trans, format, **kwd )
+ * GET /api/libraries/datasets/download/{format}
+
+ .. code-block::
+ example:
+ GET localhost:8080/api/libraries/datasets/download/tbz?ldda_ids%255B%255D=a0d84b45643a2678&ldda_ids%255B%255D=fe38c84dcd46c828
+
+ :type format: string
+ :param format: string representing requested archive format
+
+ .. note:: supported formats are: zip, tgz, tbz, uncompressed
+
+ :type lddas[]: an array
+ :param lddas[]: an array of encoded ids
+
+ :rtype: file
+ :returns: either archive with the requested datasets packed inside or a single uncompressed dataset
+
+ :raises: MessageException, ItemDeletionException, ItemAccessibilityException, HTTPBadRequest, OSError, IOError, ObjectNotFound
+ """
+ lddas = []
+ datasets_to_download = kwd['ldda_ids%5B%5D']
+
+ if ( datasets_to_download != None ):
+ datasets_to_download = util.listify( datasets_to_download )
+ for dataset_id in datasets_to_download:
+ try:
+ ldda = self.get_hda_or_ldda( trans, hda_ldda='ldda', dataset_id=dataset_id )
+ lddas.append( ldda )
+ except ItemAccessibilityException:
+ trans.response.status = 403
+ return 'Insufficient rights to access library dataset with id: (%s)' % str( dataset_id )
+ except MessageException:
+ trans.response.status = 400
+ return 'Wrong library dataset id: (%s)' % str( dataset_id )
+ except ItemDeletionException:
+ trans.response.status = 400
+ return 'The item with library dataset id: (%s) is deleted' % str( dataset_id )
+ except HTTPBadRequest, e:
+ return 'http bad request' + str( e.err_msg )
+ except Exception, e:
+ trans.response.status = 500
+ return 'error of unknown kind' + str( e )
+
+ if format in [ 'zip','tgz','tbz' ]:
+ # error = False
+ killme = string.punctuation + string.whitespace
+ trantab = string.maketrans(killme,'_'*len(killme))
+ try:
+ outext = 'zip'
+ if format == 'zip':
+ # Can't use mkstemp - the file must not exist first
+ tmpd = tempfile.mkdtemp()
+ util.umask_fix_perms( tmpd, trans.app.config.umask, 0777, self.app.config.gid )
+ tmpf = os.path.join( tmpd, 'library_download.' + format )
+ if trans.app.config.upstream_gzip:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_STORED, True )
+ else:
+ archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True )
+ archive.add = lambda x, y: archive.write( x, y.encode('CP437') )
+ elif format == 'tgz':
+ if trans.app.config.upstream_gzip:
+ archive = StreamBall( 'w|' )
+ outext = 'tar'
+ else:
+ archive = StreamBall( 'w|gz' )
+ outext = 'tgz'
+ elif format == 'tbz':
+ archive = StreamBall( 'w|bz2' )
+ outext = 'tbz2'
+ except ( OSError, zipfile.BadZipfile ):
+ log.exception( "Unable to create archive for download" )
+ trans.response.status = 500
+ return "Unable to create archive for download, please report this error"
+ except:
+ log.exception( "Unexpected error %s in create archive for download" % sys.exc_info()[0] )
+ trans.response.status = 500
+ return "Unable to create archive for download, please report - %s" % sys.exc_info()[0]
+ composite_extensions = trans.app.datatypes_registry.get_composite_extensions()
+ seen = []
+ for ldda in lddas:
+ ext = ldda.extension
+ is_composite = ext in composite_extensions
+ path = ""
+ parent_folder = ldda.library_dataset.folder
+ while parent_folder is not None:
+ # Exclude the now-hidden "root folder"
+ if parent_folder.parent is None:
+ path = os.path.join( parent_folder.library_root[0].name, path )
+ break
+ path = os.path.join( parent_folder.name, path )
+ parent_folder = parent_folder.parent
+ path += ldda.name
+ while path in seen:
+ path += '_'
+ seen.append( path )
+ zpath = os.path.split(path)[-1] # comes as base_name/fname
+ outfname,zpathext = os.path.splitext(zpath)
+ if is_composite: # need to add all the components from the extra_files_path to the zip
+ if zpathext == '':
+ zpath = '%s.html' % zpath # fake the real nature of the html file
+ try:
+ if format=='zip':
+ archive.add( ldda.dataset.file_name, zpath ) # add the primary of a composite set
+ else:
+ archive.add( ldda.dataset.file_name, zpath, check_file=True ) # add the primary of a composite set
+ except IOError:
+ log.exception( "Unable to add composite parent %s to temporary library download archive" % ldda.dataset.file_name)
+ trans.response.status = 500
+ return "Unable to create archive for download, please report this error"
+ except ObjectNotFound:
+ log.exception( "Requested dataset %s does not exist on the host." % ldda.dataset.file_name )
+ trans.response.status = 500
+ return "Requested dataset does not exist on the host."
+ except:
+ trans.response.status = 500
+ return "Unknown error, please report this error"
+ flist = glob.glob(os.path.join(ldda.dataset.extra_files_path,'*.*')) # glob returns full paths
+ for fpath in flist:
+ efp,fname = os.path.split(fpath)
+ if fname > '':
+ fname = fname.translate(trantab)
+ try:
+ if format=='zip':
+ archive.add( fpath,fname )
+ else:
+ archive.add( fpath,fname, check_file=True )
+ except IOError:
+ log.exception( "Unable to add %s to temporary library download archive %s" % (fname,outfname))
+ trans.response.status = 500
+ return "Unable to create archive for download, please report this error"
+ except ObjectNotFound:
+ log.exception( "Requested dataset %s does not exist on the host." % fpath )
+ trans.response.status = 500
+ return "Requested dataset does not exist on the host."
+ except:
+ trans.response.status = 500
+ return "Unknown error, please report this error"
+ else: # simple case
+ try:
+ if format=='zip':
+ archive.add( ldda.dataset.file_name, path )
+ else:
+ archive.add( ldda.dataset.file_name, path, check_file=True )
+ except IOError:
+ log.exception( "Unable to write %s to temporary library download archive" % ldda.dataset.file_name)
+ trans.response.status = 500
+ return "Unable to create archive for download, please report this error"
+ except ObjectNotFound:
+ log.exception( "Requested dataset %s does not exist on the host." % ldda.dataset.file_name )
+ trans.response.status = 500
+ return "Requested dataset does not exist on the host."
+ except:
+ trans.response.status = 500
+ return "Unknown error, please report this error"
+ lname = 'selected_dataset'
+ fname = lname.replace( ' ', '_' ) + '_files'
+ if format == 'zip':
+ archive.close()
+ trans.response.set_content_type( "application/octet-stream" )
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
+ archive = util.streamball.ZipBall(tmpf, tmpd)
+ archive.wsgi_status = trans.response.wsgi_status()
+ archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ trans.response.status = 200
+ return archive.stream
+ else:
+ trans.response.set_content_type( "application/x-tar" )
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s.%s"' % (fname,outext)
+ archive.wsgi_status = trans.response.wsgi_status()
+ archive.wsgi_headeritems = trans.response.wsgi_headeritems()
+ trans.response.status = 200
+ return archive.stream
+ elif format == 'uncompressed':
+ if len(lddas) != 1:
+ trans.response.status = 400
+ return 'Wrong request'
+ else:
+ single_dataset = lddas[0]
+ trans.response.set_content_type( single_dataset.get_mime() )
+ fStat = os.stat( ldda.file_name )
+ trans.response.headers[ 'Content-Length' ] = int( fStat.st_size )
+ valid_chars = '.,^_-()[]0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ fname = ldda.name
+ fname = ''.join( c in valid_chars and c or '_' for c in fname )[ 0:150 ]
+ trans.response.headers[ "Content-Disposition" ] = 'attachment; filename="%s"' % fname
+ try:
+ trans.response.status = 200
+ return open( single_dataset.file_name )
+ except:
+ trans.response.status = 500
+ return 'This dataset contains no content'
+ else:
+ trans.response.status = 400
+ return 'Wrong format parameter specified';
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b lib/galaxy/webapps/galaxy/api/libraries.py
--- a/lib/galaxy/webapps/galaxy/api/libraries.py
+++ b/lib/galaxy/webapps/galaxy/api/libraries.py
@@ -49,9 +49,10 @@
trans.model.Library.table.c.id.in_( accessible_restricted_library_ids ) ) )
rval = []
for library in query:
- item = library.to_dict()
+ item = library.to_dict( view='element' )
item['url'] = url_for( route, id=trans.security.encode_id( library.id ) )
- item['id'] = trans.security.encode_id( item['id'] )
+ item['id'] = 'F' + trans.security.encode_id( item['id'] )
+ item['root_folder_id'] = 'F' + trans.security.encode_id( item['root_folder_id'] )
rval.append( item )
return rval
@@ -131,6 +132,9 @@
rval['name'] = name
rval['id'] = encoded_id
return rval
+
+ def edit( self, trans, payload, **kwd ):
+ return "Not implemented yet"
@web.expose_api
def delete( self, trans, id, **kwd ):
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b lib/galaxy/webapps/galaxy/buildapp.py
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -46,12 +46,6 @@
atexit.register( app.shutdown )
# Create the universe WSGI application
webapp = GalaxyWebApplication( app, session_cookie='galaxysession', name='galaxy' )
- # Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
- webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
- controller='admin_toolshed',
- action='display_image_in_repository',
- repository_id=None,
- image_file=None )
webapp.add_ui_controllers( 'galaxy.webapps.galaxy.controllers', app )
# Force /history to go to /root/history -- needed since the tests assume this
webapp.add_route( '/history', controller='root', action='history' )
@@ -75,22 +69,12 @@
webapp.add_route( '/u/:username/v/:slug', controller='visualization', action='display_by_username_and_slug' )
webapp.add_route( '/search', controller='search', action='index' )
- # Add the web API
+ # ================
+ # ===== API =====
+ # ================
+
webapp.add_api_controllers( 'galaxy.webapps.galaxy.api', app )
- # The /folders section is experimental at this point:
- log.debug( "app.config.api_folders: %s" % app.config.api_folders )
- webapp.mapper.resource( 'folder', 'folders', path_prefix='/api' )
- webapp.mapper.resource( 'content', 'contents',
- controller='folder_contents',
- name_prefix='folder_',
- path_prefix='/api/folders/:folder_id',
- parent_resources=dict( member_name='folder', collection_name='folders' ) )
- webapp.mapper.resource( 'content',
- 'contents',
- controller='library_contents',
- name_prefix='library_',
- path_prefix='/api/libraries/:library_id',
- parent_resources=dict( member_name='library', collection_name='libraries' ) )
+
webapp.mapper.resource( 'content',
'contents',
controller='history_contents',
@@ -102,10 +86,6 @@
controller="datasets",
action="display",
conditions=dict(method=["GET"]))
- webapp.mapper.resource( 'permission',
- 'permissions',
- path_prefix='/api/libraries/:library_id',
- parent_resources=dict( member_name='library', collection_name='libraries' ) )
webapp.mapper.resource( 'user',
'users',
controller='group_users',
@@ -127,11 +107,6 @@
_add_item_tags_controller( webapp,
name_prefix="workflow_",
path_prefix='/api/workflows/:workflow_id' )
-
- _add_item_extended_metadata_controller( webapp,
- name_prefix="library_dataset_",
- path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
-
_add_item_annotation_controller( webapp,
name_prefix="history_content_",
path_prefix='/api/histories/:history_id/contents/:history_content_id' )
@@ -141,7 +116,6 @@
_add_item_annotation_controller( webapp,
name_prefix="workflow_",
path_prefix='/api/workflows/:workflow_id' )
-
_add_item_provenance_controller( webapp,
name_prefix="history_content_",
path_prefix='/api/histories/:history_id/contents/:history_content_id' )
@@ -170,6 +144,7 @@
parent_resources=dict( member_name='datatype', collection_name='datatypes' ) )
#webapp.mapper.connect( 'run_workflow', '/api/workflow/{workflow_id}/library/{library_id}', controller='workflows', action='run', workflow_id=None, library_id=None, conditions=dict(method=["GET"]) )
webapp.mapper.resource( 'search', 'search', path_prefix='/api' )
+
# add as a non-ATOM API call to support the notion of a 'current/working' history unique to the history resource
webapp.mapper.connect( "set_as_current", "/api/histories/{id}/set_as_current",
@@ -188,6 +163,64 @@
webapp.mapper.connect("workflow_dict", '/api/workflows/{workflow_id}/download', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
# Preserve the following download route for now for dependent applications -- deprecate at some point
webapp.mapper.connect("workflow_dict", '/api/workflows/download/{workflow_id}', controller='workflows', action='workflow_dict', conditions=dict(method=['GET']))
+
+ # =======================
+ # ===== LIBRARY API =====
+ # =======================
+
+ webapp.mapper.connect( 'show_lda_item',
+ '/api/libraries/datasets/:id',
+ controller='lda_datasets',
+ action='show',
+ conditions=dict( method=[ "GET" ] ) )
+
+ webapp.mapper.connect( 'download_lda_items',
+ '/api/libraries/datasets/download/:format',
+ controller='lda_datasets',
+ action='download',
+ conditions=dict( method=[ "POST", "GET" ] ) )
+
+ webapp.mapper.resource_with_deleted( 'library',
+ 'libraries',
+ path_prefix='/api' )
+ webapp.mapper.resource( 'folder',
+ 'folders',
+ path_prefix='/api' )
+
+ webapp.mapper.resource( 'content',
+ 'contents',
+ controller='folder_contents',
+ name_prefix='folder_',
+ path_prefix='/api/folders/:folder_id',
+ parent_resources=dict( member_name='folder', collection_name='folders' ) )
+
+ webapp.mapper.resource( 'content',
+ 'contents',
+ controller='library_contents',
+ name_prefix='library_',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
+
+ webapp.mapper.resource( 'permission',
+ 'permissions',
+ path_prefix='/api/libraries/:library_id',
+ parent_resources=dict( member_name='library', collection_name='libraries' ) )
+
+ _add_item_extended_metadata_controller( webapp,
+ name_prefix="library_dataset_",
+ path_prefix='/api/libraries/:library_id/contents/:library_content_id' )
+
+ # ====================
+ # ===== TOOLSHED =====
+ # ====================
+
+ # Handle displaying tool help images and README file images contained in repositories installed from the tool shed.
+ webapp.add_route( '/admin_toolshed/static/images/:repository_id/:image_file',
+ controller='admin_toolshed',
+ action='display_image_in_repository',
+ repository_id=None,
+ image_file=None )
+
# Galaxy API for tool shed features.
webapp.mapper.resource( 'tool_shed_repository',
'tool_shed_repositories',
@@ -201,6 +234,7 @@
path_prefix='/api',
new={ 'install_repository_revision' : 'POST' },
parent_resources=dict( member_name='tool_shed_repository', collection_name='tool_shed_repositories' ) )
+
# Connect logger from app
if app.trace_logger:
webapp.trace_logger = app.trace_logger
@@ -221,7 +255,7 @@
galaxy.model.mapping.metadata.engine.connection_provider._pool.dispose()
except:
pass
- # Close any pooled database connections before forking
+ # Close any pooled database connections before forking
try:
galaxy.model.tool_shed_install.mapping.metadata.engine.connection_provider._pool.dispose()
except:
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b lib/galaxy/webapps/galaxy/controllers/library.py
--- a/lib/galaxy/webapps/galaxy/controllers/library.py
+++ b/lib/galaxy/webapps/galaxy/controllers/library.py
@@ -76,6 +76,17 @@
library_list_grid = LibraryListGrid()
+
+ @web.expose
+ def list( self, trans, **kwd ):
+ params = util.Params( kwd )
+ # define app configuration for generic mako template
+ app = {
+ 'jscript' : "galaxy.library"
+ }
+ # fill template
+ return trans.fill_template('galaxy.panels.mako', config = {'app' : app})
+
@web.expose
def index( self, trans, **kwd ):
params = util.Params( kwd )
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b lib/galaxy/webapps/galaxy/controllers/user.py
--- a/lib/galaxy/webapps/galaxy/controllers/user.py
+++ b/lib/galaxy/webapps/galaxy/controllers/user.py
@@ -826,19 +826,22 @@
if email is None or activation_token is None:
# We don't have the email or activation_token, show error.
- return trans.show_error_message( "You are using wrong activation link. Try to log-in and we will send you a new activation email.<br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller="root", action="index" )
+ return trans.show_error_message( "You are using wrong activation link. Try to log-in and we will send you a new activation email. <br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller="root", action="index" )
else:
# Find the user
user = trans.sa_session.query( trans.app.model.User ).filter( trans.app.model.User.table.c.email==email ).first()
+ # If the user is active already don't try to activate
+ if user.active == True:
+ return trans.show_ok_message( "Your account is already active. Nothing has changed. <br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
if user.activation_token == activation_token:
user.activation_token = None
user.active = True
trans.sa_session.add(user)
trans.sa_session.flush()
- return trans.show_ok_message( "Your account has been successfully activated!<br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
+ return trans.show_ok_message( "Your account has been successfully activated! <br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
else:
# Tokens don't match. Activation is denied.
- return trans.show_error_message( "You are using wrong activation link. Try to log in and we will send you a new activation email.<br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
+ return trans.show_error_message( "You are using wrong activation link. Try to log in and we will send you a new activation email. <br><a href='%s'>Go to login page.</a>" ) % web.url_for( controller='root', action='index' )
return
def __get_user_type_form_definition( self, trans, user=None, **kwd ):
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b static/scripts/galaxy.library.js
--- /dev/null
+++ b/static/scripts/galaxy.library.js
@@ -0,0 +1,902 @@
+// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+// === GALAXY LIBRARY MODULE ====
+// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+
+// global variables
+var view = null;
+var library_router = null;
+var responses = [];
+
+// dependencies
+define([
+ "galaxy.modal",
+ "galaxy.masthead",
+ "utils/galaxy.utils",
+ "libs/toastr"], function(mod_modal, mod_masthead, mod_utils, mod_toastr) {
+
+// MMMMMMMMMMMMMMM
+// === Models ====
+// MMMMMMMMMMMMMMM
+
+ // LIBRARY
+ var Library = Backbone.Model.extend({
+ urlRoot: '/api/libraries'
+ });
+
+ // LIBRARIES
+ var Libraries = Backbone.Collection.extend({
+ url: '/api/libraries',
+ model: Library
+ });
+
+ // ITEM
+ var Item = Backbone.Model.extend({
+ urlRoot : '/api/libraries/datasets'
+ })
+
+ // FOLDER
+ var Folder = Backbone.Collection.extend({
+ model: Item
+ })
+
+ // CONTAINER for folder contents (folders, items and metadata).
+ var FolderContainer = Backbone.Model.extend({
+ defaults : {
+ folder : new Folder(),
+ full_path : "unknown",
+ urlRoot : "/api/folders/",
+ id : "unknown"
+ },
+ parse : function(obj) {
+ this.full_path = obj[0].full_path;
+ // update the inner collection
+ this.get("folder").reset(obj[1].folder_contents);
+ return obj;
+ }
+ })
+
+ // HISTORY ITEM
+ var HistoryItem = Backbone.Model.extend({
+ urlRoot : '/api/histories/'
+ });
+
+ // HISTORY
+ var GalaxyHistory = Backbone.Model.extend({
+ url : '/api/histories/'
+ });
+
+ // HISTORIES
+ var GalaxyHistories = Backbone.Collection.extend({
+ url : '/api/histories',
+ model : GalaxyHistory
+ });
+
+ //ROUTER
+ var LibraryRouter = Backbone.Router.extend({
+ routes: {
+ "" : "libraries",
+ "folders/:id" : "folder_content",
+ "folders/:folder_id/download/:format" : "download"
+ }
+ });
+
+
+// MMMMMMMMMMMMMM
+// === VIEWS ====
+// MMMMMMMMMMMMMM
+
+// galaxy folder
+var FolderContentView = Backbone.View.extend({
+ // main element definition
+ el : '#center',
+ // progress percentage
+ progress: 0,
+ // progress rate per one item
+ progressStep: 1,
+ // last selected history in modal for UX
+ lastSelectedHistory: '',
+ // self modal
+ modal : null,
+ // loaded folders
+ folders : null,
+
+ // initialize
+ initialize : function(){
+ this.folders = [];
+ this.queue = jQuery.Deferred();
+ this.queue.resolve();
+ },
+
+// MMMMMMMMMMMMMMMMMM
+// === TEMPLATES ====
+// MMMMMMMMMMMMMMMMMM
+
+ // set up
+ templateFolder : function (){
+ var tmpl_array = [];
+
+ // CONTAINER
+ tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; ">');
+ tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');
+
+ // TOOLBAR
+ tmpl_array.push('<div id="library_folder_toolbar" >');
+ tmpl_array.push(' <button title="Create New Folder" id="toolbtn_create_folder" class="btn btn-primary" type="button"><span class="fa fa-plus"></span><span class="fa fa-folder-close"></span> folder</button>');
+ tmpl_array.push(' <button id="toolbtn_bulk_import" class="btn btn-primary" style="display: none; margin-left: 0.5em;" type="button"><span class="fa fa-external-link"></span> to history</button>');
+
+ tmpl_array.push(' <div id="toolbtn_dl" class="btn-group" style="margin-left: 0.5em; display: none; ">');
+ tmpl_array.push(' <button id="drop_toggle" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">');
+ tmpl_array.push(' <span class="fa fa-download"></span> download <span class="caret"></span>');
+ tmpl_array.push(' </button>');
+ tmpl_array.push(' <ul class="dropdown-menu" role="menu">');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tgz">.tar.gz</a></li>');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/tbz">.tar.bz</a></li>');
+ tmpl_array.push(' <li><a href="#/folders/<%= id %>/download/zip">.zip</a></li>');
+ tmpl_array.push(' </ul>');
+ tmpl_array.push(' </div>');
+
+ tmpl_array.push('</div>');
+
+ // BREADCRUMBS
+ tmpl_array.push('<div class="library_breadcrumb">');
+ tmpl_array.push('<a title="Return to the list of libraries" href="#">Libraries</a><b>|</b> ');
+ tmpl_array.push('<% _.each(path, function(path_item) { %>');
+ tmpl_array.push('<% if (path_item[0] != id) { %>');
+ tmpl_array.push('<a title="Return to this folder" href="#/folders/<%- path_item[0] %>"><%- path_item[1] %></a><b>|</b> ');
+ tmpl_array.push('<% } else { %>');
+ tmpl_array.push('<span title="You are in this folder"><%- path_item[1] %></span>');
+ tmpl_array.push('<% } %>');
+ tmpl_array.push('<% }); %>');
+ tmpl_array.push('</div>');
+
+ // FOLDER CONTENT
+ tmpl_array.push('<table id="folder_table" class="table table-condensed">');
+ tmpl_array.push(' <thead>');
+ tmpl_array.push(' <th style="text-align: center; width: 20px; "><input id="select-all-checkboxes" style="margin: 0;" type="checkbox"></th>');
+ tmpl_array.push(' <th class="button_heading">view</th>');
+ tmpl_array.push(' <th>name</th>');
+ tmpl_array.push(' <th>data type</th>');
+ tmpl_array.push(' <th>size</th>');
+ tmpl_array.push(' <th>date</th>');
+ tmpl_array.push(' </thead>');
+ tmpl_array.push(' <tbody>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td><button title="Go to parent folder" type="button" data-id="<%- upper_folder_id %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-arrow-up"></span> .. go up</td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% _.each(items, function(content_item) { %>');
+ tmpl_array.push(' <tr class="folder_row light" id="<%- content_item.id %>">');
+ tmpl_array.push(' <% if (content_item.get("type") === "folder") { %>'); // folder
+ tmpl_array.push(' <td></td>');
+ tmpl_array.push(' <td><button title="Open this folder" type="button" data-id="<%- content_item.id %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-folder-open"></span> browse</td>');
+ tmpl_array.push(' <td><%- content_item.get("name") %>');
+ tmpl_array.push(' <% if (content_item.get("item_count") === 0) { %>'); // empty folder
+ tmpl_array.push(' <span class="muted">(empty folder)</span>');
+ tmpl_array.push(' <% } %>');
+ tmpl_array.push(' </td>');
+ tmpl_array.push(' <td>folder</td>'); // data type
+ tmpl_array.push(' <td><%= _.escape(content_item.get("item_count")) %> item(s)</td>'); // size
+ tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td style="text-align: center; "><input style="margin: 0;" type="checkbox"></td>');
+ tmpl_array.push(' <td>');
+ tmpl_array.push(' <button title="See details of this dataset" type="button" class="library-dataset btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-eye"></span> details');
+ tmpl_array.push(' </button>');
+ tmpl_array.push(' </td>');
+ tmpl_array.push(' <td><%- content_item.get("name") %></td>'); // dataset
+ tmpl_array.push(' <td><%= _.escape(content_item.get("data_type")) %></td>'); // data type
+ tmpl_array.push(' <td><%= _.escape(content_item.get("readable_size")) %></td>'); // size
+ tmpl_array.push(' <% } %> ');
+ tmpl_array.push(' <td><%= _.escape(content_item.get("time_updated")) %></td>'); // time updated
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push(' ');
+ tmpl_array.push(' </tbody>');
+ tmpl_array.push('</table>');
+
+ tmpl_array.push('</div>');
+ return tmpl_array.join('');
+ },
+ templateDatasetModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<div id="dataset_info_modal">');
+ tmpl_array.push(' <table class="table table-striped table-condensed">');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row" id="id_row" data-id="<%= _.escape(item.get("ldda_id")) %>">Name</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("name")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Data type</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("data_type")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Genome build</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("genome_build")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <th scope="row">Size</th>');
+ tmpl_array.push(' <td><%= _.escape(size) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Date uploaded</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("date_uploaded")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Uploaded by</th>');
+ tmpl_array.push(' <td><%= _.escape(item.get("uploaded_by")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr scope="row">');
+ tmpl_array.push(' <th scope="row">Data Lines</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_data_lines")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <th scope="row">Comment Lines</th>');
+ tmpl_array.push(' <% if (item.get("metadata_comment_lines") === "") { %>'); //folder
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_comment_lines")) %></td>');
+ tmpl_array.push(' <% } else { %>');
+ tmpl_array.push(' <td scope="row">unknown</td>');
+ tmpl_array.push(' <% } %>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Number of Columns</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_columns")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Column Types</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("metadata_column_types")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <th scope="row">Miscellaneous information</th>');
+ tmpl_array.push(' <td scope="row"><%= _.escape(item.get("misc_blurb")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' </table>');
+ tmpl_array.push(' <pre class="peek">');
+ tmpl_array.push(' </pre>');
+ tmpl_array.push('</div>');
+
+ return tmpl_array.join('');
+ },
+
+ templateHistorySelectInModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<span id="history_modal_combo" style="width:90%; margin-left: 1em; margin-right: 1em; ">');
+ tmpl_array.push('Select history: ');
+ tmpl_array.push('<select id="dataset_import_single" name="dataset_import_single" style="width:50%; margin-bottom: 1em; "> ');
+ tmpl_array.push(' <% _.each(histories, function(history) { %>'); //history select box
+ tmpl_array.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push('</select>');
+ tmpl_array.push('</span>');
+
+ return tmpl_array.join('');
+ },
+
+ templateBulkImportInModal : function(){
+ var tmpl_array = [];
+
+ tmpl_array.push('<span id="history_modal_combo_bulk" style="width:90%; margin-left: 1em; margin-right: 1em; ">');
+ tmpl_array.push('Select history: ');
+ tmpl_array.push('<select id="dataset_import_bulk" name="dataset_import_bulk" style="width:50%; margin-bottom: 1em; "> ');
+ tmpl_array.push(' <% _.each(histories, function(history) { %>'); //history select box
+ tmpl_array.push(' <option value="<%= _.escape(history.get("id")) %>"><%= _.escape(history.get("name")) %></option>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push('</select>');
+ tmpl_array.push('</span>');
+
+ return tmpl_array.join('');
+ },
+
+ // convert size to nice string
+ size_to_string : function (size)
+ {
+ // identify unit
+ var unit = "";
+ if (size >= 100000000000) { size = size / 100000000000; unit = "TB"; } else
+ if (size >= 100000000) { size = size / 100000000; unit = "GB"; } else
+ if (size >= 100000) { size = size / 100000; unit = "MB"; } else
+ if (size >= 100) { size = size / 100; unit = "KB"; } else
+ { size = size * 10; unit = "b"; }
+ // return formatted string
+ return (Math.round(size) / 10) + unit;
+ },
+
+// MMMMMMMMMMMMMMM
+// === EVENTS ====
+// MMMMMMMMMMMMMMM
+
+ // event binding
+ events: {
+ 'click #select-all-checkboxes' : 'selectAll',
+ 'click .folder_row' : 'selectClickedRow',
+ 'click #toolbtn_bulk_import' : 'modalBulkImport',
+ 'click #toolbtn_dl' : 'bulkDownload',
+ 'click .library-dataset' : 'showDatasetDetails',
+ 'click #toolbtn_create_folder' : 'createFolderModal',
+ 'click .btn_open_folder' : 'navigateToFolder'
+ },
+
+ //render the folder view
+ render: function (options) {
+ //hack to show scrollbars
+ $("#center").css('overflow','auto');
+
+ view = this;
+ var that = this;
+
+ var folderContainer = new FolderContainer({id: options.id});
+ folderContainer.url = folderContainer.attributes.urlRoot + options.id + '/contents';
+
+ folderContainer.fetch({
+ success: function (container) {
+
+ // prepare nice size strings
+ for (var i = 0; i < folderContainer.attributes.folder.models.length; i++) {
+ var model = folderContainer.attributes.folder.models[i]
+ if (model.get('type') === 'file'){
+ model.set('readable_size', that.size_to_string(model.get('file_size')))
+ }
+ };
+
+ // find the upper id
+ var path = folderContainer.full_path;
+ var upper_folder_id;
+ if (path.length === 1){ // library is above us
+ upper_folder_id = 0;
+ } else {
+ upper_folder_id = path[path.length-2][0];
+ }
+
+ var template = _.template(that.templateFolder(), { path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id, upper_folder_id: upper_folder_id });
+ // var template = _.template(that.templateFolder(), { path: folderContainer.full_path, items: folderContainer.attributes.folder.models, id: options.id });
+ that.$el.html(template);
+
+ }
+ })
+ },
+
+ // handles the click on 'open' and 'upper' folder icons
+ navigateToFolder : function(event){
+ var folder_id = $(event.target).attr('data-id');
+ if (typeof folder_id === 'undefined') {
+ return false;
+ } else if (folder_id === '0'){
+ library_router.navigate('#', {trigger: true, replace: true});
+ } else {
+ library_router.navigate('folders/' + folder_id, {trigger: true, replace: true});
+ }
+ },
+
+ //show modal with current dataset info
+ showDatasetDetails : function(event){
+ // prevent default
+ event.preventDefault();
+
+//TODO check whether we already have the data
+
+ //load the ID of the row
+ var id = $(event.target).parent().parent().attr('id');
+
+ //create new item
+ var item = new Item();
+ var histories = new GalaxyHistories();
+ item.id = id;
+ var self = this;
+
+ //fetch the dataset info
+ item.fetch({
+ success: function (item) {
+// TODO can render here already
+ //fetch user histories for import purposes
+ histories.fetch({
+ success: function (histories){self.renderModalAfterFetch(item, histories)}
+ });
+ }
+ });
+ },
+
+ // show the current dataset in a modal
+ renderModalAfterFetch : function(item, histories){
+ var size = this.size_to_string(item.get('file_size'));
+ var template = _.template(this.templateDatasetModal(), { item : item, size : size });
+ this.modal = null;
+ // make modal
+ var self = this;
+ this.modal = new mod_modal.GalaxyModal({
+ title : 'Dataset Details',
+ body : template,
+ buttons : {
+ 'Import' : function() { self.importCurrentIntoHistory() },
+ 'Download' : function() { self.downloadCurrent() },
+ 'Close' : function() { self.modal.hide(); $('.modal').remove(); self.modal = null; } // TODO refill nicely modal with data
+ }
+ });
+ $(".peek").html(item.get("peek"));
+ var history_footer_tmpl = _.template(this.templateHistorySelectInModal(), {histories : histories.models});
+ $(this.modal.elMain).find('.buttons').prepend(history_footer_tmpl);
+
+ // preset last selected history if we know it
+ if (self.lastSelectedHistory.length > 0) {
+ $(this.modal.elMain).find('#dataset_import_single').val(self.lastSelectedHistory);
+ }
+
+ // show the prepared modal
+ this.modal.show();
+ },
+
+ // download dataset shown currently in modal
+ downloadCurrent : function(){
+ //disable the buttons
+ this.modal.disableButton('Import');
+ this.modal.disableButton('Download');
+
+ var library_dataset_id = [];
+ library_dataset_id.push($('#id_row').attr('data-id'));
+ var url = '/api/libraries/datasets/download/uncompressed';
+ var data = {'ldda_ids' : library_dataset_id};
+
+ // we assume the view is existent
+ folderContentView.processDownload(url, data);
+ this.modal.enableButton('Import');
+ this.modal.enableButton('Download');
+ },
+
+ // import dataset shown currently in modal into selected history
+ importCurrentIntoHistory : function(){
+ //disable the buttons
+ this.modal.disableButton('Import');
+ this.modal.disableButton('Download');
+
+ var history_id = $(this.modal.elMain).find('select[name=dataset_import_single] option:selected').val();
+ this.lastSelectedHistory = history_id; //save selected history for further use
+
+ var library_dataset_id = $('#id_row').attr('data-id');
+ var historyItem = new HistoryItem();
+ var self = this;
+ historyItem.url = historyItem.urlRoot + history_id + '/contents';
+
+ // save the dataset into selected history
+ historyItem.save({ content : library_dataset_id, source : 'library' }, { success : function(){
+ mod_toastr.success('Dataset imported');
+ //enable the buttons
+ self.modal.enableButton('Import');
+ self.modal.enableButton('Download');
+ }, error : function(){
+ mod_toastr.error('An error occured! Dataset not imported. Please try again.')
+ //enable the buttons
+ self.modal.enableButton('Import');
+ self.modal.enableButton('Download');
+ }
+ });
+ },
+
+ // select all datasets
+ selectAll : function (event) {
+ var selected = event.target.checked;
+ that = this;
+ // Iterate each checkbox
+ $(':checkbox').each(function () {
+ this.checked = selected;
+ $row = $(this.parentElement.parentElement);
+ // Change color of selected/unselected
+ (selected) ? that.makeDarkRow($row) : that.makeWhiteRow($row);
+ });
+ // Show the tools in menu
+ this.checkTools();
+ },
+
+ // Check checkbox on row itself or row checkbox click
+ selectClickedRow : function (event) {
+ var checkbox = '';
+ var $row;
+ var source;
+ if (event.target.localName === 'input'){
+ checkbox = event.target;
+ $row = $(event.target.parentElement.parentElement);
+ source = 'input';
+ } else if (event.target.localName === 'td') {
+ checkbox = $("#" + event.target.parentElement.id).find(':checkbox')[0];
+ $row = $(event.target.parentElement);
+ source = 'td';
+ }
+ if (checkbox === '') {event.stopPropagation(); return;} // button in row was clicked
+
+ if (checkbox.checked){
+ if (source==='td'){
+ checkbox.checked = '';
+ this.makeWhiteRow($row);
+ } else if (source==='input') {
+ this.makeDarkRow($row);
+ }
+ } else {
+ if (source==='td'){
+ checkbox.checked = 'selected';
+ this.makeDarkRow($row);
+ } else if (source==='input') {
+ this.makeWhiteRow($row);
+ }
+ }
+ this.checkTools();
+ },
+
+ makeDarkRow: function($row){
+ $row.removeClass('light');
+ $row.find('a').removeClass('light');
+ $row.addClass('dark');
+ $row.find('a').addClass('dark');
+ },
+
+ makeWhiteRow: function($row){
+ $row.removeClass('dark');
+ $row.find('a').removeClass('dark');
+ $row.addClass('light');
+ $row.find('a').addClass('light');
+ },
+
+ // show toolbar in case something is selected
+ checkTools : function(){
+ var checkedValues = $('#folder_table').find(':checked');
+ if(checkedValues.length > 0){
+ $('#toolbtn_bulk_import').show();
+ $('#toolbtn_dl').show();
+ } else {
+ $('#toolbtn_bulk_import').hide();
+ $('#toolbtn_dl').hide();
+ }
+
+ },
+
+ // show bulk import modal
+ modalBulkImport : function(){
+ var self = this;
+ // fetch histories
+ var histories = new GalaxyHistories();
+ histories.fetch({
+ success: function (histories){
+ // make modal
+ var history_modal_tmpl = _.template(self.templateBulkImportInModal(), {histories : histories.models});
+ self.modal = new mod_modal.GalaxyModal({
+ title : 'Import into History',
+ body : history_modal_tmpl,
+ buttons : {
+ 'Import' : function() {self.importAllIntoHistory()},
+ 'Close' : function() {self.modal.hide(); $('.modal').remove(); self.modal = null;}
+ }
+ });
+ // show the prepared modal
+ self.modal.show();
+ }
+ });
+ },
+
+ // import all selected datasets into history
+ importAllIntoHistory : function (){
+ //disable the button
+ this.modal.disableButton('Import');
+
+ var history_id = $("select[name=dataset_import_bulk] option:selected").val();
+ var history_name = $("select[name=dataset_import_bulk] option:selected").text();
+
+ var dataset_ids = [];
+ $('#folder_table').find(':checked').each(function(){
+ if (this.parentElement.parentElement.id != '') {
+ dataset_ids.push(this.parentElement.parentElement.id);
+ }
+ });
+ var progress_bar_tmpl = _.template(this.templateProgressBar(), { history_name : history_name });
+ $(this.modal.elMain).find('.modal-body').html(progress_bar_tmpl);
+
+ // init the progress bar
+ var progressStep = 100 / dataset_ids.length;
+ this.initProgress(progressStep);
+
+ // prepare the dataset objects to be imported
+ var datasets_to_import = [];
+ for (var i = dataset_ids.length - 1; i >= 0; i--) {
+ library_dataset_id = dataset_ids[i];
+ var historyItem = new HistoryItem();
+ var self = this;
+ historyItem.url = historyItem.urlRoot + history_id + '/contents';
+ historyItem.content = library_dataset_id;
+ historyItem.source = 'library';
+ datasets_to_import.push(historyItem);
+ };
+
+ // call the recursive function to call ajax one after each other
+ this.chainCall(datasets_to_import);
+ },
+
+ chainCall: function(history_item_set){
+ var self = this;
+ var popped_item = history_item_set.pop();
+ if (typeof popped_item === "undefined") {
+ mod_toastr.success('All datasets imported');
+ this.modal.hide();
+ // enable button again
+ self.modal.enableButton('Import');
+ return
+ }
+ var promise = $.when(popped_item.save({content: popped_item.content, source: popped_item.source})).done(function(a1){
+ self.updateProgress();
+ responses.push(a1);
+ self.chainCall(history_item_set);
+ });
+ },
+
+ initProgress: function(progressStep){
+ this.progress = 0;
+ this.progressStep = progressStep;
+ },
+ updateProgress: function(){
+ this.progress += this.progressStep;
+ $('.progress-bar').width(Math.round(this.progress) + '%');
+ txt_representation = Math.round(this.progress) + '% Complete';
+ $('.completion_span').text(txt_representation);
+ },
+
+ // progress bar
+ templateProgressBar : function (){
+ var tmpl_array = [];
+
+ tmpl_array.push('<div class="import_text">');
+ tmpl_array.push('Importing selected datasets to history <b><%= _.escape(history_name) %></b>');
+ tmpl_array.push('</div>');
+ tmpl_array.push('<div class="progress">');
+ tmpl_array.push(' <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 00%;">');
+ tmpl_array.push(' <span class="completion_span">0% Complete</span>');
+ tmpl_array.push(' </div>');
+ tmpl_array.push('</div>');
+ tmpl_array.push('');
+
+ return tmpl_array.join('');
+ },
+
+ // download selected datasets
+ download : function(folder_id, format){
+ var dataset_ids = [];
+ $('#folder_table').find(':checked').each(function(){
+ if (this.parentElement.parentElement.id != '') {
+ dataset_ids.push(this.parentElement.parentElement.id);
+ }
+ });
+
+ var url = '/api/libraries/datasets/download/' + format;
+ var data = {'ldda_ids' : dataset_ids};
+ this.processDownload(url, data, 'get');
+ },
+
+ // create hidden form and submit through POST to initialize download
+ processDownload: function(url, data, method){
+ //url and data options required
+ if( url && data ){
+ //data can be string of parameters or array/object
+ data = typeof data == 'string' ? data : $.param(data);
+ //split params into form inputs
+ var inputs = '';
+ $.each(data.split('&'), function(){
+ var pair = this.split('=');
+ inputs+='<input type="hidden" name="'+ pair[0] +'" value="'+ pair[1] +'" />';
+ });
+ //send request
+ $('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
+ .appendTo('body').submit().remove();
+ mod_toastr.info('Your download will begin soon');
+ };
+ },
+
+ // shows modal for creating folder
+ createFolderModal: function(){
+ mod_toastr.info('This will create folder...in the future');
+ }
+
+ });
+
+// galaxy library view
+var GalaxyLibraryview = Backbone.View.extend({
+ el: '#center',
+
+ events: {
+ 'click #create_new_library_btn' : 'show_library_modal'
+ },
+
+ // initialize
+ initialize : function(){
+ },
+
+ // template
+ template_library_list : function (){
+ tmpl_array = [];
+ tmpl_array.push('<div id="library_container" style="width: 90%; margin: auto; margin-top: 2em; overflow: auto !important; ">');
+
+ tmpl_array.push('');
+ tmpl_array.push('<h3>New Data Libraries. This is work in progress. Report problems & ideas to <a href="mailto:marten@bx.psu.edu?Subject=DataLibraries_Feedback" target="_blank">Marten</a>.</h3>');
+ tmpl_array.push('<a href="" id="create_new_library_btn" class="btn btn-primary file ">New Library</a>');
+ tmpl_array.push('<table class="table table-condensed">');
+ tmpl_array.push(' <thead>');
+ tmpl_array.push(' <th class="button_heading"></th>');
+ tmpl_array.push(' <th>name</th>');
+ tmpl_array.push(' <th>description</th>');
+ tmpl_array.push(' <th>synopsis</th> ');
+ tmpl_array.push(' <th>model type</th> ');
+ tmpl_array.push(' </thead>');
+ tmpl_array.push(' <tbody>');
+ tmpl_array.push(' <% _.each(libraries, function(library) { %>');
+ tmpl_array.push(' <tr>');
+ tmpl_array.push(' <td><button title="Open this library" type="button" data-id="<%- library.get("root_folder_id") %>" class="btn_open_folder btn btn-default btn-xs">');
+ tmpl_array.push(' <span class="fa fa-folder-open"></span> browse</td>');
+ tmpl_array.push(' <td><%- library.get("name") %></td>');
+ tmpl_array.push(' <td><%= _.escape(library.get("description")) %></td>');
+ tmpl_array.push(' <td><%= _.escape(library.get("synopsis")) %></td>');
+ tmpl_array.push(' <td><%= _.escape(library.get("model_class")) %></td>');
+ tmpl_array.push(' </tr>');
+ tmpl_array.push(' <% }); %>');
+ tmpl_array.push(' </tbody>');
+ tmpl_array.push('</table>');
+
+ tmpl_array.push('</div>');
+ return tmpl_array.join('');
+ },
+
+ // render
+ render: function () {
+ //hack to show scrollbars due to #center element inheritance
+ $("#center").css('overflow','auto');
+
+ var that = this;
+ libraries = new Libraries();
+
+ libraries.fetch({
+ success: function (libraries) {
+ var template = _.template(that.template_library_list(), { libraries : libraries.models });
+ that.$el.html(template);
+ },
+ error: function(model, response){
+ if (response.statusCode().status === 403){
+ mod_toastr.error('Please log in first. Redirecting to login page in 3s.');
+ setTimeout(that.redirectToLogin, 3000);
+ } else {
+ mod_toastr.error('An error occured. Please try again.');
+ }
+ }
+ })
+ },
+
+ redirectToHome: function(){
+ window.location = '../';
+ },
+ redirectToLogin: function(){
+ window.location = '/user/login';
+ },
+
+ // own modal
+ modal : null,
+
+ // show/hide create library modal
+ show_library_modal : function (event){
+ event.preventDefault();
+ event.stopPropagation();
+
+ // create modal
+ var self = this;
+ this.modal = new mod_modal.GalaxyModal(
+ {
+ title : 'Create New Library',
+ body : this.template_new_library(),
+ buttons : {
+ 'Create' : function() {self.create_new_library_event()},
+ 'Close' : function() {self.modal.hide()}
+ }
+ });
+
+ // show prepared modal
+ this.modal.show();
+ },
+
+ // create the new library from modal
+ create_new_library_event: function(){
+ var libraryDetails = this.serialize_new_library();
+ if (this.validate_new_library(libraryDetails)){
+ var library = new Library();
+ var self = this;
+ library.save(libraryDetails, {
+ success: function (library) {
+ self.modal.hide();
+ self.clear_library_modal();
+ self.render();
+ mod_toastr.success('Library created');
+ },
+ error: function(){
+ mod_toastr.error('An error occured :(');
+ }
+ });
+ } else {
+ mod_toastr.error('Library\'s name is missing');
+ }
+ return false;
+ },
+
+ // clear the library modal once saved
+ clear_library_modal : function(){
+ $("input[name='Name']").val('');
+ $("input[name='Description']").val('');
+ $("input[name='Synopsis']").val('');
+ },
+
+ // serialize data from the form
+ serialize_new_library : function(){
+ return {
+ name: $("input[name='Name']").val(),
+ description: $("input[name='Description']").val(),
+ synopsis: $("input[name='Synopsis']").val()
+ };
+ },
+
+ // validate new library info
+ validate_new_library: function(libraryDetails){
+ return libraryDetails.name !== '';
+ },
+
+ // template for new library modal
+ template_new_library: function(){
+ tmpl_array = [];
+
+ tmpl_array.push('<div id="new_library_modal">');
+ tmpl_array.push('<form>');
+ tmpl_array.push('<input type="text" name="Name" value="" placeholder="Name">');
+ tmpl_array.push('<input type="text" name="Description" value="" placeholder="Description">');
+ tmpl_array.push('<input type="text" name="Synopsis" value="" placeholder="Synopsis">');
+ tmpl_array.push('</form>');
+ tmpl_array.push('</div>');
+
+ return tmpl_array.join('');
+ }
+});
+
+// galaxy library wrapper View
+var GalaxyLibrary = Backbone.View.extend({
+ folderContentView : null,
+ galaxyLibraryview : null,
+ initialize : function(){
+
+ folderContentView = new FolderContentView();
+ galaxyLibraryview = new GalaxyLibraryview();
+ library_router = new LibraryRouter();
+
+ library_router.on('route:libraries', function() {
+ // render libraries list
+ galaxyLibraryview.render();
+ });
+
+ library_router.on('route:folder_content', function(id) {
+ // render folder's contents
+ folderContentView.render({id: id});
+ });
+
+ library_router.on('route:download', function(folder_id, format) {
+ if ($('#center').find(':checked').length === 0) {
+ // this happens rarely when there is a server/data error and client gets an actual response instead of an attachment
+ // we don't know what was selected so we can't download again, we redirect to the folder provided
+ library_router.navigate('folders/' + folder_id, {trigger: true, replace: true});
+ } else {
+ // send download stream
+ folderContentView.download(folder_id, format);
+ library_router.navigate('folders/' + folder_id, {trigger: false, replace: true});
+ }
+ });
+
+Backbone.history.start();
+
+return this
+}
+});
+
+// return
+return {
+ GalaxyApp: GalaxyLibrary
+};
+
+});
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b static/scripts/galaxy.menu.js
--- a/static/scripts/galaxy.menu.js
+++ b/static/scripts/galaxy.menu.js
@@ -57,7 +57,12 @@
tab_shared.add({
title : "Data Libraries",
- content : "library/index",
+ content : "library/index"
+ });
+
+ tab_shared.add({
+ title : "New Libraries",
+ content : "library/list",
divider : true
});
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b static/scripts/galaxy.modal.js
--- a/static/scripts/galaxy.modal.js
+++ b/static/scripts/galaxy.modal.js
@@ -1,6 +1,3 @@
-/*
- galaxy modal
-*/
// dependencies
define([], function() {
@@ -25,8 +22,50 @@
// initialize
initialize : function(options) {
+ self = this;
if (options)
this.create(options);
+
+ // Bind the hiding events
+ this.bindEvents(event, self);
+ },
+
+ // bind the click-to-hide function
+ bindEvents: function(event, that) {
+ // bind the ESC key to hide() function
+ $(document).on('keyup', function(event){
+ if (event.keyCode == 27) { self.hide(); }
+ })
+ // bind the 'click anywhere' to hide() function...
+ $('html').on('click', function(event){
+ that.hide();
+ })
+ // ...but don't hide if the click is on modal content
+ $('.modal-content').on('click', function(event){
+ event.stopPropagation();
+ })
+ },
+
+ // unbind the click-to-hide function
+ unbindEvents: function(event, that){
+ // bind the ESC key to hide() function
+ $(document).off('keyup', function(event){
+ if (event.keyCode == 27) { that.hide(); }
+ })
+ // unbind the 'click anywhere' to hide() function...
+ $('html').off('click', function(event){
+ that.hide();
+ })
+ $('.modal-content').off('click', function(event){
+ event.stopPropagation();
+ })
+ },
+
+ // destroy
+ destroy : function(){
+ this.hide();
+ this.unbindEvents();
+ $('.modal').remove();
},
// adds and displays a new frame/window
@@ -63,6 +102,7 @@
// set flag
this.visible = false;
+ this.unbindEvents();
},
// create
@@ -120,6 +160,15 @@
this.$buttons.find('#' + String(name).toLowerCase()).prop('disabled', true);
},
+ // hide buttons
+ hideButton: function(name) {
+ this.$buttons.find('#' + String(name).toLowerCase()).hide();
+ },
+ // show buttons
+ showButton: function(name) {
+ this.$buttons.find('#' + String(name).toLowerCase()).show();
+ },
+
// returns scroll top for body element
scrollTop: function()
{
@@ -139,7 +188,7 @@
'<div class="modal-header">' +
'<button type="button" class="close" style="display: none;">×</button>' +
'<h4 class="title">' + title + '</h4>' +
- '</div>' +
+ '</div>' +
'<div class="modal-body"></div>' +
'<div class="modal-footer">' +
'<div class="buttons" style="float: right;"></div>' +
diff -r e304da0a99681c7fb8538c6861844f17c375097e -r c1963ee06cb0fc066c111b23d99a3ea49d66f90b static/scripts/libs/bootstrap.js
--- a/static/scripts/libs/bootstrap.js
+++ b/static/scripts/libs/bootstrap.js
@@ -575,3 +575,158 @@
}
}(window.jQuery);
+
+/* ========================================================================
+ * Bootstrap: dropdown.js v3.0.0
+ * http://twbs.github.com/bootstrap/javascript.html#dropdowns
+ * ========================================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================================== */
+
+
++function ($) { "use strict";
+
+ // DROPDOWN CLASS DEFINITION
+ // =========================
+
+ var backdrop = '.dropdown-backdrop'
+ var toggle = '[data-toggle=dropdown]'
+ var Dropdown = function (element) {
+ var $el = $(element).on('click.bs.dropdown', this.toggle)
+ }
+
+ Dropdown.prototype.toggle = function (e) {
+ var $this = $(this)
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ clearMenus()
+
+ if (!isActive) {
+ if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
+ // if mobile we we use a backdrop because click events don't delegate
+ $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
+ }
+
+ $parent.trigger(e = $.Event('show.bs.dropdown'))
+
+ if (e.isDefaultPrevented()) return
+
+ $parent
+ .toggleClass('open')
+ .trigger('shown.bs.dropdown')
+
+ $this.focus()
+ }
+
+ return false
+ }
+
+ Dropdown.prototype.keydown = function (e) {
+ if (!/(38|40|27)/.test(e.keyCode)) return
+
+ var $this = $(this)
+
+ e.preventDefault()
+ e.stopPropagation()
+
+ if ($this.is('.disabled, :disabled')) return
+
+ var $parent = getParent($this)
+ var isActive = $parent.hasClass('open')
+
+ if (!isActive || (isActive && e.keyCode == 27)) {
+ if (e.which == 27) $parent.find(toggle).focus()
+ return $this.click()
+ }
+
+ var $items = $('[role=menu] li:not(.divider):visible a', $parent)
+
+ if (!$items.length) return
+
+ var index = $items.index($items.filter(':focus'))
+
+ if (e.keyCode == 38 && index > 0) index-- // up
+ if (e.keyCode == 40 && index < $items.length - 1) index++ // down
+ if (!~index) index=0
+
+ $items.eq(index).focus()
+ }
+
+ function clearMenus() {
+ $(backdrop).remove()
+ $(toggle).each(function (e) {
+ var $parent = getParent($(this))
+ if (!$parent.hasClass('open')) return
+ $parent.trigger(e = $.Event('hide.bs.dropdown'))
+ if (e.isDefaultPrevented()) return
+ $parent.removeClass('open').trigger('hidden.bs.dropdown')
+ })
+ }
+
+ function getParent($this) {
+ var selector = $this.attr('data-target')
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ var $parent = selector && $(selector)
+
+ return $parent && $parent.length ? $parent : $this.parent()
+ }
+
+
+ // DROPDOWN PLUGIN DEFINITION
+ // ==========================
+
+ var old = $.fn.dropdown
+
+ $.fn.dropdown = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ var data = $this.data('dropdown')
+
+ if (!data) $this.data('dropdown', (data = new Dropdown(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ $.fn.dropdown.Constructor = Dropdown
+
+
+ // DROPDOWN NO CONFLICT
+ // ====================
+
+ $.fn.dropdown.noConflict = function () {
+ $.fn.dropdown = old
+ return this
+ }
+
+
+ // APPLY TO STANDARD DROPDOWN ELEMENTS
+ // ===================================
+
+ $(document)
+ .on('click.bs.dropdown.data-api', clearMenus)
+ .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+ .on('click.bs.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
+ .on('keydown.bs.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
+
+}(window.jQuery);
\ No newline at end of file
This diff is so big that we needed to truncate the remainder.
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: guerler: Grids: Attempt to fix problems with toolshed
by commits-noreply@bitbucket.org 19 Dec '13
by commits-noreply@bitbucket.org 19 Dec '13
19 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/e304da0a9968/
Changeset: e304da0a9968
User: guerler
Date: 2013-12-19 18:29:41
Summary: Grids: Attempt to fix problems with toolshed
Affected #: 2 files
diff -r c91f3185e8ca68a006b755a9fdcbed3174b27fd9 -r e304da0a99681c7fb8538c6861844f17c375097e static/scripts/galaxy.grids.js
--- a/static/scripts/galaxy.grids.js
+++ b/static/scripts/galaxy.grids.js
@@ -322,7 +322,6 @@
var button = $('#grid-' + i + '-popup');
button.off();
var popup = new PopupMenu(button);
-
// load details
var item = options['items'][i];
for (var j in options['operations'])
@@ -655,9 +654,6 @@
// Initialize new grid config
self.init_grid($.parseJSON(response_text));
- // Trigger custom event to indicate grid body has changed.
- $('#grid-table-body').trigger('update');
-
// Hide loading overlay.
$('.loading-elt-overlay').hide();
},
diff -r c91f3185e8ca68a006b755a9fdcbed3174b27fd9 -r e304da0a99681c7fb8538c6861844f17c375097e static/scripts/packed/galaxy.grids.js
--- a/static/scripts/packed/galaxy.grids.js
+++ b/static/scripts/packed/galaxy.grids.js
@@ -1,1 +1,1 @@
-jQuery.ajaxSettings.traditional=true;define(["mvc/ui"],function(){var a=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},can_async_op:function(c){return _.indexOf(this.attributes.async_ops,c)!==-1},add_filter:function(g,h,d){if(d){var e=this.attributes.filters[g],c;if(e===null||e===undefined){c=h}else{if(typeof(e)=="string"){if(e=="All"){c=h}else{var f=[];f[0]=e;f[1]=h;c=f}}else{c=e;c.push(h)}}this.attributes.filters[g]=c}else{this.attributes.filters[g]=h}},remove_filter:function(d,g){var c=this.attributes.filters[d];if(c===null||c===undefined){return false}var f=true;if(typeof(c)==="string"){if(c=="All"){f=false}else{delete this.attributes.filters[d]}}else{var e=_.indexOf(c,g);if(e!==-1){c.splice(e,1)}else{f=false}}return f},get_url_data:function(){var c={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};if(this.attributes.operation){c.operation=this.attributes.operation}if(this.attributes.item_ids){c.id=this.attributes.item_ids}var d=this;_.each(_.pairs(d.attributes.filters),function(e){c["f-"+e[0]]=e[1]});return c},get_url:function(c){return this.get("url_base")+"?"+$.param(this.get_url_data())+"&"+$.param(c)}});var b=Backbone.View.extend({grid:null,initialize:function(c){this.init_grid(c);this.init_grid_controls();$("input[type=text]").each(function(){$(this).click(function(){$(this).select()}).keyup(function(){$(this).css("font-style","normal")})})},init_grid:function(e){this.grid=new a(e);var d=this.grid.attributes;var c=this.grid.get("url_base");c=c.replace(/^.*\/\/[^\/]+/,"");this.grid.set("url_base",c);$("#grid-table-body").html(this.template_body(d));$("#grid-table-footer").html(this.template_footer(d));if(d.message){$("#grid-message").html(this.template_message(d));setTimeout(function(){$("#grid-message").html("")},5000)}this.init_grid_elements()},init_grid_controls:function(){$(".submit-image").each(function(){$(this).mousedown(function(){$(this).addClass("gray-background")});$(this).mouseup(function(){$(this).removeClass("gray-background")})});var c=this;$(".sort-link").each(function(){$(this).click(function(){c.set_sort_condition($(this).attr("sort_key"));return false})});$(".categorical-filter > a").each(function(){$(this).click(function(){c.set_categorical_filter($(this).attr("filter_key"),$(this).attr("filter_val"));return false})});$(".text-filter-form").each(function(){$(this).submit(function(){var g=$(this).attr("column_key");var f=$("#input-"+g+"-filter");var h=f.val();f.val("");c.add_filter_condition(g,h);return false})});var d=$("#input-tags-filter");if(d.length){d.autocomplete(this.grid.history_tag_autocomplete_url,{selectFirst:false,autoFill:false,highlight:false,mustMatch:false})}var e=$("#input-name-filter");if(e.length){e.autocomplete(this.grid.history_name_autocomplete_url,{selectFirst:false,autoFill:false,highlight:false,mustMatch:false})}$(".advanced-search-toggle").each(function(){$(this).click(function(){$("#standard-search").slideToggle("fast");$("#advanced-search").slideToggle("fast");return false})})},init_grid_elements:function(){$(".grid").each(function(){var r=$(this).find("input.grid-row-select-checkbox");var q=$(this).find("span.grid-selected-count");var s=function(){q.text($(r).filter(":checked").length)};$(r).each(function(){$(this).change(s)});s()});if($(".community_rating_star").length!==0){$(".community_rating_star").rating({})}var p=this.grid.attributes;var o=this;$(".page-link > a").each(function(){$(this).click(function(){o.set_page($(this).attr("page_num"));return false})});$(".use-inbound").each(function(){$(this).click(function(q){o.execute({href:$(this).attr("href"),inbound:true});return false})});$(".use-outbound").each(function(){$(this).click(function(q){o.execute({href:$(this).attr("href")});return false})});for(var h in p.items){var k=$("#grid-"+h+"-popup");k.off();var d=new PopupMenu(k);var n=p.items[h];for(var g in p.operations){var e=p.operations[g];var l=e.label;var c=n.operation_config[l];var f=n.encode_id;if(c.allowed&&e.allow_popup){var m={html:e.label,href:c.url_args,target:c.target,confirmation_text:e.confirm,inbound:e.inbound};m.func=function(s){s.preventDefault();var r=$(s.target).html();var q=this.findItemByHtml(r);o.execute(q)};d.addItem(m)}}}},add_filter_condition:function(e,g){if(g===""){return false}this.grid.add_filter(e,g,true);var f=$("<span>"+g+"<a href='javascript:void(0);'><span class='delete-search-icon' /></span></a>");f.addClass("text-filter-val");var d=this;f.click(function(){d.grid.remove_filter(e,g);$(this).remove();d.go_page_one();d.execute()});var c=$("#"+e+"-filtering-criteria");c.append(f);this.go_page_one();this.execute()},set_sort_condition:function(h){var g=this.grid.get("sort_key");var f=h;if(g.indexOf(h)!==-1){if(g.substring(0,1)!=="-"){f="-"+h}else{}}$(".sort-arrow").remove();var e=(f.substring(0,1)=="-")?"↑":"↓";var c=$("<span>"+e+"</span>").addClass("sort-arrow");var d=$("#"+h+"-header");d.append(c);this.grid.set("sort_key",f);this.go_page_one();this.execute()},set_categorical_filter:function(e,g){var d=this.grid.get("categorical_filters")[e],f=this.grid.get("filters")[e];var c=this;$("."+e+"-filter").each(function(){var m=$.trim($(this).text());var k=d[m];var l=k[e];if(l==g){$(this).empty();$(this).addClass("current-filter");$(this).append(m)}else{if(l==f){$(this).empty();var h=$("<a href='#'>"+m+"</a>");h.click(function(){c.set_categorical_filter(e,l)});$(this).removeClass("current-filter");$(this).append(h)}}});this.grid.add_filter(e,g);this.go_page_one();this.execute()},set_page:function(c){var d=this;$(".page-link").each(function(){var k=$(this).attr("id"),g=parseInt(k.split("-")[2],10),e=d.grid.get("cur_page"),h;if(g===c){h=$(this).children().text();$(this).empty();$(this).addClass("inactive-link");$(this).text(h)}else{if(g===e){h=$(this).text();$(this).empty();$(this).removeClass("inactive-link");var f=$("<a href='#'>"+h+"</a>");f.click(function(){d.set_page(g)});$(this).append(f)}}});if(c==="all"){this.grid.set("cur_page",c)}else{this.grid.set("cur_page",parseInt(c,10))}this.execute()},submit_operation:function(f,g){var e=$('input[name="id"]:checked').length;if(!e>0){return false}var d=$(f).val();var c=[];$("input[name=id]:checked").each(function(){c.push($(this).val())});this.execute({operation:d,id:c,confirmation_text:g});return true},execute:function(n){var f=null;var e=null;var g=null;var c=null;var m=null;if(n){e=n.href;g=n.operation;f=n.id;c=n.confirmation_text;m=n.inbound;if(e!==undefined&&e.indexOf("operation=")!=-1){var l=e.split("?");if(l.length>1){var k=l[1];var d=k.split("&");for(var h=0;h<d.length;h++){if(d[h].indexOf("operation")!=-1){g=d[h].split("=")[1];g=g.replace(/\+/g," ")}else{if(d[h].indexOf("id")!=-1){f=d[h].split("=")[1]}}}}}}if(g&&f){if(c&&c!=""&&c!="None"&&c!="null"){if(!confirm(c)){return false}}g=g.toLowerCase();this.grid.set({operation:g,item_ids:f});if(this.grid.can_async_op(g)){this.update_grid()}else{this.go_to(m,"")}return false}if(e){this.go_to(m,e);return false}if(this.grid.get("async")){this.update_grid()}else{this.go_to(m,"")}return false},go_to:function(f,d){var e=this.grid.get("async");this.grid.set("async",false);advanced_search=$("#advanced-search").is(":visible");this.grid.set("advanced_search",advanced_search);if(!d){d=this.grid.get("url_base")+"?"+$.param(this.grid.get_url_data())}this.grid.set({operation:undefined,item_ids:undefined,async:e});if(f){var c=$(".grid-header").closest(".inbound");if(c.length!==0){c.load(d);return}}window.location=d},update_grid:function(){var d=(this.grid.get("operation")?"POST":"GET");$(".loading-elt-overlay").show();var c=this;$.ajax({type:d,url:c.grid.get("url_base"),data:c.grid.get_url_data(),error:function(e){alert("Grid refresh failed")},success:function(e){c.init_grid($.parseJSON(e));$("#grid-table-body").trigger("update");$(".loading-elt-overlay").hide()},complete:function(){c.grid.set({operation:undefined,item_ids:undefined})}})},check_all_items:function(){var c=document.getElementById("check_all"),d=document.getElementsByTagName("input"),f=0,e;if(c.checked===true){for(e=0;e<d.length;e++){if(d[e].name.indexOf("id")!==-1){d[e].checked=true;f++}}}else{for(e=0;e<d.length;e++){if(d[e].name.indexOf("id")!==-1){d[e].checked=false}}}this.init_grid_elements()},go_page_one:function(){var c=this.grid.get("cur_page");if(c!==null&&c!==undefined&&c!=="all"){this.grid.set("cur_page",1)}},template_body:function(s){var l="";var t=0;var g=s.items.length;if(g==0){l+='<tr><td colspan="100"><em>No Items</em></td></tr>';t=1}for(i in s.items){var q=s.items[i];var c=q.encode_id;var h="grid-"+i+"-popup";l+="<tr ";if(s.current_item_id==q.id){l+='class="current"'}l+=">";if(s.show_item_checkboxes){l+='<td style="width: 1.5em;"><input type="checkbox" name="id" value="'+c+'" id="'+c+'" class="grid-row-select-checkbox" /></td>'}for(j in s.columns){var f=s.columns[j];if(f.visible){var e="";if(f.nowrap){e='style="white-space:nowrap;"'}var r=q.column_config[f.label];var k=r.link;var m=r.value;var p=r.inbound;if(jQuery.type(m)==="string"){m=m.replace(/\/\//g,"/")}var d="";var o="";if(f.attach_popup){d="grid-"+i+"-popup";o="menubutton";if(k!=""){o+=" split"}o+=" popup"}l+="<td "+e+">";if(k){if(s.operations.length!=0){l+='<div id="'+d+'" class="'+o+'" style="float: left;">'}var n="";if(p){n="use-inbound"}else{n="use-outbound"}l+='<a class="label '+n+'" href="'+k+'" onclick="return false;">'+m+"</a>";if(s.operations.length!=0){l+="</div>"}}else{l+='<div id="'+d+'" class="'+o+'"><label id="'+f.label_id_prefix+c+'" for="'+c+'">'+m+"</label></div>"}l+="</td>"}}l+="</tr>";t++}return l},template_footer:function(q){var m="";if(q.use_paging&&q.num_pages>1){var o=q.num_page_links;var c=q.cur_page_num;var p=q.num_pages;var l=o/2;var k=c-l;var g=0;if(k==0){k=1;g=l-(c-k)}var f=l+g;var e=c+f;if(e<=p){max_offset=0}else{e=p;max_offset=f-(e+1-c)}if(max_offset!=0){k-=max_offset;if(k<1){k=1}}m+='<tr id="page-links-row">';if(q.show_item_checkboxes){m+="<td></td>"}m+='<td colspan="100"><span id="page-link-container">Page:';if(k>1){m+='<span class="page-link" id="page-link-1"><a href="'+this.grid.get_url({page:n})+'" page_num="1" onclick="return false;">1</a></span> ...'}for(var n=k;n<e+1;n++){if(n==q.cur_page_num){m+='<span class="page-link inactive-link" id="page-link-'+n+'">'+n+"</span>"}else{m+='<span class="page-link" id="page-link-'+n+'"><a href="'+this.grid.get_url({page:n})+'" onclick="return false;" page_num="'+n+'">'+n+"</a></span>"}}if(e<p){m+='...<span class="page-link" id="page-link-'+p+'"><a href="'+this.grid.get_url({page:p})+'" onclick="return false;" page_num="'+p+'">'+p+"</a></span>"}m+="</span>";m+='<span class="page-link" id="show-all-link-span"> | <a href="'+this.grid.get_url({page:"all"})+'" onclick="return false;" page_num="all">Show All</a></span></td></tr>'}if(q.show_item_checkboxes){m+='<tr><input type="hidden" id="operation" name="operation" value=""><td></td><td colspan="100">For <span class="grid-selected-count"></span> selected '+q.get_class_plural+": ";for(i in q.operations){var d=q.operations[i];if(d.allow_multiple){m+='<input type="button" value="'+d.label+'" class="action-button" onclick="gridView.submit_operation(this, \''+d.confirm+"')\"> "}}m+="</td></tr>"}var h=false;for(i in q.operations){if(q.operations[i].global_operation){h=true;break}}if(h){m+='<tr><td colspan="100">';for(i in q.operations){var d=q.operations[i];if(d.global_operation){m+='<a class="action-button" href="'+d.global_operation+'">'+d.label+"</a>"}}m+="</td></tr>"}if(q.legend){m+='<tr><td colspan="100">'+q.legend+"</td></tr>"}return m},template_message:function(c){return'<p><div class="'+c.status+'message transient-message">'+c.message+'</div><div style="clear: both"></div></p>'}});return{Grid:a,GridView:b}});
\ No newline at end of file
+jQuery.ajaxSettings.traditional=true;define(["mvc/ui"],function(){var a=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},can_async_op:function(c){return _.indexOf(this.attributes.async_ops,c)!==-1},add_filter:function(g,h,d){if(d){var e=this.attributes.filters[g],c;if(e===null||e===undefined){c=h}else{if(typeof(e)=="string"){if(e=="All"){c=h}else{var f=[];f[0]=e;f[1]=h;c=f}}else{c=e;c.push(h)}}this.attributes.filters[g]=c}else{this.attributes.filters[g]=h}},remove_filter:function(d,g){var c=this.attributes.filters[d];if(c===null||c===undefined){return false}var f=true;if(typeof(c)==="string"){if(c=="All"){f=false}else{delete this.attributes.filters[d]}}else{var e=_.indexOf(c,g);if(e!==-1){c.splice(e,1)}else{f=false}}return f},get_url_data:function(){var c={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};if(this.attributes.operation){c.operation=this.attributes.operation}if(this.attributes.item_ids){c.id=this.attributes.item_ids}var d=this;_.each(_.pairs(d.attributes.filters),function(e){c["f-"+e[0]]=e[1]});return c},get_url:function(c){return this.get("url_base")+"?"+$.param(this.get_url_data())+"&"+$.param(c)}});var b=Backbone.View.extend({grid:null,initialize:function(c){this.init_grid(c);this.init_grid_controls();$("input[type=text]").each(function(){$(this).click(function(){$(this).select()}).keyup(function(){$(this).css("font-style","normal")})})},init_grid:function(e){this.grid=new a(e);var d=this.grid.attributes;var c=this.grid.get("url_base");c=c.replace(/^.*\/\/[^\/]+/,"");this.grid.set("url_base",c);$("#grid-table-body").html(this.template_body(d));$("#grid-table-footer").html(this.template_footer(d));if(d.message){$("#grid-message").html(this.template_message(d));setTimeout(function(){$("#grid-message").html("")},5000)}this.init_grid_elements()},init_grid_controls:function(){$(".submit-image").each(function(){$(this).mousedown(function(){$(this).addClass("gray-background")});$(this).mouseup(function(){$(this).removeClass("gray-background")})});var c=this;$(".sort-link").each(function(){$(this).click(function(){c.set_sort_condition($(this).attr("sort_key"));return false})});$(".categorical-filter > a").each(function(){$(this).click(function(){c.set_categorical_filter($(this).attr("filter_key"),$(this).attr("filter_val"));return false})});$(".text-filter-form").each(function(){$(this).submit(function(){var g=$(this).attr("column_key");var f=$("#input-"+g+"-filter");var h=f.val();f.val("");c.add_filter_condition(g,h);return false})});var d=$("#input-tags-filter");if(d.length){d.autocomplete(this.grid.history_tag_autocomplete_url,{selectFirst:false,autoFill:false,highlight:false,mustMatch:false})}var e=$("#input-name-filter");if(e.length){e.autocomplete(this.grid.history_name_autocomplete_url,{selectFirst:false,autoFill:false,highlight:false,mustMatch:false})}$(".advanced-search-toggle").each(function(){$(this).click(function(){$("#standard-search").slideToggle("fast");$("#advanced-search").slideToggle("fast");return false})})},init_grid_elements:function(){$(".grid").each(function(){var r=$(this).find("input.grid-row-select-checkbox");var q=$(this).find("span.grid-selected-count");var s=function(){q.text($(r).filter(":checked").length)};$(r).each(function(){$(this).change(s)});s()});if($(".community_rating_star").length!==0){$(".community_rating_star").rating({})}var p=this.grid.attributes;var o=this;$(".page-link > a").each(function(){$(this).click(function(){o.set_page($(this).attr("page_num"));return false})});$(".use-inbound").each(function(){$(this).click(function(q){o.execute({href:$(this).attr("href"),inbound:true});return false})});$(".use-outbound").each(function(){$(this).click(function(q){o.execute({href:$(this).attr("href")});return false})});for(var h in p.items){var k=$("#grid-"+h+"-popup");k.off();var d=new PopupMenu(k);var n=p.items[h];for(var g in p.operations){var e=p.operations[g];var l=e.label;var c=n.operation_config[l];var f=n.encode_id;if(c.allowed&&e.allow_popup){var m={html:e.label,href:c.url_args,target:c.target,confirmation_text:e.confirm,inbound:e.inbound};m.func=function(s){s.preventDefault();var r=$(s.target).html();var q=this.findItemByHtml(r);o.execute(q)};d.addItem(m)}}}},add_filter_condition:function(e,g){if(g===""){return false}this.grid.add_filter(e,g,true);var f=$("<span>"+g+"<a href='javascript:void(0);'><span class='delete-search-icon' /></span></a>");f.addClass("text-filter-val");var d=this;f.click(function(){d.grid.remove_filter(e,g);$(this).remove();d.go_page_one();d.execute()});var c=$("#"+e+"-filtering-criteria");c.append(f);this.go_page_one();this.execute()},set_sort_condition:function(h){var g=this.grid.get("sort_key");var f=h;if(g.indexOf(h)!==-1){if(g.substring(0,1)!=="-"){f="-"+h}else{}}$(".sort-arrow").remove();var e=(f.substring(0,1)=="-")?"↑":"↓";var c=$("<span>"+e+"</span>").addClass("sort-arrow");var d=$("#"+h+"-header");d.append(c);this.grid.set("sort_key",f);this.go_page_one();this.execute()},set_categorical_filter:function(e,g){var d=this.grid.get("categorical_filters")[e],f=this.grid.get("filters")[e];var c=this;$("."+e+"-filter").each(function(){var m=$.trim($(this).text());var k=d[m];var l=k[e];if(l==g){$(this).empty();$(this).addClass("current-filter");$(this).append(m)}else{if(l==f){$(this).empty();var h=$("<a href='#'>"+m+"</a>");h.click(function(){c.set_categorical_filter(e,l)});$(this).removeClass("current-filter");$(this).append(h)}}});this.grid.add_filter(e,g);this.go_page_one();this.execute()},set_page:function(c){var d=this;$(".page-link").each(function(){var k=$(this).attr("id"),g=parseInt(k.split("-")[2],10),e=d.grid.get("cur_page"),h;if(g===c){h=$(this).children().text();$(this).empty();$(this).addClass("inactive-link");$(this).text(h)}else{if(g===e){h=$(this).text();$(this).empty();$(this).removeClass("inactive-link");var f=$("<a href='#'>"+h+"</a>");f.click(function(){d.set_page(g)});$(this).append(f)}}});if(c==="all"){this.grid.set("cur_page",c)}else{this.grid.set("cur_page",parseInt(c,10))}this.execute()},submit_operation:function(f,g){var e=$('input[name="id"]:checked').length;if(!e>0){return false}var d=$(f).val();var c=[];$("input[name=id]:checked").each(function(){c.push($(this).val())});this.execute({operation:d,id:c,confirmation_text:g});return true},execute:function(n){var f=null;var e=null;var g=null;var c=null;var m=null;if(n){e=n.href;g=n.operation;f=n.id;c=n.confirmation_text;m=n.inbound;if(e!==undefined&&e.indexOf("operation=")!=-1){var l=e.split("?");if(l.length>1){var k=l[1];var d=k.split("&");for(var h=0;h<d.length;h++){if(d[h].indexOf("operation")!=-1){g=d[h].split("=")[1];g=g.replace(/\+/g," ")}else{if(d[h].indexOf("id")!=-1){f=d[h].split("=")[1]}}}}}}if(g&&f){if(c&&c!=""&&c!="None"&&c!="null"){if(!confirm(c)){return false}}g=g.toLowerCase();this.grid.set({operation:g,item_ids:f});if(this.grid.can_async_op(g)){this.update_grid()}else{this.go_to(m,"")}return false}if(e){this.go_to(m,e);return false}if(this.grid.get("async")){this.update_grid()}else{this.go_to(m,"")}return false},go_to:function(f,d){var e=this.grid.get("async");this.grid.set("async",false);advanced_search=$("#advanced-search").is(":visible");this.grid.set("advanced_search",advanced_search);if(!d){d=this.grid.get("url_base")+"?"+$.param(this.grid.get_url_data())}this.grid.set({operation:undefined,item_ids:undefined,async:e});if(f){var c=$(".grid-header").closest(".inbound");if(c.length!==0){c.load(d);return}}window.location=d},update_grid:function(){var d=(this.grid.get("operation")?"POST":"GET");$(".loading-elt-overlay").show();var c=this;$.ajax({type:d,url:c.grid.get("url_base"),data:c.grid.get_url_data(),error:function(e){alert("Grid refresh failed")},success:function(e){c.init_grid($.parseJSON(e));$(".loading-elt-overlay").hide()},complete:function(){c.grid.set({operation:undefined,item_ids:undefined})}})},check_all_items:function(){var c=document.getElementById("check_all"),d=document.getElementsByTagName("input"),f=0,e;if(c.checked===true){for(e=0;e<d.length;e++){if(d[e].name.indexOf("id")!==-1){d[e].checked=true;f++}}}else{for(e=0;e<d.length;e++){if(d[e].name.indexOf("id")!==-1){d[e].checked=false}}}this.init_grid_elements()},go_page_one:function(){var c=this.grid.get("cur_page");if(c!==null&&c!==undefined&&c!=="all"){this.grid.set("cur_page",1)}},template_body:function(s){var l="";var t=0;var g=s.items.length;if(g==0){l+='<tr><td colspan="100"><em>No Items</em></td></tr>';t=1}for(i in s.items){var q=s.items[i];var c=q.encode_id;var h="grid-"+i+"-popup";l+="<tr ";if(s.current_item_id==q.id){l+='class="current"'}l+=">";if(s.show_item_checkboxes){l+='<td style="width: 1.5em;"><input type="checkbox" name="id" value="'+c+'" id="'+c+'" class="grid-row-select-checkbox" /></td>'}for(j in s.columns){var f=s.columns[j];if(f.visible){var e="";if(f.nowrap){e='style="white-space:nowrap;"'}var r=q.column_config[f.label];var k=r.link;var m=r.value;var p=r.inbound;if(jQuery.type(m)==="string"){m=m.replace(/\/\//g,"/")}var d="";var o="";if(f.attach_popup){d="grid-"+i+"-popup";o="menubutton";if(k!=""){o+=" split"}o+=" popup"}l+="<td "+e+">";if(k){if(s.operations.length!=0){l+='<div id="'+d+'" class="'+o+'" style="float: left;">'}var n="";if(p){n="use-inbound"}else{n="use-outbound"}l+='<a class="label '+n+'" href="'+k+'" onclick="return false;">'+m+"</a>";if(s.operations.length!=0){l+="</div>"}}else{l+='<div id="'+d+'" class="'+o+'"><label id="'+f.label_id_prefix+c+'" for="'+c+'">'+m+"</label></div>"}l+="</td>"}}l+="</tr>";t++}return l},template_footer:function(q){var m="";if(q.use_paging&&q.num_pages>1){var o=q.num_page_links;var c=q.cur_page_num;var p=q.num_pages;var l=o/2;var k=c-l;var g=0;if(k==0){k=1;g=l-(c-k)}var f=l+g;var e=c+f;if(e<=p){max_offset=0}else{e=p;max_offset=f-(e+1-c)}if(max_offset!=0){k-=max_offset;if(k<1){k=1}}m+='<tr id="page-links-row">';if(q.show_item_checkboxes){m+="<td></td>"}m+='<td colspan="100"><span id="page-link-container">Page:';if(k>1){m+='<span class="page-link" id="page-link-1"><a href="'+this.grid.get_url({page:n})+'" page_num="1" onclick="return false;">1</a></span> ...'}for(var n=k;n<e+1;n++){if(n==q.cur_page_num){m+='<span class="page-link inactive-link" id="page-link-'+n+'">'+n+"</span>"}else{m+='<span class="page-link" id="page-link-'+n+'"><a href="'+this.grid.get_url({page:n})+'" onclick="return false;" page_num="'+n+'">'+n+"</a></span>"}}if(e<p){m+='...<span class="page-link" id="page-link-'+p+'"><a href="'+this.grid.get_url({page:p})+'" onclick="return false;" page_num="'+p+'">'+p+"</a></span>"}m+="</span>";m+='<span class="page-link" id="show-all-link-span"> | <a href="'+this.grid.get_url({page:"all"})+'" onclick="return false;" page_num="all">Show All</a></span></td></tr>'}if(q.show_item_checkboxes){m+='<tr><input type="hidden" id="operation" name="operation" value=""><td></td><td colspan="100">For <span class="grid-selected-count"></span> selected '+q.get_class_plural+": ";for(i in q.operations){var d=q.operations[i];if(d.allow_multiple){m+='<input type="button" value="'+d.label+'" class="action-button" onclick="gridView.submit_operation(this, \''+d.confirm+"')\"> "}}m+="</td></tr>"}var h=false;for(i in q.operations){if(q.operations[i].global_operation){h=true;break}}if(h){m+='<tr><td colspan="100">';for(i in q.operations){var d=q.operations[i];if(d.global_operation){m+='<a class="action-button" href="'+d.global_operation+'">'+d.label+"</a>"}}m+="</td></tr>"}if(q.legend){m+='<tr><td colspan="100">'+q.legend+"</td></tr>"}return m},template_message:function(c){return'<p><div class="'+c.status+'message transient-message">'+c.message+'</div><div style="clear: both"></div></p>'}});return{Grid:a,GridView:b}});
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: carlfeberhard: History panel: bring error state HDA button order into line with other states
by commits-noreply@bitbucket.org 19 Dec '13
by commits-noreply@bitbucket.org 19 Dec '13
19 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/c91f3185e8ca/
Changeset: c91f3185e8ca
User: carlfeberhard
Date: 2013-12-19 17:47:11
Summary: History panel: bring error state HDA button order into line with other states
Affected #: 2 files
diff -r 4fc04317cef36f002e071d391523450a7a956232 -r c91f3185e8ca68a006b755a9fdcbed3174b27fd9 static/scripts/mvc/dataset/hda-base.js
--- a/static/scripts/mvc/dataset/hda-base.js
+++ b/static/scripts/mvc/dataset/hda-base.js
@@ -354,7 +354,7 @@
html = '<div>' + this.model.get( 'misc_blurb' ) + '</div>' + html;
}
return this._render_stateBodyHelper( html,
- this.defaultPrimaryActionButtonRenderers.concat([ this._render_downloadButton ])
+ [ this._render_downloadButton ].concat( this.defaultPrimaryActionButtonRenderers )
);
},
diff -r 4fc04317cef36f002e071d391523450a7a956232 -r c91f3185e8ca68a006b755a9fdcbed3174b27fd9 static/scripts/packed/mvc/dataset/hda-base.js
--- a/static/scripts/packed/mvc/dataset/hda-base.js
+++ b/static/scripts/packed/mvc/dataset/hda-base.js
@@ -1,1 +1,1 @@
-define(["mvc/dataset/hda-model"],function(b){var a=Backbone.View.extend(LoggableMixin).extend({tagName:"div",className:"dataset hda history-panel-hda",id:function(){return"hda-"+this.model.get("id")},fxSpeed:"fast",initialize:function(c){if(c.logger){this.logger=this.model.logger=c.logger}this.log(this+".initialize:",c);this.defaultPrimaryActionButtonRenderers=[this._render_showParamsButton];this.linkTarget=c.linkTarget||"_blank";this.selectable=c.selectable||false;this.selected=c.selected||false;this.expanded=c.expanded||false;this.draggable=c.draggable||false;this._setUpListeners()},_setUpListeners:function(){this.model.on("change",function(d,c){if(this.model.changedAttributes().state&&this.model.inReadyState()&&this.expanded&&!this.model.hasDetails()){this.model.fetch()}else{this.render()}},this)},render:function(e){e=(e===undefined)?(true):(e);var c=this;this.$el.find("[title]").tooltip("destroy");this.urls=this.model.urls();var d=$(a.templates.skeleton(this.model.toJSON()));d.find(".dataset-primary-actions").append(this._render_titleButtons());d.children(".dataset-body").replaceWith(this._render_body());this._setUpBehaviors(d);if(e){$(c).queue(function(f){this.$el.fadeOut(c.fxSpeed,f)})}$(c).queue(function(f){this.$el.empty().attr("class",c.className).addClass("state-"+c.model.get("state")).append(d.children());if(this.selectable){this.showSelector(0)}f()});if(e){$(c).queue(function(f){this.$el.fadeIn(c.fxSpeed,f)})}$(c).queue(function(f){this.trigger("rendered",c);if(this.model.inReadyState()){this.trigger("rendered:ready",c)}if(this.draggable){this.draggableOn()}f()});return this},_setUpBehaviors:function(c){c=c||this.$el;make_popup_menus(c);c.find("[title]").tooltip({placement:"bottom"})},_render_titleButtons:function(){return[this._render_displayButton()]},_render_displayButton:function(){if((this.model.get("state")===b.HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(this.model.get("state")===b.HistoryDatasetAssociation.STATES.DISCARDED)||(this.model.get("state")===b.HistoryDatasetAssociation.STATES.NEW)||(!this.model.get("accessible"))){return null}var d={target:this.linkTarget,classes:"dataset-display"};if(this.model.get("purged")){d.disabled=true;d.title=_l("Cannot display datasets removed from disk")}else{if(this.model.get("state")===b.HistoryDatasetAssociation.STATES.UPLOAD){d.disabled=true;d.title=_l("This dataset must finish uploading before it can be viewed")}else{d.title=_l("View data");d.href=this.urls.display;var c=this;d.onclick=function(){if(Galaxy.frame&&Galaxy.frame.active){Galaxy.frame.add({title:"Data Viewer: "+c.model.get("name"),type:"url",content:c.urls.display})}}}}d.faIcon="fa-eye";return faIconButton(d)},_render_downloadButton:function(){if(this.model.get("purged")||!this.model.hasData()){return null}var d=this.urls,e=this.model.get("meta_files");if(_.isEmpty(e)){return $(['<a href="'+d.download+'" title="'+_l("Download")+'" class="icon-btn">','<span class="fa fa-floppy-o"></span>',"</a>"].join(""))}var f="dataset-"+this.model.get("id")+"-popup",c=['<div popupmenu="'+f+'">','<a href="'+d.download+'">',_l("Download Dataset"),"</a>","<a>"+_l("Additional Files")+"</a>",_.map(e,function(g){return['<a class="action-button" href="',d.meta_download+g.file_type,'">',_l("Download")," ",g.file_type,"</a>"].join("")}).join("\n"),"</div>",'<div class="icon-btn-group">','<a href="'+d.download+'" title="'+_l("Download")+'" class="icon-btn">','<span class="fa fa-floppy-o"></span>','</a><a class="icon-btn popup" id="'+f+'">','<span class="fa fa-caret-down"></span>',"</a>","</div>"].join("\n");return $(c)},_render_showParamsButton:function(){return faIconButton({title:_l("View details"),href:this.urls.show_params,target:this.linkTarget,faIcon:"fa-info-circle"})},_render_body:function(){var d=$('<div>Error: unknown dataset state "'+this.model.get("state")+'".</div>'),c=this["_render_body_"+this.model.get("state")];if(_.isFunction(c)){d=c.call(this)}this._setUpBehaviors(d);if(this.expanded){d.show()}return d},_render_stateBodyHelper:function(c,f){f=f||[];var d=this,e=$(a.templates.body(_.extend(this.model.toJSON(),{body:c})));e.find(".dataset-actions .left").append(_.map(f,function(g){return g.call(d)}));return e},_render_body_new:function(){return this._render_stateBodyHelper("<div>"+_l("This is a new dataset and not all of its data are available yet")+"</div>")},_render_body_noPermission:function(){return this._render_stateBodyHelper("<div>"+_l("You do not have permission to view this dataset")+"</div>")},_render_body_discarded:function(){return this._render_stateBodyHelper("<div>"+_l("The job creating this dataset was cancelled before completion")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_queued:function(){return this._render_stateBodyHelper("<div>"+_l("This job is waiting to run")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_upload:function(){return this._render_stateBodyHelper("<div>"+_l("This dataset is currently uploading")+"</div>")},_render_body_setting_metadata:function(){return this._render_stateBodyHelper("<div>"+_l("Metadata is being auto-detected")+"</div>")},_render_body_running:function(){return this._render_stateBodyHelper("<div>"+_l("This job is currently running")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_paused:function(){return this._render_stateBodyHelper("<div>"+_l('This job is paused. Use the "Resume Paused Jobs" in the history menu to resume')+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_error:function(){var c=['<span class="help-text">',_l("An error occurred with this dataset"),":</span>",'<div class="job-error-text">',$.trim(this.model.get("misc_info")),"</div>"].join("");if(!this.model.get("purged")){c="<div>"+this.model.get("misc_blurb")+"</div>"+c}return this._render_stateBodyHelper(c,this.defaultPrimaryActionButtonRenderers.concat([this._render_downloadButton]))},_render_body_empty:function(){return this._render_stateBodyHelper("<div>"+_l("No data")+": <i>"+this.model.get("misc_blurb")+"</i></div>",this.defaultPrimaryActionButtonRenderers)},_render_body_failed_metadata:function(){var c=$('<div class="warningmessagesmall"></div>').append($("<strong/>").text(_l("An error occurred setting the metadata for this dataset"))),d=this._render_body_ok();d.prepend(c);return d},_render_body_ok:function(){var c=this,e=$(a.templates.body(this.model.toJSON())),d=[this._render_downloadButton].concat(this.defaultPrimaryActionButtonRenderers);e.find(".dataset-actions .left").append(_.map(d,function(f){return f.call(c)}));if(this.model.isDeletedOrPurged()){return e}return e},events:{"click .dataset-title-bar":"toggleBodyVisibility","keydown .dataset-title-bar":"toggleBodyVisibility","click .dataset-selector":"toggleSelect",},toggleBodyVisibility:function(f,d){var c=32,e=13;if(f&&(f.type==="keydown")&&!(f.keyCode===c||f.keyCode===e)){return true}var g=this.$el.find(".dataset-body");d=(d===undefined)?(!g.is(":visible")):(d);if(d){this.expandBody()}else{this.collapseBody()}return false},expandBody:function(){var c=this;function d(){c.$el.children(".dataset-body").replaceWith(c._render_body());c.$el.children(".dataset-body").slideDown(c.fxSpeed,function(){c.expanded=true;c.trigger("body-expanded",c.model.get("id"))})}if(this.model.inReadyState()&&!this.model.hasDetails()){this.model.fetch({silent:true}).always(function(e){d()})}else{d()}},collapseBody:function(){var c=this;this.$el.children(".dataset-body").slideUp(c.fxSpeed,function(){c.expanded=false;c.trigger("body-collapsed",c.model.get("id"))})},showSelector:function(e){if(this.$el.find(".dataset-selector").css("display")!=="none"){return}e=(e!==undefined)?(e):(this.fxSpeed);if(this.selected){this.select(null,true)}var d=this,c=32;if(e){this.$el.queue("fx",function(f){$(this).find(".dataset-primary-actions").fadeOut(e,f)});this.$el.queue("fx",function(f){$(this).find(".dataset-selector").show().animate({width:c},e,f);$(this).find(".dataset-title-bar").animate({"margin-left":c},e,f);d.selectable=true;d.trigger("selectable",true,d)})}else{this.$el.find(".dataset-primary-actions").hide();this.$el.find(".dataset-selector").show().css({width:c});this.$el.find(".dataset-title-bar").show().css({"margin-left":c});d.selectable=true;d.trigger("selectable",true,d)}},hideSelector:function(c){c=(c!==undefined)?(c):(this.fxSpeed);this.selectable=false;this.trigger("selectable",false,this);if(c){this.$el.queue("fx",function(d){$(this).find(".dataset-title-bar").show().css({"margin-left":"0"});$(this).find(".dataset-selector").animate({width:"0px"},c,function(){$(this).hide();d()})});this.$el.queue("fx",function(d){$(this).find(".dataset-primary-actions").fadeIn(c,d)})}else{$(this).find(".dataset-selector").css({width:"0px"}).hide();$(this).find(".dataset-primary-actions").show()}},toggleSelector:function(c){if(!this.$el.find(".dataset-selector").is(":visible")){this.showSelector(c)}else{this.hideSelector(c)}},select:function(c){this.$el.find(".dataset-selector span").removeClass("fa-square-o").addClass("fa-check-square-o");if(!this.selected){this.trigger("selected",this);this.selected=true}return false},deselect:function(c){this.$el.find(".dataset-selector span").removeClass("fa-check-square-o").addClass("fa-square-o");if(this.selected){this.trigger("de-selected",this);this.selected=false}return false},toggleSelect:function(c){if(this.selected){this.deselect(c)}else{this.select(c)}},draggableOn:function(){this.draggable=true;this.dragStartHandler=_.bind(this._dragStartHandler,this);this.dragEndHandler=_.bind(this._dragEndHandler,this);var c=this.$el.find(".dataset-title-bar").attr("draggable",true).get(0);c.addEventListener("dragstart",this.dragStartHandler,false);c.addEventListener("dragend",this.dragEndHandler,false)},draggableOff:function(){this.draggable=false;var c=this.$el.find(".dataset-title-bar").attr("draggable",false).get(0);c.removeEventListener("dragstart",this.dragStartHandler,false);c.removeEventListener("dragend",this.dragEndHandler,false)},toggleDraggable:function(){if(this.draggable){this.draggableOff()}else{this.draggableOn()}},_dragStartHandler:function(c){this.trigger("dragstart",this);c.dataTransfer.effectAllowed="move";c.dataTransfer.setData("text",JSON.stringify(this.model.toJSON()));return false},_dragEndHandler:function(c){this.trigger("dragend",this);return false},remove:function(d){var c=this;this.$el.fadeOut(c.fxSpeed,function(){c.$el.remove();c.off();if(d){d()}})},toString:function(){var c=(this.model)?(this.model+""):("(no model)");return"HDABaseView("+c+")"}});a.templates={skeleton:Handlebars.templates["template-hda-skeleton"],body:Handlebars.templates["template-hda-body"]};return{HDABaseView:a}});
\ No newline at end of file
+define(["mvc/dataset/hda-model"],function(b){var a=Backbone.View.extend(LoggableMixin).extend({tagName:"div",className:"dataset hda history-panel-hda",id:function(){return"hda-"+this.model.get("id")},fxSpeed:"fast",initialize:function(c){if(c.logger){this.logger=this.model.logger=c.logger}this.log(this+".initialize:",c);this.defaultPrimaryActionButtonRenderers=[this._render_showParamsButton];this.linkTarget=c.linkTarget||"_blank";this.selectable=c.selectable||false;this.selected=c.selected||false;this.expanded=c.expanded||false;this.draggable=c.draggable||false;this._setUpListeners()},_setUpListeners:function(){this.model.on("change",function(d,c){if(this.model.changedAttributes().state&&this.model.inReadyState()&&this.expanded&&!this.model.hasDetails()){this.model.fetch()}else{this.render()}},this)},render:function(e){e=(e===undefined)?(true):(e);var c=this;this.$el.find("[title]").tooltip("destroy");this.urls=this.model.urls();var d=$(a.templates.skeleton(this.model.toJSON()));d.find(".dataset-primary-actions").append(this._render_titleButtons());d.children(".dataset-body").replaceWith(this._render_body());this._setUpBehaviors(d);if(e){$(c).queue(function(f){this.$el.fadeOut(c.fxSpeed,f)})}$(c).queue(function(f){this.$el.empty().attr("class",c.className).addClass("state-"+c.model.get("state")).append(d.children());if(this.selectable){this.showSelector(0)}f()});if(e){$(c).queue(function(f){this.$el.fadeIn(c.fxSpeed,f)})}$(c).queue(function(f){this.trigger("rendered",c);if(this.model.inReadyState()){this.trigger("rendered:ready",c)}if(this.draggable){this.draggableOn()}f()});return this},_setUpBehaviors:function(c){c=c||this.$el;make_popup_menus(c);c.find("[title]").tooltip({placement:"bottom"})},_render_titleButtons:function(){return[this._render_displayButton()]},_render_displayButton:function(){if((this.model.get("state")===b.HistoryDatasetAssociation.STATES.NOT_VIEWABLE)||(this.model.get("state")===b.HistoryDatasetAssociation.STATES.DISCARDED)||(this.model.get("state")===b.HistoryDatasetAssociation.STATES.NEW)||(!this.model.get("accessible"))){return null}var d={target:this.linkTarget,classes:"dataset-display"};if(this.model.get("purged")){d.disabled=true;d.title=_l("Cannot display datasets removed from disk")}else{if(this.model.get("state")===b.HistoryDatasetAssociation.STATES.UPLOAD){d.disabled=true;d.title=_l("This dataset must finish uploading before it can be viewed")}else{d.title=_l("View data");d.href=this.urls.display;var c=this;d.onclick=function(){if(Galaxy.frame&&Galaxy.frame.active){Galaxy.frame.add({title:"Data Viewer: "+c.model.get("name"),type:"url",content:c.urls.display})}}}}d.faIcon="fa-eye";return faIconButton(d)},_render_downloadButton:function(){if(this.model.get("purged")||!this.model.hasData()){return null}var d=this.urls,e=this.model.get("meta_files");if(_.isEmpty(e)){return $(['<a href="'+d.download+'" title="'+_l("Download")+'" class="icon-btn">','<span class="fa fa-floppy-o"></span>',"</a>"].join(""))}var f="dataset-"+this.model.get("id")+"-popup",c=['<div popupmenu="'+f+'">','<a href="'+d.download+'">',_l("Download Dataset"),"</a>","<a>"+_l("Additional Files")+"</a>",_.map(e,function(g){return['<a class="action-button" href="',d.meta_download+g.file_type,'">',_l("Download")," ",g.file_type,"</a>"].join("")}).join("\n"),"</div>",'<div class="icon-btn-group">','<a href="'+d.download+'" title="'+_l("Download")+'" class="icon-btn">','<span class="fa fa-floppy-o"></span>','</a><a class="icon-btn popup" id="'+f+'">','<span class="fa fa-caret-down"></span>',"</a>","</div>"].join("\n");return $(c)},_render_showParamsButton:function(){return faIconButton({title:_l("View details"),href:this.urls.show_params,target:this.linkTarget,faIcon:"fa-info-circle"})},_render_body:function(){var d=$('<div>Error: unknown dataset state "'+this.model.get("state")+'".</div>'),c=this["_render_body_"+this.model.get("state")];if(_.isFunction(c)){d=c.call(this)}this._setUpBehaviors(d);if(this.expanded){d.show()}return d},_render_stateBodyHelper:function(c,f){f=f||[];var d=this,e=$(a.templates.body(_.extend(this.model.toJSON(),{body:c})));e.find(".dataset-actions .left").append(_.map(f,function(g){return g.call(d)}));return e},_render_body_new:function(){return this._render_stateBodyHelper("<div>"+_l("This is a new dataset and not all of its data are available yet")+"</div>")},_render_body_noPermission:function(){return this._render_stateBodyHelper("<div>"+_l("You do not have permission to view this dataset")+"</div>")},_render_body_discarded:function(){return this._render_stateBodyHelper("<div>"+_l("The job creating this dataset was cancelled before completion")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_queued:function(){return this._render_stateBodyHelper("<div>"+_l("This job is waiting to run")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_upload:function(){return this._render_stateBodyHelper("<div>"+_l("This dataset is currently uploading")+"</div>")},_render_body_setting_metadata:function(){return this._render_stateBodyHelper("<div>"+_l("Metadata is being auto-detected")+"</div>")},_render_body_running:function(){return this._render_stateBodyHelper("<div>"+_l("This job is currently running")+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_paused:function(){return this._render_stateBodyHelper("<div>"+_l('This job is paused. Use the "Resume Paused Jobs" in the history menu to resume')+"</div>",this.defaultPrimaryActionButtonRenderers)},_render_body_error:function(){var c=['<span class="help-text">',_l("An error occurred with this dataset"),":</span>",'<div class="job-error-text">',$.trim(this.model.get("misc_info")),"</div>"].join("");if(!this.model.get("purged")){c="<div>"+this.model.get("misc_blurb")+"</div>"+c}return this._render_stateBodyHelper(c,[this._render_downloadButton].concat(this.defaultPrimaryActionButtonRenderers))},_render_body_empty:function(){return this._render_stateBodyHelper("<div>"+_l("No data")+": <i>"+this.model.get("misc_blurb")+"</i></div>",this.defaultPrimaryActionButtonRenderers)},_render_body_failed_metadata:function(){var c=$('<div class="warningmessagesmall"></div>').append($("<strong/>").text(_l("An error occurred setting the metadata for this dataset"))),d=this._render_body_ok();d.prepend(c);return d},_render_body_ok:function(){var c=this,e=$(a.templates.body(this.model.toJSON())),d=[this._render_downloadButton].concat(this.defaultPrimaryActionButtonRenderers);e.find(".dataset-actions .left").append(_.map(d,function(f){return f.call(c)}));if(this.model.isDeletedOrPurged()){return e}return e},events:{"click .dataset-title-bar":"toggleBodyVisibility","keydown .dataset-title-bar":"toggleBodyVisibility","click .dataset-selector":"toggleSelect",},toggleBodyVisibility:function(f,d){var c=32,e=13;if(f&&(f.type==="keydown")&&!(f.keyCode===c||f.keyCode===e)){return true}var g=this.$el.find(".dataset-body");d=(d===undefined)?(!g.is(":visible")):(d);if(d){this.expandBody()}else{this.collapseBody()}return false},expandBody:function(){var c=this;function d(){c.$el.children(".dataset-body").replaceWith(c._render_body());c.$el.children(".dataset-body").slideDown(c.fxSpeed,function(){c.expanded=true;c.trigger("body-expanded",c.model.get("id"))})}if(this.model.inReadyState()&&!this.model.hasDetails()){this.model.fetch({silent:true}).always(function(e){d()})}else{d()}},collapseBody:function(){var c=this;this.$el.children(".dataset-body").slideUp(c.fxSpeed,function(){c.expanded=false;c.trigger("body-collapsed",c.model.get("id"))})},showSelector:function(e){if(this.$el.find(".dataset-selector").css("display")!=="none"){return}e=(e!==undefined)?(e):(this.fxSpeed);if(this.selected){this.select(null,true)}var d=this,c=32;if(e){this.$el.queue("fx",function(f){$(this).find(".dataset-primary-actions").fadeOut(e,f)});this.$el.queue("fx",function(f){$(this).find(".dataset-selector").show().animate({width:c},e,f);$(this).find(".dataset-title-bar").animate({"margin-left":c},e,f);d.selectable=true;d.trigger("selectable",true,d)})}else{this.$el.find(".dataset-primary-actions").hide();this.$el.find(".dataset-selector").show().css({width:c});this.$el.find(".dataset-title-bar").show().css({"margin-left":c});d.selectable=true;d.trigger("selectable",true,d)}},hideSelector:function(c){c=(c!==undefined)?(c):(this.fxSpeed);this.selectable=false;this.trigger("selectable",false,this);if(c){this.$el.queue("fx",function(d){$(this).find(".dataset-title-bar").show().css({"margin-left":"0"});$(this).find(".dataset-selector").animate({width:"0px"},c,function(){$(this).hide();d()})});this.$el.queue("fx",function(d){$(this).find(".dataset-primary-actions").fadeIn(c,d)})}else{$(this).find(".dataset-selector").css({width:"0px"}).hide();$(this).find(".dataset-primary-actions").show()}},toggleSelector:function(c){if(!this.$el.find(".dataset-selector").is(":visible")){this.showSelector(c)}else{this.hideSelector(c)}},select:function(c){this.$el.find(".dataset-selector span").removeClass("fa-square-o").addClass("fa-check-square-o");if(!this.selected){this.trigger("selected",this);this.selected=true}return false},deselect:function(c){this.$el.find(".dataset-selector span").removeClass("fa-check-square-o").addClass("fa-square-o");if(this.selected){this.trigger("de-selected",this);this.selected=false}return false},toggleSelect:function(c){if(this.selected){this.deselect(c)}else{this.select(c)}},draggableOn:function(){this.draggable=true;this.dragStartHandler=_.bind(this._dragStartHandler,this);this.dragEndHandler=_.bind(this._dragEndHandler,this);var c=this.$el.find(".dataset-title-bar").attr("draggable",true).get(0);c.addEventListener("dragstart",this.dragStartHandler,false);c.addEventListener("dragend",this.dragEndHandler,false)},draggableOff:function(){this.draggable=false;var c=this.$el.find(".dataset-title-bar").attr("draggable",false).get(0);c.removeEventListener("dragstart",this.dragStartHandler,false);c.removeEventListener("dragend",this.dragEndHandler,false)},toggleDraggable:function(){if(this.draggable){this.draggableOff()}else{this.draggableOn()}},_dragStartHandler:function(c){this.trigger("dragstart",this);c.dataTransfer.effectAllowed="move";c.dataTransfer.setData("text",JSON.stringify(this.model.toJSON()));return false},_dragEndHandler:function(c){this.trigger("dragend",this);return false},remove:function(d){var c=this;this.$el.fadeOut(c.fxSpeed,function(){c.$el.remove();c.off();if(d){d()}})},toString:function(){var c=(this.model)?(this.model+""):("(no model)");return"HDABaseView("+c+")"}});a.templates={skeleton:Handlebars.templates["template-hda-skeleton"],body:Handlebars.templates["template-hda-body"]};return{HDABaseView:a}});
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: carlfeberhard: History panel: do not update title when empty string or unchanged
by commits-noreply@bitbucket.org 19 Dec '13
by commits-noreply@bitbucket.org 19 Dec '13
19 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/4fc04317cef3/
Changeset: 4fc04317cef3
User: carlfeberhard
Date: 2013-12-19 16:39:55
Summary: History panel: do not update title when empty string or unchanged
Affected #: 2 files
diff -r 67fe4b091f0ad66d68d06a36b8fb645aa445d032 -r 4fc04317cef36f002e071d391523450a7a956232 static/scripts/mvc/history/history-panel.js
--- a/static/scripts/mvc/history/history-panel.js
+++ b/static/scripts/mvc/history/history-panel.js
@@ -698,11 +698,16 @@
.attr( 'title', _l( 'Click to rename history' ) ).tooltip({ placement: 'bottom' })
.make_text_editable({
on_finish: function( newName ){
- $where.find( '.history-name' ).text( newName );
- panel.model.save({ name: newName })
- .fail( function(){
- $where.find( '.history-name' ).text( panel.model.previous( 'name' ) );
- });
+ var previousName = panel.model.get( 'name' );
+ if( newName && newName !== previousName ){
+ panel.$el.find( '.history-name' ).text( newName );
+ panel.model.save({ name: newName })
+ .fail( function(){
+ panel.$el.find( '.history-name' ).text( panel.model.previous( 'name' ) );
+ });
+ } else {
+ panel.$el.find( '.history-name' ).text( previousName );
+ }
}
});
this._setUpDatasetActionsPopup( $where );
diff -r 67fe4b091f0ad66d68d06a36b8fb645aa445d032 -r 4fc04317cef36f002e071d391523450a7a956232 static/scripts/packed/mvc/history/history-panel.js
--- a/static/scripts/packed/mvc/history/history-panel.js
+++ b/static/scripts/packed/mvc/history/history-panel.js
@@ -1,1 +1,1 @@
-define(["mvc/history/history-model","mvc/dataset/hda-model","mvc/dataset/hda-base","mvc/dataset/hda-edit"],function(c,f,a,i){var g=SessionStorageModel.extend({defaults:{searching:false,tagsEditorShown:false,annotationEditorShown:false},toString:function(){return"HistoryPanelPrefs("+JSON.stringify(this.toJSON())+")"}});g.storageKey=function h(){return("history-panel")};var d=SessionStorageModel.extend({defaults:{expandedHdas:{},show_deleted:false,show_hidden:false},addExpandedHda:function(j){this.save("expandedHdas",_.extend(this.get("expandedHdas"),_.object([j],[true])))},removeExpandedHda:function(j){this.save("expandedHdas",_.omit(this.get("expandedHdas"),j))},toString:function(){return"HistoryPrefs("+this.id+")"}});d.historyStorageKey=function b(j){if(!j){throw new Error("HistoryPrefs.historyStorageKey needs valid id: "+j)}return("history:"+j)};var e=Backbone.View.extend(LoggableMixin).extend({defaultHDAViewClass:i.HDAEditView,tagName:"div",className:"history-panel",fxSpeed:"fast",datasetsSelector:".datasets-list",emptyMsgSelector:".empty-history-message",msgsSelector:".message-container",initialize:function(j){j=j||{};if(j.logger){this.logger=j.logger}this.log(this+".initialize:",j);this.HDAViewClass=j.HDAViewClass||this.defaultHDAViewClass;this.linkTarget=j.linkTarget||"_blank";this.hdaViews={};this.indicator=new LoadingIndicator(this.$el);this.preferences=new g(_.extend({id:g.storageKey()},_.pick(j,_.keys(g.prototype.defaults))));this.filters=[];this.selecting=j.selecting||false;this.annotationEditorShown=j.annotationEditorShown||false;this._setUpListeners();if(this.model){this._setUpWebStorage(j.initiallyExpanded,j.show_deleted,j.show_hidden);this._setUpModelEventHandlers()}if(j.onready){j.onready.call(this)}},_setUpListeners:function(){this.on("error",function(k,n,j,m,l){this.errorHandler(k,n,j,m,l)});this.on("loading-history",function(){this.showLoadingIndicator("loading history...")});this.on("loading-done",function(){this.hideLoadingIndicator()});this.once("rendered",function(){this.trigger("rendered:initial",this);return false});this.on("switched-history current-history new-history",function(){if(_.isEmpty(this.hdaViews)){this.trigger("empty-history",this)}});if(this.logger){this.on("all",function(j){this.log(this+"",arguments)},this)}},errorHandler:function(l,o,k,n,m){var j=this._parseErrorMessage(l,o,k,n,m);if(o&&o.status===0&&o.readyState===0){}else{if(o&&o.status===502){}else{if(!this.$el.find(this.msgsSelector).is(":visible")){this.once("rendered",function(){this.displayMessage("error",j.message,j.details)})}else{this.displayMessage("error",j.message,j.details)}}}},_parseErrorMessage:function(m,q,l,p,o){var k=Galaxy.currUser,j={message:this._bePolite(p),details:{user:(k instanceof User)?(k.toJSON()):(k+""),source:(m instanceof Backbone.Model)?(m.toJSON()):(m+""),xhr:q,options:(q)?(_.omit(l,"xhr")):(l)}};_.extend(j.details,o||{});if(q&&_.isFunction(q.getAllResponseHeaders)){var n=q.getAllResponseHeaders();n=_.compact(n.split("\n"));n=_.map(n,function(r){return r.split(": ")});j.details.xhr.responseHeaders=_.object(n)}return j},_bePolite:function(j){j=j||_l("An error occurred while getting updates from the server");return j+". "+_l("Please contact a Galaxy administrator if the problem persists.")},loadCurrentHistory:function(k){var j=this;return this.loadHistoryWithHDADetails("current",k).then(function(m,l){j.trigger("current-history",j)})},switchToHistory:function(m,l){var j=this,k=function(){return jQuery.post(galaxy_config.root+"api/histories/"+m+"/set_as_current")};return this.loadHistoryWithHDADetails(m,l,k).then(function(o,n){j.trigger("switched-history",j)})},createNewHistory:function(l){if(!Galaxy||!Galaxy.currUser||Galaxy.currUser.isAnonymous()){this.displayMessage("error",_l("You must be logged in to create histories"));return $.when()}var j=this,k=function(){return jQuery.post(galaxy_config.root+"api/histories",{current:true})};return this.loadHistory(undefined,l,k).then(function(n,m){j.trigger("new-history",j)})},loadHistoryWithHDADetails:function(m,l,k,o){var j=this,n=function(p){return j.getExpandedHdaIds(p.id)};return this.loadHistory(m,l,k,o,n)},loadHistory:function(m,l,k,p,n){this.trigger("loading-history",this);l=l||{};var j=this;var o=c.History.getHistoryData(m,{historyFn:k,hdaFn:p,hdaDetailIds:l.initiallyExpanded||n});return this._loadHistoryFromXHR(o,l).fail(function(s,q,r){j.trigger("error",j,s,l,_l("An error was encountered while "+q),{historyId:m,history:r||{}})}).always(function(){j.trigger("loading-done",j)})},_loadHistoryFromXHR:function(l,k){var j=this;l.then(function(m,n){j.setModel(m,n,k)});l.fail(function(n,m){j.render()});return l},setModel:function(l,j,k){k=k||{};if(this.model){this.model.clearUpdateTimeout();this.stopListening(this.model);this.stopListening(this.model.hdas)}this.hdaViews={};if(Galaxy&&Galaxy.currUser){l.user=Galaxy.currUser.toJSON()}this.model=new c.History(l,j,k);this._setUpWebStorage(k.initiallyExpanded,k.show_deleted,k.show_hidden);this._setUpModelEventHandlers();this.trigger("new-model",this);this.render();return this},_setUpWebStorage:function(k,j,l){this.storage=new d({id:d.historyStorageKey(this.model.get("id"))});if(_.isObject(k)){this.storage.set("exandedHdas",k)}if(_.isBoolean(j)){this.storage.set("show_deleted",j)}if(_.isBoolean(l)){this.storage.set("show_hidden",l)}this.trigger("new-storage",this.storage,this);this.log(this+" (init'd) storage:",this.storage.get())},clearWebStorage:function(){for(var j in sessionStorage){if(j.indexOf("history:")===0){sessionStorage.removeItem(j)}}},getStoredOptions:function(k){if(!k||k==="current"){return(this.storage)?(this.storage.get()):({})}var j=sessionStorage.getItem(d.historyStorageKey(k));return(j===null)?({}):(JSON.parse(j))},getExpandedHdaIds:function(j){var k=this.getStoredOptions(j).expandedHdas;return((_.isEmpty(k))?([]):(_.keys(k)))},_setUpModelEventHandlers:function(){this.model.on("error error:hdas",function(k,m,j,l){this.errorHandler(k,m,j,l)},this);this.model.on("change:nice_size",this.updateHistoryDiskSize,this);if(Galaxy&&Galaxy.quotaMeter){this.listenTo(this.model,"change:nice_size",function(){Galaxy.quotaMeter.update()})}this.model.hdas.on("add",this.addHdaView,this);this.model.hdas.on("change:deleted",this.handleHdaDeletionChange,this);this.model.hdas.on("change:visible",this.handleHdaVisibleChange,this);this.model.hdas.on("change:purged",function(j){this.model.fetch()},this);this.model.hdas.on("state:ready",function(k,l,j){if((!k.get("visible"))&&(!this.storage.get("show_hidden"))){this.removeHdaView(this.hdaViews[k.id])}},this)},render:function(l,m){l=(l===undefined)?(this.fxSpeed):(l);var j=this,k;if(this.model){k=this.renderModel()}else{k=this.renderWithoutModel()}$(j).queue("fx",[function(n){if(l&&j.$el.is(":visible")){j.$el.fadeOut(l,n)}else{n()}},function(n){j.$el.empty();if(k){j.$el.append(k.children());j.renderBasedOnPrefs()}n()},function(n){if(l&&!j.$el.is(":visible")){j.$el.fadeIn(l,n)}else{n()}},function(n){if(m){m.call(this)}j.trigger("rendered",this);n()}]);return this},renderWithoutModel:function(){var j=$("<div/>"),k=$("<div/>").addClass("message-container").css({"margin-left":"4px","margin-right":"4px"});return j.append(k)},renderModel:function(){var j=$("<div/>");if(!Galaxy||!Galaxy.currUser||Galaxy.currUser.isAnonymous()){j.append(e.templates.anonHistoryPanel(this.model.toJSON()))}else{j.append(e.templates.historyPanel(this.model.toJSON()));if(Galaxy.currUser.id&&Galaxy.currUser.id===this.model.get("user_id")){this._renderTags(j);this._renderAnnotation(j)}}j.find(".history-secondary-actions").prepend(this._renderSearchButton());this._setUpBehaviours(j);this.renderHdas(j);return j},renderBasedOnPrefs:function(){if(this.preferences.get("searching")){this.showSearchControls(0)}},_renderTags:function(j){var k=this;this.tagsEditor=new TagsEditor({model:this.model,el:j.find(".history-controls .tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){k.preferences.set("tagsEditorShown",true);k.toggleHDATagEditors(true,k.fxSpeed)},onhide:function(){k.preferences.set("tagsEditorShown",false);k.toggleHDATagEditors(false,k.fxSpeed)},$activator:faIconButton({title:_l("Edit history tags"),classes:"history-tag-btn",faIcon:"fa-tags"}).appendTo(j.find(".history-secondary-actions"))});if(this.preferences.get("tagsEditorShown")){this.tagsEditor.toggle(true)}},_renderAnnotation:function(j){var k=this;this.annotationEditor=new AnnotationEditor({model:this.model,el:j.find(".history-controls .annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){k.preferences.set("annotationEditorShown",true);k.toggleHDAAnnotationEditors(true,k.fxSpeed)},onhide:function(){k.preferences.set("annotationEditorShown",false);k.toggleHDAAnnotationEditors(false,k.fxSpeed)},$activator:faIconButton({title:_l("Edit history Annotation"),classes:"history-annotate-btn",faIcon:"fa-comment"}).appendTo(j.find(".history-secondary-actions"))});if(this.preferences.get("annotationEditorShown")){this.annotationEditor.toggle(true)}},_renderSearchButton:function(j){return faIconButton({title:_l("Search datasets"),classes:"history-search-btn",faIcon:"fa-search"})},_renderSelectButton:function(j){return faIconButton({title:_l("Operations on multiple datasets"),classes:"history-select-btn",faIcon:"fa-check-square-o"})},_setUpBehaviours:function(j){j=j||this.$el;j.find("[title]").tooltip({placement:"bottom"});if((!this.model)||(!Galaxy.currUser||Galaxy.currUser.isAnonymous())||(Galaxy.currUser.id!==this.model.get("user_id"))){return}var k=this;j.find(".history-name").attr("title",_l("Click to rename history")).tooltip({placement:"bottom"}).make_text_editable({on_finish:function(l){j.find(".history-name").text(l);k.model.save({name:l}).fail(function(){j.find(".history-name").text(k.model.previous("name"))})}});this._setUpDatasetActionsPopup(j)},_setUpDatasetActionsPopup:function(j){var k=this;(new PopupMenu(j.find(".history-dataset-action-popup-btn"),[{html:_l("Hide datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.hide;k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Unhide datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.unhide;k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Delete datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype["delete"];k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Undelete datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.undelete;k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Permanently delete datasets"),func:function(){if(confirm(_l("This will permanently remove the data in your datasets. Are you sure?"))){var l=f.HistoryDatasetAssociation.prototype.purge;k.getSelectedHdaCollection().ajaxQueue(l)}}}]))},refreshHdas:function(k,j){if(this.model){return this.model.refresh(k,j)}return $.when()},addHdaView:function(m){this.log("add."+this,m);var k=this;if(!m.isVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"))){return}$({}).queue([function l(o){var n=k.$el.find(k.emptyMsgSelector);if(n.is(":visible")){n.fadeOut(k.fxSpeed,o)}else{o()}},function j(o){k.scrollToTop();var n=k.$el.find(k.datasetsSelector);k.createHdaView(m).$el.hide().prependTo(n).slideDown(k.fxSpeed)}])},createHdaView:function(l){var k=l.get("id"),j=this.storage.get("expandedHdas")[k],m=new this.HDAViewClass({model:l,linkTarget:this.linkTarget,expanded:j,tagsEditorShown:this.preferences.get("tagsEditorShown"),annotationEditorShown:this.preferences.get("annotationEditorShown"),selectable:this.selecting,hasUser:this.model.ownedByCurrUser(),logger:this.logger});this._setUpHdaListeners(m);this.hdaViews[k]=m;return m.render()},_setUpHdaListeners:function(k){var j=this;k.on("body-expanded",function(l){j.storage.addExpandedHda(l)});k.on("body-collapsed",function(l){j.storage.removeExpandedHda(l)});k.on("error",function(m,o,l,n){j.errorHandler(m,o,l,n)})},handleHdaDeletionChange:function(j){if(j.get("deleted")&&!this.storage.get("show_deleted")){this.removeHdaView(this.hdaViews[j.id])}},handleHdaVisibleChange:function(j){if(j.hidden()&&!this.storage.get("show_hidden")){this.removeHdaView(this.hdaViews[j.id])}},removeHdaView:function(k){if(!k){return}var j=this;k.$el.fadeOut(j.fxSpeed,function(){k.off();k.remove();delete j.hdaViews[k.model.id];if(_.isEmpty(j.hdaViews)){j.$el.find(j.emptyMsgSelector).fadeIn(j.fxSpeed,function(){j.trigger("empty-history",j)})}})},renderHdas:function(l){l=l||this.$el;this.hdaViews={};var k=this,j=l.find(this.datasetsSelector),m=this.model.hdas.getVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"),this.filters);j.empty();if(m.length){m.each(function(n){j.prepend(k.createHdaView(n).$el)});l.find(this.emptyMsgSelector).hide()}else{l.find(this.emptyMsgSelector).show()}return this.hdaViews},toggleHDATagEditors:function(j){var k=arguments;_.each(this.hdaViews,function(l){if(l.tagsEditor){l.tagsEditor.toggle.apply(l.tagsEditor,k)}})},toggleHDAAnnotationEditors:function(j){var k=arguments;_.each(this.hdaViews,function(l){if(l.annotationEditor){l.annotationEditor.toggle.apply(l.annotationEditor,k)}})},events:{"click .message-container":"clearMessages","click .history-search-btn":"toggleSearchControls","click .history-select-btn":function(j){this.toggleSelectors(this.fxSpeed)},"click .history-select-all-datasets-btn":"selectAllDatasets"},updateHistoryDiskSize:function(){this.$el.find(".history-size").text(this.model.get("nice_size"))},collapseAllHdaBodies:function(){_.each(this.hdaViews,function(j){j.toggleBodyVisibility(null,false)});this.storage.set("expandedHdas",{})},toggleShowDeleted:function(){this.storage.set("show_deleted",!this.storage.get("show_deleted"));this.renderHdas();return this.storage.get("show_deleted")},toggleShowHidden:function(){this.storage.set("show_hidden",!this.storage.get("show_hidden"));this.renderHdas();return this.storage.get("show_hidden")},renderSearchControls:function(k){var l=this;function n(o){l.searchFor=o;l.filters=[function(p){return p.matchesAll(l.searchFor)}];l.trigger("search:searching",o,l);l.renderHdas()}function j(o){if(l.model.hdas.haveDetails()){n(o);return}l.$el.find(".history-search-controls").searchInput("toggle-loading");l.model.hdas.fetchAllDetails({silent:true}).always(function(){l.$el.find(".history-search-controls").searchInput("toggle-loading")}).done(function(){n(o)})}function m(){l.searchFor="";l.filters=[];l.trigger("search:clear",l);l.renderHdas()}return k.searchInput({initialVal:l.searchFor,name:"history-search",placeholder:"search datasets",classes:"history-search",onfirstsearch:j,onsearch:n,onclear:m})},showSearchControls:function(l){l=(l===undefined)?(this.fxSpeed):(l);var j=this,k=this.$el.find(".history-search-controls");if(!k.children().size()){k=this.renderSearchControls(k).hide()}k.show(l,function(){$(this).find("input").focus();j.preferences.set("searching",true)})},hideSearchControls:function(){speed=(speed===undefined)?(this.fxSpeed):(speed);var j=this;this.$el.find(".history-search-controls").hide(speed,function(){j.preferences.set("searching",false)})},toggleSearchControls:function(j){speed=(jQuery.type(j)==="number")?(j):(this.fxSpeed);if(this.$el.find(".history-search-controls").is(":visible")){this.hideSearchControls(speed)}else{this.showSearchControls(speed)}},showSelectors:function(j){this.selecting=true;this.$el.find(".history-dataset-actions").slideDown(j);_.each(this.hdaViews,function(k){k.showSelector(j)})},hideSelectors:function(j){this.selecting=false;this.$el.find(".history-dataset-actions").slideUp(j);_.each(this.hdaViews,function(k){k.hideSelector(j)})},toggleSelectors:function(j){if(!this.selecting){this.showSelectors(j)}else{this.hideSelectors(j)}},selectAllDatasets:function(k){var j=this.$el.find(".history-select-all-datasets-btn");currMode=j.data("mode");if(currMode==="select"){_.each(this.hdaViews,function(l){l.select(k)});j.data("mode","deselect");j.text(_l("De-select all"))}else{if(currMode==="deselect"){_.each(this.hdaViews,function(l){l.deselect(k)});j.data("mode","select");j.text(_l("Select all"))}}},getSelectedHdaViews:function(){return _.filter(this.hdaViews,function(j){return j.selected})},getSelectedHdaCollection:function(){return new f.HDACollection(_.map(this.getSelectedHdaViews(),function(j){return j.model}),{historyId:this.model.id})},showLoadingIndicator:function(k,j,l){j=(j!==undefined)?(j):(this.fxSpeed);if(!this.indicator){this.indicator=new LoadingIndicator(this.$el,this.$el.parent())}if(!this.$el.is(":visible")){this.indicator.show(0,l)}else{this.$el.fadeOut(j);this.indicator.show(k,j,l)}},hideLoadingIndicator:function(j,k){j=(j!==undefined)?(j):(this.fxSpeed);if(this.indicator){this.indicator.hide(j,k)}},displayMessage:function(o,p,n){var l=this;this.scrollToTop();var m=this.$el.find(this.msgsSelector),j=$("<div/>").addClass(o+"message").html(p);if(!_.isEmpty(n)){var k=$('<a href="javascript:void(0)">Details</a>').click(function(){Galaxy.modal.show(l.messageToModalOptions(o,p,n));return false});j.append(" ",k)}return m.html(j)},messageToModalOptions:function(n,p,m){var j=this,o=$("<div/>"),l={title:"Details"};function k(q){q=_.omit(q,_.functions(q));return["<table>",_.map(q,function(s,r){s=(_.isObject(s))?(k(s)):(s);return'<tr><td style="vertical-align: top; color: grey">'+r+'</td><td style="padding-left: 8px">'+s+"</td></tr>"}).join(""),"</table>"].join("")}if(_.isObject(m)){l.body=o.append(k(m))}else{l.body=o.html(m)}l.buttons={Ok:function(){Galaxy.modal.hide();j.clearMessages()}};return l},clearMessages:function(){var j=this.$el.find(this.msgsSelector);j.empty()},scrollPosition:function(){return this.$el.parent().scrollTop()},scrollTo:function(j){this.$el.parent().scrollTop(j)},scrollToTop:function(){this.$el.parent().scrollTop(0);return this},scrollIntoView:function(k,l){if(!l){this.$el.parent().parent().scrollTop(k);return this}var j=window,m=this.$el.parent().parent(),o=$(j).innerHeight(),n=(o/2)-(l/2);$(m).scrollTop(k-n);return this},scrollToId:function(k){if((!k)||(!this.hdaViews[k])){return this}var j=this.hdaViews[k].$el;this.scrollIntoView(j.offset().top,j.outerHeight());return this},scrollToHid:function(j){var k=this.model.hdas.getByHid(j);if(!k){return this}return this.scrollToId(k.id)},connectToQuotaMeter:function(j){if(!j){return this}this.listenTo(j,"quota:over",this.showQuotaMessage);this.listenTo(j,"quota:under",this.hideQuotaMessage);this.on("rendered rendered:initial",function(){if(j&&j.isOverQuota()){this.showQuotaMessage()}});return this},showQuotaMessage:function(){var j=this.$el.find(".quota-message");if(j.is(":hidden")){j.slideDown(this.fxSpeed)}},hideQuotaMessage:function(){var j=this.$el.find(".quota-message");if(!j.is(":hidden")){j.slideUp(this.fxSpeed)}},connectToOptionsMenu:function(j){if(!j){return this}this.on("new-storage",function(l,k){if(j&&l){j.findItemByHtml(_l("Include Deleted Datasets")).checked=l.get("show_deleted");j.findItemByHtml(_l("Include Hidden Datasets")).checked=l.get("show_hidden")}});return this},toString:function(){return"HistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});e.templates={historyPanel:Handlebars.templates["template-history-historyPanel"],anonHistoryPanel:Handlebars.templates["template-history-historyPanel-anon"]};return{HistoryPanel:e}});
\ No newline at end of file
+define(["mvc/history/history-model","mvc/dataset/hda-model","mvc/dataset/hda-base","mvc/dataset/hda-edit"],function(c,f,a,i){var g=SessionStorageModel.extend({defaults:{searching:false,tagsEditorShown:false,annotationEditorShown:false},toString:function(){return"HistoryPanelPrefs("+JSON.stringify(this.toJSON())+")"}});g.storageKey=function h(){return("history-panel")};var d=SessionStorageModel.extend({defaults:{expandedHdas:{},show_deleted:false,show_hidden:false},addExpandedHda:function(j){this.save("expandedHdas",_.extend(this.get("expandedHdas"),_.object([j],[true])))},removeExpandedHda:function(j){this.save("expandedHdas",_.omit(this.get("expandedHdas"),j))},toString:function(){return"HistoryPrefs("+this.id+")"}});d.historyStorageKey=function b(j){if(!j){throw new Error("HistoryPrefs.historyStorageKey needs valid id: "+j)}return("history:"+j)};var e=Backbone.View.extend(LoggableMixin).extend({defaultHDAViewClass:i.HDAEditView,tagName:"div",className:"history-panel",fxSpeed:"fast",datasetsSelector:".datasets-list",emptyMsgSelector:".empty-history-message",msgsSelector:".message-container",initialize:function(j){j=j||{};if(j.logger){this.logger=j.logger}this.log(this+".initialize:",j);this.HDAViewClass=j.HDAViewClass||this.defaultHDAViewClass;this.linkTarget=j.linkTarget||"_blank";this.hdaViews={};this.indicator=new LoadingIndicator(this.$el);this.preferences=new g(_.extend({id:g.storageKey()},_.pick(j,_.keys(g.prototype.defaults))));this.filters=[];this.selecting=j.selecting||false;this.annotationEditorShown=j.annotationEditorShown||false;this._setUpListeners();if(this.model){this._setUpWebStorage(j.initiallyExpanded,j.show_deleted,j.show_hidden);this._setUpModelEventHandlers()}if(j.onready){j.onready.call(this)}},_setUpListeners:function(){this.on("error",function(k,n,j,m,l){this.errorHandler(k,n,j,m,l)});this.on("loading-history",function(){this.showLoadingIndicator("loading history...")});this.on("loading-done",function(){this.hideLoadingIndicator()});this.once("rendered",function(){this.trigger("rendered:initial",this);return false});this.on("switched-history current-history new-history",function(){if(_.isEmpty(this.hdaViews)){this.trigger("empty-history",this)}});if(this.logger){this.on("all",function(j){this.log(this+"",arguments)},this)}},errorHandler:function(l,o,k,n,m){var j=this._parseErrorMessage(l,o,k,n,m);if(o&&o.status===0&&o.readyState===0){}else{if(o&&o.status===502){}else{if(!this.$el.find(this.msgsSelector).is(":visible")){this.once("rendered",function(){this.displayMessage("error",j.message,j.details)})}else{this.displayMessage("error",j.message,j.details)}}}},_parseErrorMessage:function(m,q,l,p,o){var k=Galaxy.currUser,j={message:this._bePolite(p),details:{user:(k instanceof User)?(k.toJSON()):(k+""),source:(m instanceof Backbone.Model)?(m.toJSON()):(m+""),xhr:q,options:(q)?(_.omit(l,"xhr")):(l)}};_.extend(j.details,o||{});if(q&&_.isFunction(q.getAllResponseHeaders)){var n=q.getAllResponseHeaders();n=_.compact(n.split("\n"));n=_.map(n,function(r){return r.split(": ")});j.details.xhr.responseHeaders=_.object(n)}return j},_bePolite:function(j){j=j||_l("An error occurred while getting updates from the server");return j+". "+_l("Please contact a Galaxy administrator if the problem persists.")},loadCurrentHistory:function(k){var j=this;return this.loadHistoryWithHDADetails("current",k).then(function(m,l){j.trigger("current-history",j)})},switchToHistory:function(m,l){var j=this,k=function(){return jQuery.post(galaxy_config.root+"api/histories/"+m+"/set_as_current")};return this.loadHistoryWithHDADetails(m,l,k).then(function(o,n){j.trigger("switched-history",j)})},createNewHistory:function(l){if(!Galaxy||!Galaxy.currUser||Galaxy.currUser.isAnonymous()){this.displayMessage("error",_l("You must be logged in to create histories"));return $.when()}var j=this,k=function(){return jQuery.post(galaxy_config.root+"api/histories",{current:true})};return this.loadHistory(undefined,l,k).then(function(n,m){j.trigger("new-history",j)})},loadHistoryWithHDADetails:function(m,l,k,o){var j=this,n=function(p){return j.getExpandedHdaIds(p.id)};return this.loadHistory(m,l,k,o,n)},loadHistory:function(m,l,k,p,n){this.trigger("loading-history",this);l=l||{};var j=this;var o=c.History.getHistoryData(m,{historyFn:k,hdaFn:p,hdaDetailIds:l.initiallyExpanded||n});return this._loadHistoryFromXHR(o,l).fail(function(s,q,r){j.trigger("error",j,s,l,_l("An error was encountered while "+q),{historyId:m,history:r||{}})}).always(function(){j.trigger("loading-done",j)})},_loadHistoryFromXHR:function(l,k){var j=this;l.then(function(m,n){j.setModel(m,n,k)});l.fail(function(n,m){j.render()});return l},setModel:function(l,j,k){k=k||{};if(this.model){this.model.clearUpdateTimeout();this.stopListening(this.model);this.stopListening(this.model.hdas)}this.hdaViews={};if(Galaxy&&Galaxy.currUser){l.user=Galaxy.currUser.toJSON()}this.model=new c.History(l,j,k);this._setUpWebStorage(k.initiallyExpanded,k.show_deleted,k.show_hidden);this._setUpModelEventHandlers();this.trigger("new-model",this);this.render();return this},_setUpWebStorage:function(k,j,l){this.storage=new d({id:d.historyStorageKey(this.model.get("id"))});if(_.isObject(k)){this.storage.set("exandedHdas",k)}if(_.isBoolean(j)){this.storage.set("show_deleted",j)}if(_.isBoolean(l)){this.storage.set("show_hidden",l)}this.trigger("new-storage",this.storage,this);this.log(this+" (init'd) storage:",this.storage.get())},clearWebStorage:function(){for(var j in sessionStorage){if(j.indexOf("history:")===0){sessionStorage.removeItem(j)}}},getStoredOptions:function(k){if(!k||k==="current"){return(this.storage)?(this.storage.get()):({})}var j=sessionStorage.getItem(d.historyStorageKey(k));return(j===null)?({}):(JSON.parse(j))},getExpandedHdaIds:function(j){var k=this.getStoredOptions(j).expandedHdas;return((_.isEmpty(k))?([]):(_.keys(k)))},_setUpModelEventHandlers:function(){this.model.on("error error:hdas",function(k,m,j,l){this.errorHandler(k,m,j,l)},this);this.model.on("change:nice_size",this.updateHistoryDiskSize,this);if(Galaxy&&Galaxy.quotaMeter){this.listenTo(this.model,"change:nice_size",function(){Galaxy.quotaMeter.update()})}this.model.hdas.on("add",this.addHdaView,this);this.model.hdas.on("change:deleted",this.handleHdaDeletionChange,this);this.model.hdas.on("change:visible",this.handleHdaVisibleChange,this);this.model.hdas.on("change:purged",function(j){this.model.fetch()},this);this.model.hdas.on("state:ready",function(k,l,j){if((!k.get("visible"))&&(!this.storage.get("show_hidden"))){this.removeHdaView(this.hdaViews[k.id])}},this)},render:function(l,m){l=(l===undefined)?(this.fxSpeed):(l);var j=this,k;if(this.model){k=this.renderModel()}else{k=this.renderWithoutModel()}$(j).queue("fx",[function(n){if(l&&j.$el.is(":visible")){j.$el.fadeOut(l,n)}else{n()}},function(n){j.$el.empty();if(k){j.$el.append(k.children());j.renderBasedOnPrefs()}n()},function(n){if(l&&!j.$el.is(":visible")){j.$el.fadeIn(l,n)}else{n()}},function(n){if(m){m.call(this)}j.trigger("rendered",this);n()}]);return this},renderWithoutModel:function(){var j=$("<div/>"),k=$("<div/>").addClass("message-container").css({"margin-left":"4px","margin-right":"4px"});return j.append(k)},renderModel:function(){var j=$("<div/>");if(!Galaxy||!Galaxy.currUser||Galaxy.currUser.isAnonymous()){j.append(e.templates.anonHistoryPanel(this.model.toJSON()))}else{j.append(e.templates.historyPanel(this.model.toJSON()));if(Galaxy.currUser.id&&Galaxy.currUser.id===this.model.get("user_id")){this._renderTags(j);this._renderAnnotation(j)}}j.find(".history-secondary-actions").prepend(this._renderSearchButton());this._setUpBehaviours(j);this.renderHdas(j);return j},renderBasedOnPrefs:function(){if(this.preferences.get("searching")){this.showSearchControls(0)}},_renderTags:function(j){var k=this;this.tagsEditor=new TagsEditor({model:this.model,el:j.find(".history-controls .tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){k.preferences.set("tagsEditorShown",true);k.toggleHDATagEditors(true,k.fxSpeed)},onhide:function(){k.preferences.set("tagsEditorShown",false);k.toggleHDATagEditors(false,k.fxSpeed)},$activator:faIconButton({title:_l("Edit history tags"),classes:"history-tag-btn",faIcon:"fa-tags"}).appendTo(j.find(".history-secondary-actions"))});if(this.preferences.get("tagsEditorShown")){this.tagsEditor.toggle(true)}},_renderAnnotation:function(j){var k=this;this.annotationEditor=new AnnotationEditor({model:this.model,el:j.find(".history-controls .annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){k.preferences.set("annotationEditorShown",true);k.toggleHDAAnnotationEditors(true,k.fxSpeed)},onhide:function(){k.preferences.set("annotationEditorShown",false);k.toggleHDAAnnotationEditors(false,k.fxSpeed)},$activator:faIconButton({title:_l("Edit history Annotation"),classes:"history-annotate-btn",faIcon:"fa-comment"}).appendTo(j.find(".history-secondary-actions"))});if(this.preferences.get("annotationEditorShown")){this.annotationEditor.toggle(true)}},_renderSearchButton:function(j){return faIconButton({title:_l("Search datasets"),classes:"history-search-btn",faIcon:"fa-search"})},_renderSelectButton:function(j){return faIconButton({title:_l("Operations on multiple datasets"),classes:"history-select-btn",faIcon:"fa-check-square-o"})},_setUpBehaviours:function(j){j=j||this.$el;j.find("[title]").tooltip({placement:"bottom"});if((!this.model)||(!Galaxy.currUser||Galaxy.currUser.isAnonymous())||(Galaxy.currUser.id!==this.model.get("user_id"))){return}var k=this;j.find(".history-name").attr("title",_l("Click to rename history")).tooltip({placement:"bottom"}).make_text_editable({on_finish:function(l){var m=k.model.get("name");if(l&&l!==m){k.$el.find(".history-name").text(l);k.model.save({name:l}).fail(function(){k.$el.find(".history-name").text(k.model.previous("name"))})}else{k.$el.find(".history-name").text(m)}}});this._setUpDatasetActionsPopup(j)},_setUpDatasetActionsPopup:function(j){var k=this;(new PopupMenu(j.find(".history-dataset-action-popup-btn"),[{html:_l("Hide datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.hide;k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Unhide datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.unhide;k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Delete datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype["delete"];k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Undelete datasets"),func:function(){var l=f.HistoryDatasetAssociation.prototype.undelete;k.getSelectedHdaCollection().ajaxQueue(l)}},{html:_l("Permanently delete datasets"),func:function(){if(confirm(_l("This will permanently remove the data in your datasets. Are you sure?"))){var l=f.HistoryDatasetAssociation.prototype.purge;k.getSelectedHdaCollection().ajaxQueue(l)}}}]))},refreshHdas:function(k,j){if(this.model){return this.model.refresh(k,j)}return $.when()},addHdaView:function(m){this.log("add."+this,m);var k=this;if(!m.isVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"))){return}$({}).queue([function l(o){var n=k.$el.find(k.emptyMsgSelector);if(n.is(":visible")){n.fadeOut(k.fxSpeed,o)}else{o()}},function j(o){k.scrollToTop();var n=k.$el.find(k.datasetsSelector);k.createHdaView(m).$el.hide().prependTo(n).slideDown(k.fxSpeed)}])},createHdaView:function(l){var k=l.get("id"),j=this.storage.get("expandedHdas")[k],m=new this.HDAViewClass({model:l,linkTarget:this.linkTarget,expanded:j,tagsEditorShown:this.preferences.get("tagsEditorShown"),annotationEditorShown:this.preferences.get("annotationEditorShown"),selectable:this.selecting,hasUser:this.model.ownedByCurrUser(),logger:this.logger});this._setUpHdaListeners(m);this.hdaViews[k]=m;return m.render()},_setUpHdaListeners:function(k){var j=this;k.on("body-expanded",function(l){j.storage.addExpandedHda(l)});k.on("body-collapsed",function(l){j.storage.removeExpandedHda(l)});k.on("error",function(m,o,l,n){j.errorHandler(m,o,l,n)})},handleHdaDeletionChange:function(j){if(j.get("deleted")&&!this.storage.get("show_deleted")){this.removeHdaView(this.hdaViews[j.id])}},handleHdaVisibleChange:function(j){if(j.hidden()&&!this.storage.get("show_hidden")){this.removeHdaView(this.hdaViews[j.id])}},removeHdaView:function(k){if(!k){return}var j=this;k.$el.fadeOut(j.fxSpeed,function(){k.off();k.remove();delete j.hdaViews[k.model.id];if(_.isEmpty(j.hdaViews)){j.$el.find(j.emptyMsgSelector).fadeIn(j.fxSpeed,function(){j.trigger("empty-history",j)})}})},renderHdas:function(l){l=l||this.$el;this.hdaViews={};var k=this,j=l.find(this.datasetsSelector),m=this.model.hdas.getVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"),this.filters);j.empty();if(m.length){m.each(function(n){j.prepend(k.createHdaView(n).$el)});l.find(this.emptyMsgSelector).hide()}else{l.find(this.emptyMsgSelector).show()}return this.hdaViews},toggleHDATagEditors:function(j){var k=arguments;_.each(this.hdaViews,function(l){if(l.tagsEditor){l.tagsEditor.toggle.apply(l.tagsEditor,k)}})},toggleHDAAnnotationEditors:function(j){var k=arguments;_.each(this.hdaViews,function(l){if(l.annotationEditor){l.annotationEditor.toggle.apply(l.annotationEditor,k)}})},events:{"click .message-container":"clearMessages","click .history-search-btn":"toggleSearchControls","click .history-select-btn":function(j){this.toggleSelectors(this.fxSpeed)},"click .history-select-all-datasets-btn":"selectAllDatasets"},updateHistoryDiskSize:function(){this.$el.find(".history-size").text(this.model.get("nice_size"))},collapseAllHdaBodies:function(){_.each(this.hdaViews,function(j){j.toggleBodyVisibility(null,false)});this.storage.set("expandedHdas",{})},toggleShowDeleted:function(){this.storage.set("show_deleted",!this.storage.get("show_deleted"));this.renderHdas();return this.storage.get("show_deleted")},toggleShowHidden:function(){this.storage.set("show_hidden",!this.storage.get("show_hidden"));this.renderHdas();return this.storage.get("show_hidden")},renderSearchControls:function(k){var l=this;function n(o){l.searchFor=o;l.filters=[function(p){return p.matchesAll(l.searchFor)}];l.trigger("search:searching",o,l);l.renderHdas()}function j(o){if(l.model.hdas.haveDetails()){n(o);return}l.$el.find(".history-search-controls").searchInput("toggle-loading");l.model.hdas.fetchAllDetails({silent:true}).always(function(){l.$el.find(".history-search-controls").searchInput("toggle-loading")}).done(function(){n(o)})}function m(){l.searchFor="";l.filters=[];l.trigger("search:clear",l);l.renderHdas()}return k.searchInput({initialVal:l.searchFor,name:"history-search",placeholder:"search datasets",classes:"history-search",onfirstsearch:j,onsearch:n,onclear:m})},showSearchControls:function(l){l=(l===undefined)?(this.fxSpeed):(l);var j=this,k=this.$el.find(".history-search-controls");if(!k.children().size()){k=this.renderSearchControls(k).hide()}k.show(l,function(){$(this).find("input").focus();j.preferences.set("searching",true)})},hideSearchControls:function(){speed=(speed===undefined)?(this.fxSpeed):(speed);var j=this;this.$el.find(".history-search-controls").hide(speed,function(){j.preferences.set("searching",false)})},toggleSearchControls:function(j){speed=(jQuery.type(j)==="number")?(j):(this.fxSpeed);if(this.$el.find(".history-search-controls").is(":visible")){this.hideSearchControls(speed)}else{this.showSearchControls(speed)}},showSelectors:function(j){this.selecting=true;this.$el.find(".history-dataset-actions").slideDown(j);_.each(this.hdaViews,function(k){k.showSelector(j)})},hideSelectors:function(j){this.selecting=false;this.$el.find(".history-dataset-actions").slideUp(j);_.each(this.hdaViews,function(k){k.hideSelector(j)})},toggleSelectors:function(j){if(!this.selecting){this.showSelectors(j)}else{this.hideSelectors(j)}},selectAllDatasets:function(k){var j=this.$el.find(".history-select-all-datasets-btn");currMode=j.data("mode");if(currMode==="select"){_.each(this.hdaViews,function(l){l.select(k)});j.data("mode","deselect");j.text(_l("De-select all"))}else{if(currMode==="deselect"){_.each(this.hdaViews,function(l){l.deselect(k)});j.data("mode","select");j.text(_l("Select all"))}}},getSelectedHdaViews:function(){return _.filter(this.hdaViews,function(j){return j.selected})},getSelectedHdaCollection:function(){return new f.HDACollection(_.map(this.getSelectedHdaViews(),function(j){return j.model}),{historyId:this.model.id})},showLoadingIndicator:function(k,j,l){j=(j!==undefined)?(j):(this.fxSpeed);if(!this.indicator){this.indicator=new LoadingIndicator(this.$el,this.$el.parent())}if(!this.$el.is(":visible")){this.indicator.show(0,l)}else{this.$el.fadeOut(j);this.indicator.show(k,j,l)}},hideLoadingIndicator:function(j,k){j=(j!==undefined)?(j):(this.fxSpeed);if(this.indicator){this.indicator.hide(j,k)}},displayMessage:function(o,p,n){var l=this;this.scrollToTop();var m=this.$el.find(this.msgsSelector),j=$("<div/>").addClass(o+"message").html(p);if(!_.isEmpty(n)){var k=$('<a href="javascript:void(0)">Details</a>').click(function(){Galaxy.modal.show(l.messageToModalOptions(o,p,n));return false});j.append(" ",k)}return m.html(j)},messageToModalOptions:function(n,p,m){var j=this,o=$("<div/>"),l={title:"Details"};function k(q){q=_.omit(q,_.functions(q));return["<table>",_.map(q,function(s,r){s=(_.isObject(s))?(k(s)):(s);return'<tr><td style="vertical-align: top; color: grey">'+r+'</td><td style="padding-left: 8px">'+s+"</td></tr>"}).join(""),"</table>"].join("")}if(_.isObject(m)){l.body=o.append(k(m))}else{l.body=o.html(m)}l.buttons={Ok:function(){Galaxy.modal.hide();j.clearMessages()}};return l},clearMessages:function(){var j=this.$el.find(this.msgsSelector);j.empty()},scrollPosition:function(){return this.$el.parent().scrollTop()},scrollTo:function(j){this.$el.parent().scrollTop(j)},scrollToTop:function(){this.$el.parent().scrollTop(0);return this},scrollIntoView:function(k,l){if(!l){this.$el.parent().parent().scrollTop(k);return this}var j=window,m=this.$el.parent().parent(),o=$(j).innerHeight(),n=(o/2)-(l/2);$(m).scrollTop(k-n);return this},scrollToId:function(k){if((!k)||(!this.hdaViews[k])){return this}var j=this.hdaViews[k].$el;this.scrollIntoView(j.offset().top,j.outerHeight());return this},scrollToHid:function(j){var k=this.model.hdas.getByHid(j);if(!k){return this}return this.scrollToId(k.id)},connectToQuotaMeter:function(j){if(!j){return this}this.listenTo(j,"quota:over",this.showQuotaMessage);this.listenTo(j,"quota:under",this.hideQuotaMessage);this.on("rendered rendered:initial",function(){if(j&&j.isOverQuota()){this.showQuotaMessage()}});return this},showQuotaMessage:function(){var j=this.$el.find(".quota-message");if(j.is(":hidden")){j.slideDown(this.fxSpeed)}},hideQuotaMessage:function(){var j=this.$el.find(".quota-message");if(!j.is(":hidden")){j.slideUp(this.fxSpeed)}},connectToOptionsMenu:function(j){if(!j){return this}this.on("new-storage",function(l,k){if(j&&l){j.findItemByHtml(_l("Include Deleted Datasets")).checked=l.get("show_deleted");j.findItemByHtml(_l("Include Hidden Datasets")).checked=l.get("show_hidden")}});return this},toString:function(){return"HistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});e.templates={historyPanel:Handlebars.templates["template-history-historyPanel"],anonHistoryPanel:Handlebars.templates["template-history-historyPanel-anon"]};return{HistoryPanel:e}});
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: greg: Fix for determining a guid in the install_manager.
by commits-noreply@bitbucket.org 19 Dec '13
by commits-noreply@bitbucket.org 19 Dec '13
19 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/a9a0ac9c1afa/
Changeset: a9a0ac9c1afa
Branch: stable
User: greg
Date: 2013-12-19 01:03:05
Summary: Fix for determining a guid in the install_manager.
Affected #: 1 file
diff -r 26da838b778c68a5577d4cca9836f6ef70b8ed1b -r a9a0ac9c1afae8dd0d662c9c800d0228c883c107 lib/tool_shed/galaxy_install/install_manager.py
--- a/lib/tool_shed/galaxy_install/install_manager.py
+++ b/lib/tool_shed/galaxy_install/install_manager.py
@@ -244,21 +244,20 @@
def get_guid( self, repository_clone_url, relative_install_dir, tool_config ):
if self.shed_config_dict.get( 'tool_path' ):
- relative_install_dir = os.path.join( self.shed_config_dict['tool_path'], relative_install_dir )
- found = False
+ relative_install_dir = os.path.join( self.shed_config_dict[ 'tool_path' ], relative_install_dir )
+ tool_config_filename = suc.strip_path( tool_config )
for root, dirs, files in os.walk( relative_install_dir ):
if root.find( '.hg' ) < 0 and root.find( 'hgrc' ) < 0:
if '.hg' in dirs:
dirs.remove( '.hg' )
for name in files:
- if name == tool_config:
- found = True
- break
- if found:
- break
- full_path = str( os.path.abspath( os.path.join( root, name ) ) )
- tool = self.toolbox.load_tool( full_path )
- return suc.generate_tool_guid( repository_clone_url, tool )
+ filename = suc.strip_path( name )
+ if filename == tool_config_filename:
+ full_path = str( os.path.abspath( os.path.join( root, name ) ) )
+ tool = self.toolbox.load_tool( full_path )
+ return suc.generate_tool_guid( repository_clone_url, tool )
+ # Not quite sure what should happen here, throw an exception or what?
+ return None
def get_prior_install_required_dict( self, tool_shed_repositories, repository_dependencies_dict ):
"""
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: greg: Don't uninstall repositories when testing tool dependency definitions in the tool shed's install and test process.
by commits-noreply@bitbucket.org 19 Dec '13
by commits-noreply@bitbucket.org 19 Dec '13
19 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/67fe4b091f0a/
Changeset: 67fe4b091f0a
User: greg
Date: 2013-12-19 14:47:20
Summary: Don't uninstall repositories when testing tool dependency definitions in the tool shed's install and test process.
Affected #: 1 file
diff -r 5bfa1d47b3999a695a04294adb13da0b5b3ec041 -r 67fe4b091f0ad66d68d06a36b8fb645aa445d032 test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
@@ -201,28 +201,18 @@
else:
log.debug( 'Installation succeeded for revision %s of repository %s owned by %s.' % \
( changeset_revision, name, owner ) )
- # Keep statistics for this repository's tool dependencies that resulted in installation errors.
- for missing_tool_dependency in repository.missing_tool_dependencies:
- name = str( missing_tool_dependency.name )
- type = str( missing_tool_dependency.type )
- version = str( missing_tool_dependency.version )
- error_message = unicodify( missing_tool_dependency.error_message )
- missing_tool_dependency_info_dict = dict( type=type,
- name=name,
- version=version,
- error_message=error_message )
- install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ].append( missing_tool_dependency_info_dict )
- # Attempt to uninstall this repository and all of its dependencies if its repository dependencies or
- # tool dependencies resulted in an installation error.
- missing_tool_dependencies = install_and_test_base_util.get_missing_tool_dependencies( repository )
- if missing_tool_dependencies or repository.missing_repository_dependencies:
- install_and_test_base_util.handle_missing_dependencies( app=app,
- repository=repository,
- missing_tool_dependencies=missing_tool_dependencies,
- repository_dict=repository_dict,
- tool_test_results_dicts=tool_test_results_dicts,
- tool_test_results_dict=tool_test_results_dict,
- can_update_tool_shed=can_update_tool_shed )
+ if repository.missing_tool_dependencies:
+ # Keep statistics for this repository's tool dependencies that resulted in installation errors.
+ for missing_tool_dependency in repository.missing_tool_dependencies:
+ name = str( missing_tool_dependency.name )
+ type = str( missing_tool_dependency.type )
+ version = str( missing_tool_dependency.version )
+ error_message = unicodify( missing_tool_dependency.error_message )
+ missing_tool_dependency_info_dict = dict( type=type,
+ name=name,
+ version=version,
+ error_message=error_message )
+ install_and_test_statistics_dict[ 'tool_dependencies_with_installation_error' ].append( missing_tool_dependency_info_dict )
else:
# This repository and all of its dependencies were successfully installed.
install_and_test_statistics_dict[ 'successful_installations' ].append( repository_identifier_dict )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: greg: Fix for handling missing dependencies in the tool shed's install and test framework.
by commits-noreply@bitbucket.org 18 Dec '13
by commits-noreply@bitbucket.org 18 Dec '13
18 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/5bfa1d47b399/
Changeset: 5bfa1d47b399
User: greg
Date: 2013-12-19 02:32:15
Summary: Fix for handling missing dependencies in the tool shed's install and test framework.
Affected #: 2 files
diff -r 4077a5a1f801a772acd01d19c9695329a42c5958 -r 5bfa1d47b3999a695a04294adb13da0b5b3ec041 test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
@@ -286,16 +286,15 @@
if 'missing_test_components' not in tool_test_results_dict:
tool_test_results_dict[ 'missing_test_components' ] = []
missing_tool_dependencies = install_and_test_base_util.get_missing_tool_dependencies( repository )
- if missing_tool_dependencies or repository.missing_repository_dependencies:
+ if missing_tool_dependencies or repository.missing_repository_dependencies:
install_and_test_statistics_dict = \
- install_and_test_base_util.handle_missing_dependencies( app,
- repository,
- missing_tool_dependencies,
- repository_dict,
- tool_test_results_dicts,
- tool_test_results_dict,
- install_and_test_statistics_dict,
- can_update_tool_shed )
+ install_and_test_base_util.handle_missing_dependencies( app=app,
+ repository=repository,
+ missing_tool_dependencies=missing_tool_dependencies,
+ repository_dict=repository_dict,
+ tool_test_results_dicts=tool_test_results_dicts,
+ tool_test_results_dict=tool_test_results_dict,
+ can_update_tool_shed=can_update_tool_shed )
# Set the test_toolbox.toolbox module-level variable to the new app.toolbox.
test_toolbox.toolbox = app.toolbox
else:
diff -r 4077a5a1f801a772acd01d19c9695329a42c5958 -r 5bfa1d47b3999a695a04294adb13da0b5b3ec041 test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
@@ -216,14 +216,13 @@
# tool dependencies resulted in an installation error.
missing_tool_dependencies = install_and_test_base_util.get_missing_tool_dependencies( repository )
if missing_tool_dependencies or repository.missing_repository_dependencies:
- install_and_test_base_util.handle_missing_dependencies( app,
- repository,
- missing_tool_dependencies,
- repository_dict,
- tool_test_results_dicts,
- tool_test_results_dict,
- install_and_test_statistics_dict,
- can_update_tool_shed )
+ install_and_test_base_util.handle_missing_dependencies( app=app,
+ repository=repository,
+ missing_tool_dependencies=missing_tool_dependencies,
+ repository_dict=repository_dict,
+ tool_test_results_dicts=tool_test_results_dicts,
+ tool_test_results_dict=tool_test_results_dict,
+ can_update_tool_shed=can_update_tool_shed )
else:
# This repository and all of its dependencies were successfully installed.
install_and_test_statistics_dict[ 'successful_installations' ].append( repository_identifier_dict )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: greg: Fix imports in the tool shed's functional test framework.
by commits-noreply@bitbucket.org 18 Dec '13
by commits-noreply@bitbucket.org 18 Dec '13
18 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/4077a5a1f801/
Changeset: 4077a5a1f801
User: greg
Date: 2013-12-19 01:09:11
Summary: Fix imports in the tool shed's functional test framework.
Affected #: 3 files
diff -r a53f2f700699c9d88b1896814b3eb2ec8cc028b8 -r 4077a5a1f801a772acd01d19c9695329a42c5958 test/install_and_test_tool_shed_repositories/base/util.py
--- a/test/install_and_test_tool_shed_repositories/base/util.py
+++ b/test/install_and_test_tool_shed_repositories/base/util.py
@@ -595,7 +595,6 @@
exclude_list.append( exclude_dict )
log.debug( 'The xml document %s containing the exclude list defines the following %s repositories to be excluded from testing...' % \
( str( xml_filename ), str( exclude_count ) ) )
- #if '-list_repositories' in sys.argv:
for name, owner, changeset_revision in exclude_verbose:
if changeset_revision:
log.debug( 'Repository %s owned by %s, changeset revision %s.' % ( str( name ), str( owner ), str( changeset_revision ) ) )
diff -r a53f2f700699c9d88b1896814b3eb2ec8cc028b8 -r 4077a5a1f801a772acd01d19c9695329a42c5958 test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/repositories_with_tools/functional_tests.py
@@ -45,6 +45,7 @@
from galaxy.app import UniverseApplication
from galaxy.util.json import from_json_string
from galaxy.util.json import to_json_string
+from galaxy.util import unicodify
from galaxy.web import buildapp
from functional_tests import generate_config_file
from nose.plugins import Plugin
diff -r a53f2f700699c9d88b1896814b3eb2ec8cc028b8 -r 4077a5a1f801a772acd01d19c9695329a42c5958 test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
--- a/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
+++ b/test/install_and_test_tool_shed_repositories/tool_dependency_definitions/functional_tests.py
@@ -45,6 +45,7 @@
from galaxy.app import UniverseApplication
from galaxy.util.json import from_json_string
from galaxy.util.json import to_json_string
+from galaxy.util import unicodify
from galaxy.web import buildapp
from functional_tests import generate_config_file
from nose.plugins import Plugin
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: greg: Fix for determining a guid in the install_manager.
by commits-noreply@bitbucket.org 18 Dec '13
by commits-noreply@bitbucket.org 18 Dec '13
18 Dec '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/a53f2f700699/
Changeset: a53f2f700699
User: greg
Date: 2013-12-19 01:03:05
Summary: Fix for determining a guid in the install_manager.
Affected #: 1 file
diff -r 9fa754a203b7312d58211b5e1e7140bf36aec76a -r a53f2f700699c9d88b1896814b3eb2ec8cc028b8 lib/tool_shed/galaxy_install/install_manager.py
--- a/lib/tool_shed/galaxy_install/install_manager.py
+++ b/lib/tool_shed/galaxy_install/install_manager.py
@@ -244,21 +244,20 @@
def get_guid( self, repository_clone_url, relative_install_dir, tool_config ):
if self.shed_config_dict.get( 'tool_path' ):
- relative_install_dir = os.path.join( self.shed_config_dict['tool_path'], relative_install_dir )
- found = False
+ relative_install_dir = os.path.join( self.shed_config_dict[ 'tool_path' ], relative_install_dir )
+ tool_config_filename = suc.strip_path( tool_config )
for root, dirs, files in os.walk( relative_install_dir ):
if root.find( '.hg' ) < 0 and root.find( 'hgrc' ) < 0:
if '.hg' in dirs:
dirs.remove( '.hg' )
for name in files:
- if name == tool_config:
- found = True
- break
- if found:
- break
- full_path = str( os.path.abspath( os.path.join( root, name ) ) )
- tool = self.toolbox.load_tool( full_path )
- return suc.generate_tool_guid( repository_clone_url, tool )
+ filename = suc.strip_path( name )
+ if filename == tool_config_filename:
+ full_path = str( os.path.abspath( os.path.join( root, name ) ) )
+ tool = self.toolbox.load_tool( full_path )
+ return suc.generate_tool_guid( repository_clone_url, tool )
+ # Not quite sure what should happen here, throw an exception or what?
+ return None
def get_prior_install_required_dict( self, tool_shed_repositories, repository_dependencies_dict ):
"""
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0