galaxy-commits
  Threads by month 
                
            - ----- 2025 -----
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2024 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2023 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2022 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2021 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2020 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2019 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2018 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2017 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2016 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2015 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2014 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2013 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2012 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2011 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2010 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 
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 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/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 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/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 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/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