6 new commits in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/1a76c4302896/ Changeset: 1a76c4302896 User: dannon Date: 2013-10-02 01:58:31 Summary: Backout re-encoding from 10711, since it's done in send_redirect now. Affected #: 1 file diff -r e416f8c710b63d7fc9f682539929e9823c26f041 -r 1a76c43028967403168b7750879dc21c25c53919 lib/galaxy/webapps/galaxy/controllers/dataset.py --- a/lib/galaxy/webapps/galaxy/controllers/dataset.py +++ b/lib/galaxy/webapps/galaxy/controllers/dataset.py @@ -708,7 +708,6 @@ redirect_url = kwd['redirect_url'] % urllib.quote_plus( kwd['display_url'] ) except: redirect_url = kwd['redirect_url'] # not all will need custom text - redirect_url = redirect_url.encode( 'utf-8' ) current_user_roles = trans.get_current_user_roles() if trans.app.security_agent.dataset_is_public( data.dataset ): return trans.response.send_redirect( redirect_url ) # anon access already permitted by rbac https://bitbucket.org/galaxy/galaxy-central/commits/2172bff6e3bb/ Changeset: 2172bff6e3bb User: dannon Date: 2013-10-04 17:42:21 Summary: Fix imports in datasets controller, removing *s Affected #: 1 file diff -r 1a76c43028967403168b7750879dc21c25c53919 -r 2172bff6e3bbafed3749d8d3d1bd558903d00a5b lib/galaxy/webapps/galaxy/controllers/dataset.py --- a/lib/galaxy/webapps/galaxy/controllers/dataset.py +++ b/lib/galaxy/webapps/galaxy/controllers/dataset.py @@ -1,23 +1,20 @@ import logging -import mimetypes import os import string -import sys import tempfile import urllib import zipfile -from galaxy.web.base.controller import * -from galaxy.web.framework.helpers import time_ago, iff, grids -from galaxy import util, datatypes, web, model -from galaxy.datatypes.display_applications.util import encode_dataset_user, decode_dataset_user +from galaxy import datatypes, eggs, model, util, web +from galaxy.datatypes.display_applications.util import decode_dataset_user, encode_dataset_user +from galaxy.model.item_attrs import UsesAnnotations, UsesItemRatings +from galaxy.util import inflector from galaxy.util.sanitize_html import sanitize_html -from galaxy.util import inflector -from galaxy.model.item_attrs import * +from galaxy.web.base.controller import BaseUIController, ERROR, SUCCESS, url_for, UsesHistoryDatasetAssociationMixin, UsesHistoryMixin +from galaxy.web.framework.helpers import grids, iff, time_ago from galaxy.web.framework.helpers import to_unicode -import pkg_resources; -pkg_resources.require( "Paste" ) +eggs.require( "Paste" ) import paste.httpexceptions tmpd = tempfile.mkdtemp() https://bitbucket.org/galaxy/galaxy-central/commits/2aa0bfdf1534/ Changeset: 2aa0bfdf1534 User: dannon Date: 2013-10-04 17:43:33 Summary: Dataset controller: log needed to exist just post import; this was previously using log from one of the * imports... Affected #: 1 file diff -r 2172bff6e3bbafed3749d8d3d1bd558903d00a5b -r 2aa0bfdf15340da4cf203713066bcd71cd5b3f2e lib/galaxy/webapps/galaxy/controllers/dataset.py --- a/lib/galaxy/webapps/galaxy/controllers/dataset.py +++ b/lib/galaxy/webapps/galaxy/controllers/dataset.py @@ -17,6 +17,8 @@ eggs.require( "Paste" ) import paste.httpexceptions +log = logging.getLogger( __name__ ) + tmpd = tempfile.mkdtemp() comptypes=[] ziptype = '32' @@ -37,7 +39,6 @@ pass os.rmdir( tmpd ) -log = logging.getLogger( __name__ ) error_report_template = """ GALAXY TOOL ERROR REPORT https://bitbucket.org/galaxy/galaxy-central/commits/0b51e7ba3ac5/ Changeset: 0b51e7ba3ac5 User: dannon Date: 2013-10-04 17:48:07 Summary: Datasets controller, fix misc invalid references and function calls. Affected #: 1 file diff -r 2aa0bfdf15340da4cf203713066bcd71cd5b3f2e -r 0b51e7ba3ac525b00577f6c67a36f4bdc61d8e2f lib/galaxy/webapps/galaxy/controllers/dataset.py --- a/lib/galaxy/webapps/galaxy/controllers/dataset.py +++ b/lib/galaxy/webapps/galaxy/controllers/dataset.py @@ -202,7 +202,7 @@ trans.response.set_content_type( 'text/plain' ) exit_code = "" try: - job = _get_job_for_dataset( dataset_id ) + job = self._get_job_for_dataset( dataset_id ) exit_code = job.exit_code except: exit_code = "Invalid dataset ID or you are not allowed to access this dataset" @@ -613,7 +613,7 @@ return trans.show_error_message( "The specified dataset does not exist." ) # Rate dataset. - dataset_rating = self.rate_item( rate_item, trans.get_user(), dataset, rating ) + dataset_rating = self.rate_item( trans.sa_session, trans.get_user(), dataset, rating ) return self.get_ave_item_rating_data( trans.sa_session, dataset ) https://bitbucket.org/galaxy/galaxy-central/commits/8e6d9ee6bb64/ Changeset: 8e6d9ee6bb64 User: dannon Date: 2013-10-04 17:56:40 Summary: Strip unused code/vars from datasets controller Affected #: 1 file diff -r 0b51e7ba3ac525b00577f6c67a36f4bdc61d8e2f -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 lib/galaxy/webapps/galaxy/controllers/dataset.py --- a/lib/galaxy/webapps/galaxy/controllers/dataset.py +++ b/lib/galaxy/webapps/galaxy/controllers/dataset.py @@ -561,19 +561,15 @@ @web.expose def imp( self, trans, dataset_id=None, **kwd ): """ Import another user's dataset via a shared URL; dataset is added to user's current history. """ - msg = "" - # Set referer message. referer = trans.request.referer if referer is not "": referer_message = "<a href='%s'>return to the previous page</a>" % referer else: referer_message = "<a href='%s'>go to Galaxy's start page</a>" % url_for( '/' ) - # Error checking. if not dataset_id: return trans.show_error_message( "You must specify a dataset to import. You can %s." % referer_message, use_panels=True ) - # Do import. cur_history = trans.get_history( create=True ) status, message = self._copy_datasets( trans, [ dataset_id ], [ cur_history ], imported=True ) @@ -613,7 +609,7 @@ return trans.show_error_message( "The specified dataset does not exist." ) # Rate dataset. - dataset_rating = self.rate_item( trans.sa_session, trans.get_user(), dataset, rating ) + self.rate_item( trans.sa_session, trans.get_user(), dataset, rating ) return self.get_ave_item_rating_data( trans.sa_session, dataset ) @@ -706,7 +702,6 @@ redirect_url = kwd['redirect_url'] % urllib.quote_plus( kwd['display_url'] ) except: redirect_url = kwd['redirect_url'] # not all will need custom text - current_user_roles = trans.get_current_user_roles() if trans.app.security_agent.dataset_is_public( data.dataset ): return trans.response.send_redirect( redirect_url ) # anon access already permitted by rbac if self._can_access_dataset( trans, data ): @@ -807,7 +802,6 @@ id = None try: id = trans.app.security.decode_id( dataset_id ) - history = trans.get_history() hda = trans.sa_session.query( self.app.model.HistoryDatasetAssociation ).get( id ) assert hda, 'Invalid HDA: %s' % id # Walk up parent datasets to find the containing history @@ -854,7 +848,7 @@ hda.mark_undeleted() trans.sa_session.flush() trans.log_event( "Dataset id %s has been undeleted" % str(id) ) - except Exception, e: + except Exception: msg = 'HDA undeletion failed (encoded: %s, decoded: %s)' % ( dataset_id, id ) log.exception( msg ) trans.log_event( msg ) @@ -925,7 +919,7 @@ except: log.exception( 'Unable to purge dataset (%s) on purge of HDA (%s):' % ( hda.dataset.id, hda.id ) ) trans.sa_session.flush() - except Exception, e: + except Exception: msg = 'HDA purge failed (encoded: %s, decoded: %s)' % ( dataset_id, id ) log.exception( msg ) trans.log_event( msg ) @@ -1036,7 +1030,6 @@ @web.expose def copy_datasets( self, trans, source_history=None, source_dataset_ids="", target_history_id=None, target_history_ids="", new_history_name="", do_copy=False, **kwd ): - params = util.Params( kwd ) user = trans.get_user() if source_history is not None: history = self.get_history(trans, source_history) https://bitbucket.org/galaxy/galaxy-central/commits/e2839b5b3659/ Changeset: e2839b5b3659 User: dannon Date: 2013-10-04 17:56:52 Summary: merge Affected #: 33 files diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/galaxy/config.py --- a/lib/galaxy/config.py +++ b/lib/galaxy/config.py @@ -132,8 +132,8 @@ self.admin_users = kwargs.get( "admin_users", "" ) self.mailing_join_addr = kwargs.get('mailing_join_addr',"galaxy-announce-join@bx.psu.edu") self.error_email_to = kwargs.get( 'error_email_to', None ) - self.admin_email = kwargs.get( 'admin_email', None ) - self.user_activation_on = kwargs.get( 'user_activation_on', None ) + self.activation_email = kwargs.get( 'activation_email', None ) + self.user_activation_on = string_as_bool( kwargs.get( 'user_activation_on', False ) ) self.activation_grace_period = kwargs.get( 'activation_grace_period', None ) self.inactivity_box_content = kwargs.get( 'inactivity_box_content', None ) self.registration_warning_message = kwargs.get( 'registration_warning_message', None ) @@ -143,10 +143,10 @@ if self.blacklist_location is not None: self.blacklist_file = resolve_path( kwargs.get( 'blacklist_file', None ), self.root ) try: - with open(self.blacklist_file) as blacklist: - self.blacklist_content = [ line.rstrip() for line in blacklist.readlines() ] + with open( self.blacklist_file ) as blacklist: + self.blacklist_content = [ line.rstrip() for line in blacklist.readlines() ] except IOError: - print ( "CONFIGURATION ERROR: Can't open supplied blacklist file from path: " + str( self.blacklist_file ) ) + print ( "CONFIGURATION ERROR: Can't open supplied blacklist file from path: " + str( self.blacklist_file ) ) self.smtp_server = kwargs.get( 'smtp_server', None ) self.smtp_username = kwargs.get( 'smtp_username', None ) self.smtp_password = kwargs.get( 'smtp_password', None ) diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/galaxy/model/__init__.py --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -2351,7 +2351,7 @@ self.tags.append(new_swta) def to_dict( self, view='collection', value_mapper = None ): - rval = super( StoredWorkflow, self ).to_dict(self, view=view, value_mapper = value_mapper) + rval = super( StoredWorkflow, self ).to_dict( view=view, value_mapper = value_mapper ) tags_str_list = [] for tag in self.tags: tag_str = tag.user_tname @@ -3912,7 +3912,7 @@ return [ tool_version.tool_id for tool_version in self.get_versions( app ) ] def to_dict( self, view='element' ): - rval = super( ToolVersion, self ).to_dict( self, view ) + rval = super( ToolVersion, self ).to_dict( view=view ) rval['tool_name'] = self.tool_id for a in self.parent_tool_association: rval['parent_tool_id'] = a.parent_id diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/galaxy/tools/__init__.py --- a/lib/galaxy/tools/__init__.py +++ b/lib/galaxy/tools/__init__.py @@ -11,6 +11,7 @@ import shutil import sys import tempfile +import threading import traceback import types import urllib @@ -1333,19 +1334,16 @@ help_header = "" help_footer = "" if self.help is not None: - # Handle tool help image display for tools that are contained in repositories that are in the tool shed or installed into Galaxy. - # When tool config files use the special string $PATH_TO_IMAGES, the following code will replace that string with the path on disk. - if self.repository_id and self.help.text.find( '$PATH_TO_IMAGES' ) >= 0: - if self.app.name == 'galaxy': - repository = self.sa_session.query( self.app.model.ToolShedRepository ).get( self.app.security.decode_id( self.repository_id ) ) - if repository: - path_to_images = '/tool_runner/static/images/%s' % self.repository_id - self.help.text = self.help.text.replace( '$PATH_TO_IMAGES', path_to_images ) - elif self.app.name == 'tool_shed': - repository = self.sa_session.query( self.app.model.Repository ).get( self.app.security.decode_id( self.repository_id ) ) - if repository: - path_to_images = '/repository/static/images/%s' % self.repository_id - self.help.text = self.help.text.replace( '$PATH_TO_IMAGES', path_to_images ) + if self.repository_id and self.help.text.find( '.. image:: ' ) >= 0: + # Handle tool help image display for tools that are contained in repositories in the tool shed or installed into Galaxy. + lock = threading.Lock() + lock.acquire( True ) + try: + self.help.text = shed_util_common.set_image_paths( self.app, self.repository_id, self.help.text ) + except Exception, e: + log.exception( "Exception in parse_help, so images may not be properly displayed:\n%s" % str( e ) ) + finally: + lock.release() help_pages = self.help.findall( "page" ) help_header = self.help.text try: diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/galaxy/web/form_builder.py --- a/lib/galaxy/web/form_builder.py +++ b/lib/galaxy/web/form_builder.py @@ -756,13 +756,6 @@ refresh_on_change=refresh_on_change, refresh_on_change_values=refresh_on_change_values, size=size ) - if display is None and initial_value == 'none': - # Only insert an initial "Select one" option if we are not displaying check boxes - # or radio buttons and we have not received an initial_value other than 'none'. - if selected_value == initial_value: - select_field.add_option( 'Select one', initial_value, selected=True ) - else: - select_field.add_option( 'Select one', initial_value ) for obj in objs: if label_attr == 'self': # Each obj is a string diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/galaxy/webapps/galaxy/buildapp.py --- a/lib/galaxy/webapps/galaxy/buildapp.py +++ b/lib/galaxy/webapps/galaxy/buildapp.py @@ -46,8 +46,12 @@ atexit.register( app.shutdown ) # Create the universe WSGI application webapp = GalaxyWebApplication( app, session_cookie='galaxysession', name='galaxy' ) - # The following route will handle displaying tool shelp images for tools contained in repositories installed from the tool shed. - webapp.add_route( '/tool_runner/static/images/:repository_id/:image_file', controller='tool_runner', action='display_tool_help_image_in_repository', repository_id=None, image_file=None ) + # 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' ) diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 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 @@ -285,6 +285,30 @@ status=status ) @web.expose + def display_image_in_repository( self, trans, **kwd ): + """ + Open an image file that is contained in an installed tool shed repository or that is referenced by a URL for display. The + image can be defined in either a README.rst file contained in the repository or the help section of a Galaxy tool config that + is contained in the repository. The following image definitions are all supported. The former $PATH_TO_IMAGES is no longer + required, and is now ignored. + .. image:: https://raw.github.com/galaxy/some_image.png + .. image:: $PATH_TO_IMAGES/some_image.png + .. image:: /static/images/some_image.gif + .. image:: some_image.jpg + .. image:: /deep/some_image.png + """ + repository_id = kwd.get( 'repository_id', None ) + relative_path_to_image_file = kwd.get( 'image_file', None ) + if repository_id and relative_path_to_image_file: + repository = suc.get_tool_shed_repository_by_id( trans, repository_id ) + if repository: + repo_files_dir = repository.repo_files_directory( trans.app ) + path_to_file = suc.get_absolute_path_to_file_in_repository( repo_files_dir, relative_path_to_image_file ) + if os.path.exists( path_to_file ): + return open( path_to_file, 'r' ) + return None + + @web.expose @web.require_admin def find_tools_in_tool_shed( self, trans, **kwd ): tool_shed_url = kwd[ 'tool_shed_url' ] diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 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 @@ -98,22 +98,6 @@ **vars ) @web.expose - def display_tool_help_image_in_repository( self, trans, **kwd ): - repository_id = kwd.get( 'repository_id', None ) - image_file = kwd.get( 'image_file', None ) - if repository_id and image_file: - repository = suc.get_tool_shed_repository_by_id( trans, repository_id ) - repo_files_dir = os.path.join( repository.repo_files_directory( trans.app ) ) - default_path = os.path.abspath( os.path.join( repo_files_dir, 'static', 'images', image_file ) ) - if os.path.exists( default_path ): - return open( default_path, 'r' ) - else: - path_to_file = suc.get_absolute_path_to_file_in_repository( repo_files_dir, image_file ) - if os.path.exists( path_to_file ): - return open( path_to_file, 'r' ) - return None - - @web.expose def rerun( self, trans, id=None, from_noframe=None, **kwd ): """ Given a HistoryDatasetAssociation id, find the job and that created diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/galaxy/webapps/galaxy/controllers/user.py --- a/lib/galaxy/webapps/galaxy/controllers/user.py +++ b/lib/galaxy/webapps/galaxy/controllers/user.py @@ -491,12 +491,12 @@ message = "No such user (please note that login is case sensitive)" elif user.deleted: message = "This account has been marked deleted, contact your local Galaxy administrator to restore the account." - if trans.app.config.admin_email is not None: - message += 'Contact: %s' % trans.app.config.admin_email + if trans.app.config.error_email_to is not None: + message += ' Contact: %s' % trans.app.config.error_email_to elif user.external: message = "This account was created for use with an external authentication method, contact your local Galaxy administrator to activate it." - if trans.app.config.admin_email is not None: - message += 'Contact: %s' % trans.app.config.admin_email + if trans.app.config.error_email_to is not None: + message += ' Contact: %s' % trans.app.config.error_email_to elif not user.check_password( password ): message = "Invalid password" elif trans.app.config.user_activation_on and not user.active: # activation is ON and the user is INACTIVE @@ -536,8 +536,8 @@ message = 'This account has not been activated yet. The activation link has been sent again. Please check your email address %s.<br>' % email else: message = 'This account has not been activated yet but we are unable to send the activation link. Please contact your local Galaxy administrator.' - if trans.app.config.admin_email is not None: - message += 'Contact: %s' % trans.app.config.admin_email + if trans.app.config.error_email_to is not None: + message += ' Contact: %s' % trans.app.config.error_email_to return message def is_outside_grace_period ( self, trans, create_time ): @@ -591,8 +591,8 @@ is_admin = cntrller == 'admin' and trans.user_is_admin if not trans.app.config.allow_user_creation and not trans.user_is_admin(): message = 'User registration is disabled. Please contact your local Galaxy administrator for an account.' - if trans.app.config.admin_email is not None: - message += 'Contact: %s' % trans.app.config.admin_email + if trans.app.config.error_email_to is not None: + message += ' Contact: %s' % trans.app.config.error_email_to status = 'error' else: if not refresh_frames: @@ -670,8 +670,10 @@ user = trans.app.model.User( email=email ) user.set_password_cleartext( password ) user.username = username - if trans.app.config.user_activation_on: # Do not set the active flag in case activation is OFF. + if trans.app.config.user_activation_on: user.active = False + else: + user.active = True # Activation is off, every new user is active by default. trans.sa_session.add( user ) trans.sa_session.flush() trans.app.security_agent.create_private_user_role( user ) @@ -733,8 +735,8 @@ success = True else: message = 'Unable to send activation email, please contact your local Galaxy administrator.' - if trans.app.config.admin_email is not None: - message += 'Contact: %s' % trans.app.config.admin_email + if trans.app.config.error_email_to is not None: + message += ' Contact: %s' % trans.app.config.error_email_to success = False else: # User activation is OFF, proceed without sending the activation email. message = 'Now logged in as %s.<br><a target="_top" href="%s">Return to the home page.</a>' % ( user.email, url_for( '/' ) ) @@ -752,7 +754,7 @@ "Activation link: %s \n\n" "Your Galaxy Team" % ( email, activation_link )) to = email - frm = trans.app.config.admin_email + frm = trans.app.config.activation_email subject = 'How to activate your Galaxy account' try: util.send_mail( frm, to, subject, body, trans.app.config ) diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/galaxy/webapps/tool_shed/buildapp.py --- a/lib/galaxy/webapps/tool_shed/buildapp.py +++ b/lib/galaxy/webapps/tool_shed/buildapp.py @@ -69,8 +69,12 @@ webapp.add_route( '/view/{owner}', controller='repository', action='sharable_owner' ) webapp.add_route( '/view/{owner}/{name}', controller='repository', action='sharable_repository' ) webapp.add_route( '/view/{owner}/{name}/{changeset_revision}', controller='repository', action='sharable_repository_revision' ) - # The following route will handle displaying tool shelp images for tools contained in repositories. - webapp.add_route( '/repository/static/images/:repository_id/:image_file', controller='repository', action='display_tool_help_image_in_repository', repository_id=None, image_file=None ) + # Handle displaying tool help images and README file images for tools contained in repositories. + webapp.add_route( '/repository/static/images/:repository_id/:image_file', + controller='repository', + action='display_image_in_repository', + repository_id=None, + image_file=None ) webapp.add_route( '/:controller/:action', action='index' ) webapp.add_route( '/:action', controller='repository', action='index' ) webapp.add_route( '/repos/*path_info', controller='hg', action='handle_request', path_info='/' ) diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 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 @@ -666,7 +666,7 @@ # Update repository files for browsing. suc.update_repository( repo ) changeset_revision = repository.tip( trans.app ) - metadata = self.get_metadata( trans, id, changeset_revision ) + metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans, id, changeset_revision, metadata_only=True ) repository_type_select_field = rt_util.build_repository_type_select_field( trans, repository=repository ) return trans.fill_template( '/webapps/tool_shed/repository/browse_repository.mako', repository=repository, @@ -897,7 +897,10 @@ message = kwd.get( 'message', '' ) status = kwd.get( 'status', 'done' ) repository = suc.get_repository_in_tool_shed( trans, id ) - metadata = self.get_metadata( trans, id, repository.tip( trans.app ) ) + metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans, + id, + repository.tip( trans.app ), + metadata_only=True ) if trans.user and trans.user.email: return trans.fill_template( "/webapps/tool_shed/repository/contact_owner.mako", repository=repository, @@ -1015,6 +1018,29 @@ status=status ) ) @web.expose + def display_image_in_repository( self, trans, **kwd ): + """ + Open an image file that is contained in repository or that is referenced by a URL for display. The image can be defined in + either a README.rst file contained in the repository or the help section of a Galaxy tool config that is contained in the repository. + The following image definitions are all supported. The former $PATH_TO_IMAGES is no longer required, and is now ignored. + .. image:: https://raw.github.com/galaxy/some_image.png + .. image:: $PATH_TO_IMAGES/some_image.png + .. image:: /static/images/some_image.gif + .. image:: some_image.jpg + .. image:: /deep/some_image.png + """ + repository_id = kwd.get( 'repository_id', None ) + relative_path_to_image_file = kwd.get( 'image_file', None ) + if repository_id and relative_path_to_image_file: + repository = suc.get_repository_in_tool_shed( trans, repository_id ) + if repository: + repo_files_dir = repository.repo_path( trans.app ) + path_to_file = suc.get_absolute_path_to_file_in_repository( repo_files_dir, relative_path_to_image_file ) + if os.path.exists( path_to_file ): + return open( path_to_file, 'r' ) + return None + + @web.expose def display_tool( self, trans, repository_id, tool_config, changeset_revision, **kwd ): message = kwd.get( 'message', '' ) status = kwd.get( 'status', 'done' ) @@ -1023,7 +1049,10 @@ if message: status = 'error' tool_state = tool_util.new_state( trans, tool, invalid=False ) - metadata = self.get_metadata( trans, repository_id, changeset_revision ) + metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans, + repository_id, + changeset_revision, + metadata_only=True ) try: return trans.fill_template( "/webapps/tool_shed/repository/tool_form.mako", repository=repository, @@ -1052,22 +1081,6 @@ status='error' ) ) @web.expose - def display_tool_help_image_in_repository( self, trans, **kwd ): - repository_id = kwd.get( 'repository_id', None ) - image_file = kwd.get( 'image_file', None ) - if repository_id and image_file: - repository = suc.get_repository_in_tool_shed( trans, repository_id ) - repo_files_dir = os.path.join( repository.repo_path( trans.app ), repository.name ) - default_path = os.path.abspath( os.path.join( repo_files_dir, 'static', 'images', image_file ) ) - if os.path.exists( default_path ): - return open( default_path, 'r' ) - else: - path_to_file = suc.get_absolute_path_to_file_in_repository( repo_files_dir, image_file ) - if os.path.exists( path_to_file ): - return open( path_to_file, 'r' ) - return None - - @web.expose def download( self, trans, repository_id, changeset_revision, file_type, **kwd ): """Download an archive of the repository files compressed as zip, gz or bz2.""" # FIXME: thgis will currently only download the repository tip, no matter which installable changeset_revision is being viewed. @@ -1571,12 +1584,6 @@ pubdate=strftime( '%a, %d %b %Y %H:%M:%S UT', gmtime() ), items=functional_test_results ) - def get_metadata( self, trans, repository_id, changeset_revision ): - repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans, repository_id, changeset_revision ) - if repository_metadata and repository_metadata.metadata: - return repository_metadata.metadata - return None - @web.json def get_readme_files( self, trans, **kwd ): """ @@ -2409,7 +2416,10 @@ avg_rating, num_ratings = self.get_ave_item_rating_data( trans.sa_session, repository, webapp_model=trans.model ) display_reviews = util.string_as_bool( kwd.get( 'display_reviews', False ) ) rra = self.get_user_item_rating( trans.sa_session, trans.user, repository, webapp_model=trans.model ) - metadata = self.get_metadata( trans, id, repository.tip( trans.app ) ) + metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans, + id, + repository.tip( trans.app ), + metadata_only=True ) repository_type_select_field = rt_util.build_repository_type_select_field( trans, repository=repository ) return trans.fill_template( '/webapps/tool_shed/repository/rate_repository.mako', repository=repository, @@ -2806,7 +2816,10 @@ 'has_metadata' : has_metadata } # Make sure we'll view latest changeset first. changesets.insert( 0, change_dict ) - metadata = self.get_metadata( trans, id, repository.tip( trans.app ) ) + metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans, + id, + repository.tip( trans.app ), + metadata_only=True ) return trans.fill_template( '/webapps/tool_shed/repository/view_changelog.mako', repository=repository, metadata=metadata, @@ -2839,7 +2852,7 @@ diffs = [] for diff in patch.diff( repo, node1=ctx_parent.node(), node2=ctx.node() ): diffs.append( suc.to_html_string( diff ) ) - metadata = self.get_metadata( trans, id, ctx_str ) + metadata = metadata_util.get_repository_metadata_by_repository_id_changeset_revision( trans, id, ctx_str, metadata_only=True ) # For rendering the prev button. if ctx_parent: ctx_parent_rev = ctx_parent.rev() diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py --- a/lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py +++ b/lib/tool_shed/galaxy_install/tool_dependencies/fabric_util.py @@ -284,7 +284,8 @@ elif action_type == 'move_file': td_common_util.move_file( current_dir=current_dir, source=os.path.join( action_dict[ 'source' ] ), - destination_dir=os.path.join( action_dict[ 'destination' ] ) ) + destination=os.path.join( action_dict[ 'destination' ] ), + rename_to=action_dict[ 'rename_to' ] ) elif action_type == 'set_environment': # Currently the only action supported in this category is "environment_variable". # Build a command line from the prior_installation_required, in case an environment variable is referenced diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 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 @@ -464,7 +464,7 @@ is_binary_download = True else: is_binary_download = False - elif action_elem: + elif action_elem is not None: # We were provided with a single <action> element to perform certain actions after a platform-specific tarball was downloaded. elems = [ action_elem ] else: @@ -550,11 +550,7 @@ action_dict[ 'directory' ] = action_elem.text else: continue - elif action_type in [ 'move_directory_files', 'move_file' ]: - # <action type="move_file"> - # <source>misc/some_file</source> - # <destination>$INSTALL_DIR/bin</destination> - # </action> + elif action_type == 'move_directory_files': # <action type="move_directory_files"> # <source_directory>bin</source_directory> # <destination_directory>$INSTALL_DIR/bin</destination_directory> @@ -563,6 +559,14 @@ move_elem_text = evaluate_template( move_elem.text ) if move_elem_text: action_dict[ move_elem.tag ] = move_elem_text + elif action_type == 'move_file': + # <action type="move_file" rename_to="new_file_name"> + # <source>misc/some_file</source> + # <destination>$INSTALL_DIR/bin</destination> + # </action> + action_dict[ 'source' ] = evaluate_template( action_elem.find( 'source' ).text ) + action_dict[ 'destination' ] = evaluate_template( action_elem.find( 'destination' ).text ) + action_dict[ 'rename_to' ] = action_elem.get( 'rename_to' ) elif action_type == 'set_environment': # <action type="set_environment"> # <environment_variable name="PYTHONPATH" action="append_to">$INSTALL_DIR/lib/python</environment_variable> diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py --- a/lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py +++ b/lib/tool_shed/galaxy_install/tool_dependencies/td_common_util.py @@ -12,6 +12,109 @@ log = logging.getLogger( __name__ ) + +class CompressedFile( object ): + + def __init__( self, file_path, mode='r' ): + if istar( file_path ): + self.file_type = 'tar' + elif iszip( file_path ) and not isjar( file_path ): + self.file_type = 'zip' + self.file_name = os.path.splitext( file_path )[ 0 ] + if self.file_name.endswith( '.tar' ): + self.file_name = os.path.splitext( self.file_name )[ 0 ] + self.type = self.file_type + method = 'open_%s' % self.file_type + if hasattr( self, method ): + self.archive = getattr( self, method )( file_path, mode ) + else: + raise NameError( 'File type %s specified, no open method found.' % self.file_type ) + + def extract( self, path ): + full_path = self.extraction_path( path ) + self.archive.extractall( full_path ) + return full_path + + def extraction_path( self, path ): + '''Determine the path to which the archive should be extracted.''' + contents = self.getmembers() + extraction_path = path + if len( contents ) == 1: + # The archive contains a single file, return the extraction path. + if self.isfile( contents[ 0 ] ): + extraction_path = os.path.join( path, self.file_name ) + else: + # Sort filenames within the archive by length, because if the shortest path entry in the archive is a directory, + # and the next entry has the directory prepended, then everything following that entry must be within that directory. + # For example, consider tarball for BWA 0.5.9: + # bwa-0.5.9.tar.bz2: + # bwa-0.5.9/ + # bwa-0.5.9/bwt.c + # bwa-0.5.9/bwt.h + # bwa-0.5.9/Makefile + # bwa-0.5.9/bwt_gen/ + # bwa-0.5.9/bwt_gen/Makefile + # bwa-0.5.9/bwt_gen/bwt_gen.c + # bwa-0.5.9/bwt_gen/bwt_gen.h + # When sorted by length, one sees a directory at the root of the tarball, and all other tarball contents as + # children of that directory. + filenames = sorted( [ self.getname( item ) for item in contents ], cmp=lambda a,b: cmp( len( a ), len( b ) ) ) + parent_name = filenames[ 0 ] + parent = self.getmember( parent_name ) + first_child = filenames[ 1 ] + if first_child.startswith( parent_name ) and parent is not None and self.isdir( parent ): + extraction_path = os.path.join( path, self.getname( parent ) ) + else: + extraction_path = os.path.join( path, self.file_name ) + if not os.path.exists( extraction_path ): + os.makedirs( extraction_path ) + return os.path.abspath( extraction_path ) + + def getmembers_tar( self ): + return self.archive.getmembers() + + def getmembers_zip( self ): + return self.archive.infolist() + + def getname_tar( self, item ): + return item.name + + def getname_zip( self, item ): + return item.filename + + def getmember( self, name ): + for member in self.getmembers(): + if self.getname( member ) == name: + return member + + def getmembers( self ): + return getattr( self, 'getmembers_%s' % self.type )() + + def getname( self, member ): + return getattr( self, 'getname_%s' % self.type )( member ) + + def isdir( self, member ): + return getattr( self, 'isdir_%s' % self.type )( member ) + + def isdir_tar( self, member ): + return member.isdir() + + def isdir_zip( self, member ): + if member.filename.endswith( os.sep ): + return True + return False + + def isfile( self, member ): + if not self.isdir( member ): + return True + return False + + def open_tar( self, filepath, mode ): + return tarfile.open( filepath, mode ) + + def open_zip( self, filepath, mode ): + return zipfile.ZipFile( filepath, mode ) + def clean_tool_shed_url( base_url ): if base_url: if base_url.find( '://' ) > -1: @@ -92,30 +195,6 @@ dir = url_download( work_dir, downloaded_filename, url, extract=False ) return downloaded_filename -def extract_tar( file_name, file_path ): - if isgzip( file_name ) or isbz2( file_name ): - # Open for reading with transparent compression. - tar = tarfile.open( file_name, 'r:*', errorlevel=0 ) - else: - tar = tarfile.open( file_name, errorlevel=0 ) - tar.extractall( path=file_path ) - tar.close() - -def extract_zip( archive_path, extraction_path ): - # TODO: change this method to use zipfile.Zipfile.extractall() when we stop supporting Python 2.5. - if not zipfile_ok( archive_path ): - return False - zip_archive = zipfile.ZipFile( archive_path, 'r' ) - for name in zip_archive.namelist(): - uncompressed_path = os.path.join( extraction_path, name ) - if uncompressed_path.endswith( '/' ): - if not os.path.isdir( uncompressed_path ): - os.makedirs( uncompressed_path ) - else: - file( uncompressed_path, 'wb' ).write( zip_archive.read( name ) ) - zip_archive.close() - return True - def format_traceback(): ex_type, ex, tb = sys.exc_info() return ''.join( traceback.format_tb( tb ) ) @@ -232,12 +311,18 @@ destination_file = os.path.join( destination_directory, file_name ) shutil.move( source_file, destination_file ) -def move_file( current_dir, source, destination_dir ): +def move_file( current_dir, source, destination, rename_to=None ): source_file = os.path.abspath( os.path.join( current_dir, source ) ) - destination_directory = os.path.join( destination_dir ) - if not os.path.isdir( destination_directory ): + if rename_to is not None: + destination_file = rename_to + destination_directory = os.path.join( destination ) + destination_path = os.path.join( destination_directory, destination_file ) + else: + destination_directory = os.path.join( destination ) + destination_path = os.path.join( destination_directory, source_file ) + if not os.path.exists( destination_directory ): os.makedirs( destination_directory ) - shutil.move( source_file, destination_directory ) + shutil.move( source_file, destination_path ) def parse_package_elem( package_elem, platform_info_dict=None, include_after_install_actions=True ): """ @@ -340,19 +425,6 @@ continue return actions_elem_tuples -def tar_extraction_directory( file_path, file_name ): - """Try to return the correct extraction directory.""" - file_name = file_name.strip() - extensions = [ '.tar.gz', '.tgz', '.tar.bz2', '.tar', '.zip' ] - for extension in extensions: - if file_name.find( extension ) > 0: - dir_name = file_name[ :-len( extension ) ] - if os.path.exists( os.path.abspath( os.path.join( file_path, dir_name ) ) ): - return dir_name - if os.path.exists( os.path.abspath( os.path.join( file_path, file_name ) ) ): - return os.path.abspath( file_path ) - raise ValueError( 'Could not find path to file %s' % os.path.abspath( os.path.join( file_path, file_name ) ) ) - def url_download( install_dir, downloaded_file_name, download_url, extract=True ): file_path = os.path.join( install_dir, downloaded_file_name ) src = None @@ -374,32 +446,14 @@ if dst: dst.close() if extract: - if istar( file_path ): - # <action type="download_by_url">http://sourceforge.net/projects/samtools/files/samtools/0.1.18/samtools-0.1.18.tar.bz2</action> - extract_tar( file_path, install_dir ) - dir = tar_extraction_directory( install_dir, downloaded_file_name ) - elif isjar( file_path ): - dir = os.path.curdir - elif iszip( file_path ): - # <action type="download_by_url">http://downloads.sourceforge.net/project/picard/picard-tools/1.56/picard-tools-1.56.zip</action> - zip_archive_extracted = extract_zip( file_path, install_dir ) - dir = zip_extraction_directory( install_dir, downloaded_file_name ) + if istar( file_path ) or iszip( file_path ): + archive = CompressedFile( file_path ) + extraction_path = archive.extract( install_dir ) else: - dir = os.path.abspath( install_dir ) + extraction_path = os.path.abspath( install_dir ) else: - dir = os.path.abspath( install_dir ) - return dir - -def zip_extraction_directory( file_path, file_name ): - """Try to return the correct extraction directory.""" - files = [ filename for filename in os.listdir( file_path ) if not filename.endswith( '.zip' ) ] - if len( files ) > 1: - return os.path.abspath( file_path ) - elif len( files ) == 1: - # If there is only on file it should be a directory. - if os.path.isdir( os.path.join( file_path, files[ 0 ] ) ): - return os.path.abspath( os.path.join( file_path, files[ 0 ] ) ) - raise ValueError( 'Could not find directory for the extracted file %s' % os.path.abspath( os.path.join( file_path, file_name ) ) ) + extraction_path = os.path.abspath( install_dir ) + return extraction_path def zipfile_ok( path_to_archive ): """ diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/tool_shed/util/metadata_util.py --- a/lib/tool_shed/util/metadata_util.py +++ b/lib/tool_shed/util/metadata_util.py @@ -710,8 +710,16 @@ fp = open( relative_path, 'rb' ) workflow_text = fp.read() fp.close() - exported_workflow_dict = json.from_json_string( workflow_text ) - if 'a_galaxy_workflow' in exported_workflow_dict and exported_workflow_dict[ 'a_galaxy_workflow' ] == 'true': + if workflow_text: + valid_exported_galaxy_workflow = True + try: + exported_workflow_dict = json.from_json_string( workflow_text ) + except Exception, e: + log.exception( "Skipping file %s since it does not seem to be a valid exported Galaxy workflow: %s" \ + % str( relative_path ), str( e ) ) + valid_exported_galaxy_workflow = False + if valid_exported_galaxy_workflow and \ + 'a_galaxy_workflow' in exported_workflow_dict and exported_workflow_dict[ 'a_galaxy_workflow' ] == 'true': metadata_dict = generate_workflow_metadata( relative_path, exported_workflow_dict, metadata_dict ) # Handle any data manager entries metadata_dict = generate_data_manager_metadata( app, @@ -1064,13 +1072,15 @@ """Get repository metadata from the database""" return trans.sa_session.query( trans.model.RepositoryMetadata ).get( trans.security.decode_id( id ) ) -def get_repository_metadata_by_repository_id_changeset_revision( trans, id, changeset_revision ): - """Get a specified metadata record for a specified repository.""" - return trans.sa_session.query( trans.model.RepositoryMetadata ) \ - .filter( and_( trans.model.RepositoryMetadata.table.c.repository_id == trans.security.decode_id( id ), - trans.model.RepositoryMetadata.table.c.changeset_revision == changeset_revision ) ) \ - .first() - +def get_repository_metadata_by_repository_id_changeset_revision( trans, id, changeset_revision, metadata_only=False ): + """Get a specified metadata record for a specified repository in the tool shed.""" + if metadata_only: + repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans, id, changeset_revision ) + if repository_metadata and repository_metadata.metadata: + return repository_metadata.metadata + return None + return suc.get_repository_metadata_by_changeset_revision( trans, id, changeset_revision ) + def get_repository_metadata_revisions_for_review( repository, reviewed=True ): repository_metadata_revisions = [] metadata_changeset_revision_hashes = [] diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/tool_shed/util/readme_util.py --- a/lib/tool_shed/util/readme_util.py +++ b/lib/tool_shed/util/readme_util.py @@ -1,7 +1,11 @@ import logging import os +import threading +from mako.template import Template from galaxy import eggs +from galaxy import web from galaxy.util import json +from galaxy.util import rst_to_html from galaxy.util import unicodify import tool_shed.util.shed_util_common as suc from tool_shed.util import common_util @@ -34,13 +38,39 @@ full_path_to_readme_file = os.path.abspath( os.path.join( tool_path, relative_path_to_readme_file ) ) else: full_path_to_readme_file = os.path.abspath( relative_path_to_readme_file ) + text = None try: f = open( full_path_to_readme_file, 'r' ) text = unicodify( f.read() ) f.close() - readme_files_dict[ readme_file_name ] = suc.size_string( text ) except Exception, e: log.exception( "Error reading README file '%s' from disk: %s" % ( str( relative_path_to_readme_file ), str( e ) ) ) + text = None + if text: + text_of_reasonable_length = suc.size_string( text ) + if text_of_reasonable_length.find( '.. image:: ' ) >= 0: + # Handle image display for README files that are contained in repositories in the tool shed or installed into Galaxy. + lock = threading.Lock() + lock.acquire( True ) + try: + text_of_reasonable_length = suc.set_image_paths( trans.app, + trans.security.encode_id( repository.id ), + text_of_reasonable_length ) + except Exception, e: + log.exception( "Exception in build_readme_files_dict, so images may not be properly displayed:\n%s" % str( e ) ) + finally: + lock.release() + if readme_file_name.endswith( '.rst' ): + text_of_reasonable_length = Template( rst_to_html( text_of_reasonable_length ), + input_encoding='utf-8', + output_encoding='utf-8', + default_filters=[ 'decode.utf8' ], + encoding_errors='replace' ) + text_of_reasonable_length = text_of_reasonable_length.render( static_path=web.url_for( '/static' ), + host_url=web.url_for( '/', qualified=True ) ) + else: + text_of_reasonable_length = suc.to_html_string( text_of_reasonable_length ) + readme_files_dict[ readme_file_name ] = text_of_reasonable_length else: # We must be in the tool shed and have an old changeset_revision, so we need to retrieve the file contents from the repository manifest. ctx = suc.get_changectx_for_changeset( repo, changeset_revision ) diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 lib/tool_shed/util/shed_util_common.py --- a/lib/tool_shed/util/shed_util_common.py +++ b/lib/tool_shed/util/shed_util_common.py @@ -1,5 +1,6 @@ import logging import os +import re import shutil import string import tempfile @@ -1504,6 +1505,30 @@ """Return a reversed list of changesets in the repository changelog up to and including the included_upper_bounds_changeset_revision.""" return reversed_lower_upper_bounded_changelog( repo, INITIAL_CHANGELOG_HASH, included_upper_bounds_changeset_revision ) +def set_image_paths( app, encoded_repository_id, text ): + """ + Handle tool help image display for tools that are contained in repositories in the tool shed or installed into Galaxy as well as image + display in repository README files. This method will determine the location of the image file and return the path to it that will enable + the caller to open the file. + """ + if text: + if app.name == 'galaxy': + route_to_images = '/admin_toolshed/static/images/%s' % encoded_repository_id + else: + # We're in the tool shed. + route_to_images = '/repository/static/images/%s' % encoded_repository_id + # We used to require $PATH_TO_IMAGES, but we now eliminate it if it's used. + text = text.replace( '$PATH_TO_IMAGES', '' ) + # Eliminate the invalid setting of ./static/images since the routes will properly display images contained in that directory. + text = text.replace( './static/images', '' ) + # Eliminate the default setting of /static/images since the routes will properly display images contained in that directory. + text = text.replace( '/static/images', '' ) + # Use regex to instantiate routes into the defined image paths. + for match in re.findall( '.. image:: (?!http)/?(.+)', text ): + text = text.replace( match, match.replace( '/', '%2F' ) ) + text = re.sub( '.. image:: (?!http)/?(.+)', '.. image:: %s/\\1' % route_to_images, text ) + return text + def set_only_if_compiling_contained_td( repository, required_repository ): """Return True if the received required_repository is only needed to compile a tool dependency defined for the received repository.""" # This method is called only from Galaxy when rendering repository dependencies for an installed tool shed repository. diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 static/scripts/galaxy.base.js --- a/static/scripts/galaxy.base.js +++ b/static/scripts/galaxy.base.js @@ -175,18 +175,14 @@ // if theres confirm text, send the dialog if ( !confirmtext || confirm( confirmtext ) ) { - var f; - // Http request target is a window named demolocal on the local box - if ( target === "demo" ) { - if ( f === undefined || f.closed ) { - f = window.open( href,target ); - f.creator = self; - } - + // link.click() doesn't use target for some reason, + // so manually do it here. + if (target) { + window.open(href, target); } // For all other links, do the default action. else { - link.trigger('click'); + link.click(); } } } diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 static/scripts/galaxy.grids.js --- a/static/scripts/galaxy.grids.js +++ b/static/scripts/galaxy.grids.js @@ -255,7 +255,8 @@ }); // Initialize ratings. - $('.community_rating_star').rating({}); + if ($('.community_rating_star').length !== 0) + $('.community_rating_star').rating({}); // Initialize item menu operations. make_popup_menus(); diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 static/scripts/mvc/data.js --- a/static/scripts/mvc/data.js +++ b/static/scripts/mvc/data.js @@ -179,17 +179,17 @@ // -- Helper functions. -- _renderCell: function(cell_contents, index, colspan) { + var $cell = $('<td>').text(cell_contents); var column_types = this.model.get_metadata('column_types'); if (colspan !== undefined) { - return $('<td>').attr('colspan', colspan).addClass('stringalign').text(cell_contents); + $cell.attr('colspan', colspan).addClass('stringalign'); + } else if (column_types !== undefined && index < column_types.length) { + if (column_types[index] === 'str' || column_types === 'list') { + /* Left align all str columns, right align the rest */ + $cell.addClass('stringalign'); + } } - else if (column_types[index] === 'str' || column_types === 'list') { - /* Left align all str columns, right align the rest */ - return $('<td>').addClass('stringalign').text(cell_contents); - } - else { - return $('<td>').text(cell_contents); - } + return $cell; }, _renderRow: function(line) { @@ -416,49 +416,54 @@ error: function() { alert( ( "Could not add this dataset to browser" ) + '.' ); }, success: function(table_html) { var parent = window.parent; + parent.Galaxy.modal.show({ + title : "View Data in a New or Saved Visualization", + buttons :{ + "Cancel": function(){ + parent.Galaxy.modal.hide(); + }, + "View in saved visualization": function(){ + // Show new modal with saved visualizations. + parent.Galaxy.modal.show( + { + title: "Add Data to Saved Visualization", + body: table_html, + buttons :{ + "Cancel": function(){ + parent.Galaxy.modal.hide(); + }, + "Add to visualization": function(){ + $(parent.document).find('input[name=id]:checked').each(function(){ + // hide + parent.Galaxy.modal.hide(); + + var vis_id = $(this).val(); + dataset_params.id = vis_id; + + // add widget + parent.Galaxy.frame_manager.frame_new({ + title : "Trackster", + type : "url", + content : vis_url + "/trackster?" + $.param(dataset_params) + }); + }); + } + } + }); + }, + "View in new visualization": function(){ + // hide + parent.Galaxy.modal.hide(); + + var url = vis_url + "/trackster?" + $.param(dataset_params); - parent.show_modal( ( "View Data in a New or Saved Visualization" ), "", { - "Cancel": function() { - parent.hide_modal(); - }, - "View in saved visualization": function() { - // Show new modal with saved visualizations. - parent.show_modal( ( "Add Data to Saved Visualization" ), table_html, { - "Cancel": function() { - parent.hide_modal(); - }, - "Add to visualization": function() { - $(parent.document).find('input[name=id]:checked').each(function() { - var vis_id = $(this).val(); - dataset_params.id = vis_id; - - // add widget - parent.Galaxy.frame_manager.frame_new( - { - title : "Trackster", - type : "url", - content : vis_url + "/trackster?" + $.param(dataset_params) - }); - - // hide - parent.hide_modal(); - }); - } - }); - }, - "View in new visualization": function() { - var url = vis_url + "/trackster?" + $.param(dataset_params); - - // add widget - parent.Galaxy.frame_manager.frame_new( - { - title : "Trackster", - type : "url", - content : url - }); - - // hide - parent.hide_modal(); + // add widget + parent.Galaxy.frame_manager.frame_new({ + title : "Trackster", + type : "url", + content : url + }); + } } }); } diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 static/scripts/mvc/dataset/hda-edit.js --- a/static/scripts/mvc/dataset/hda-edit.js +++ b/static/scripts/mvc/dataset/hda-edit.js @@ -635,67 +635,77 @@ * @returns function that displays modal, loads trackster */ //TODO: should be imported from trackster.js -function create_trackster_action_fn(vis_url, dataset_params, dbkey) { - return function() { - var listTracksParams = {}; - if (dbkey){ - // list_tracks seems to use 'f-dbkey' (??) - listTracksParams[ 'f-dbkey' ] = dbkey; - } - $.ajax({ - url: vis_url + '/list_tracks?' + $.param( listTracksParams ), - dataType: "html", - error: function() { alert( _l( "Could not add this dataset to browser" ) + '.' ); }, - success: function(table_html) { - var parent = window.parent; +//TODO: This function is redundant and also exists in data.js + // create action + function create_trackster_action_fn (vis_url, dataset_params, dbkey) { + return function() { + var listTracksParams = {}; + if (dbkey){ + // list_tracks seems to use 'f-dbkey' (??) + listTracksParams[ 'f-dbkey' ] = dbkey; + } + $.ajax({ + url: vis_url + '/list_tracks?' + $.param( listTracksParams ), + dataType: "html", + error: function() { alert( ( "Could not add this dataset to browser" ) + '.' ); }, + success: function(table_html) { + var parent = window.parent; + parent.Galaxy.modal.show({ + title : "View Data in a New or Saved Visualization", + buttons :{ + "Cancel": function(){ + parent.Galaxy.modal.hide(); + }, + "View in saved visualization": function(){ + // Show new modal with saved visualizations. + parent.Galaxy.modal.show( + { + title: "Add Data to Saved Visualization", + body: table_html, + buttons :{ + "Cancel": function(){ + parent.Galaxy.modal.hide(); + }, + "Add to visualization": function(){ + $(parent.document).find('input[name=id]:checked').each(function(){ + // hide + parent.Galaxy.modal.hide(); + + var vis_id = $(this).val(); + dataset_params.id = vis_id; + + // add widget + parent.Galaxy.frame_manager.frame_new({ + title : "Trackster", + type : "url", + content : vis_url + "/trackster?" + $.param(dataset_params) + }); + }); + } + } + }); + }, + "View in new visualization": function(){ + // hide + parent.Galaxy.modal.hide(); + + var url = vis_url + "/trackster?" + $.param(dataset_params); - parent.show_modal( _l( "View Data in a New or Saved Visualization" ), "", { - "Cancel": function() { - parent.hide_modal(); - }, - "View in saved visualization": function() { - // Show new modal with saved visualizations. - parent.show_modal( _l( "Add Data to Saved Visualization" ), table_html, { - "Cancel": function() { - parent.hide_modal(); - }, - "Add to visualization": function() { - $(parent.document).find('input[name=id]:checked').each(function() { - var vis_id = $(this).val(); - dataset_params.id = vis_id; - - // hide modal - parent.hide_modal(); - - // add widget - Galaxy.frame_manager.frame_new( - { - title : "Trackster", - type : "url", - content : vis_url + "/trackster?" + $.param(dataset_params) - }); + // add widget + parent.Galaxy.frame_manager.frame_new({ + title : "Trackster", + type : "url", + content : url }); } - }); - }, - "View in new visualization": function() { - // hide modal - parent.hide_modal(); + } + }); + } + }); + return false; + }; + } - // add widget - Galaxy.frame_manager.frame_new( - { - title : "Trackster", - type : "url", - content : vis_url + "/trackster?" + $.param(dataset_params) - }); - } - }); - } - }); - return false; - }; -} //============================================================================== //return { diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 static/scripts/utils/config.js --- a/static/scripts/utils/config.js +++ b/static/scripts/utils/config.js @@ -245,9 +245,13 @@ if (this.$el.children().length === 0) { this.render(); } - show_modal("Configure", drawable.config.build_form(), { - "Cancel": cancel_fn, - "OK": ok_fn + Galaxy.modal.show({ + title: "Configure", + body: drawable.config.build_form(), + buttons: { + "Cancel": cancel_fn, + "OK": ok_fn + } }); }, diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 static/scripts/viz/trackster/filters.js --- a/static/scripts/viz/trackster/filters.js +++ b/static/scripts/viz/trackster/filters.js @@ -596,15 +596,19 @@ $.getJSON(run_tool_url, url_params, function(response) { if (response.error) { // General error. - show_modal("Filter Dataset", - "Error running tool " + tool_id, - { "Close" : hide_modal } ); + Galaxy.modal.show({ + title: "Filter Dataset", + body : "Error running tool " + tool_id, + buttons : { "Close" : Galaxy.modal.hide() } + }); } else if (filters.length === 0) { // No more filters to run. - show_modal("Filtering Dataset", - "Filter(s) are running on the complete dataset. Outputs are in dataset's history.", - { "Close" : hide_modal } ); + Galaxy.modal.show({ + title: "Filtering Dataset", + body: "Filter(s) are running on the complete dataset. Outputs are in dataset's history.", + buttons: { "Close" : Galaxy.modal.hide() } + }); } else { // More filters to run. diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 templates/base/base_panels.mako --- a/templates/base/base_panels.mako +++ b/templates/base/base_panels.mako @@ -102,10 +102,11 @@ }); ## load galaxy js-modules - require(['galaxy.master', 'galaxy.frame', 'galaxy.upload'], function(master, frame, upload) + require(['galaxy.master', 'galaxy.frame', 'galaxy.modal', 'galaxy.upload'], function(master, frame, modal, upload) { Galaxy.master = new master.GalaxyMaster(); Galaxy.frame_manager = new frame.GalaxyFrameManager(); + Galaxy.modal = new modal.GalaxyModal(); ##Galaxy.upload = new upload.GalaxyUpload(); }); </script> diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 templates/grid_base.mako --- a/templates/grid_base.mako +++ b/templates/grid_base.mako @@ -86,7 +86,7 @@ // Create grid. var grid = new Grid({ - url_base: location.pathname, + url_base: '${trans.request.path_url}', async: is_true('${grid.use_async}'), async_ops: async_ops, categorical_filters: categorical_filters, @@ -102,6 +102,12 @@ // Initialize grid objects on load. // FIXME: use a grid view object eventually. $(document).ready(function() { + + // strip protocol and domain + var url = grid.get('url_base'); + url = url.replace(/^.*\/\/[^\/]+/, ''); + grid.set('url_base', url); + init_grid_elements(); init_grid_controls(); // Initialize text filters to select text on click and use normal font when user is typing. diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 templates/webapps/galaxy/base_panels.mako --- a/templates/webapps/galaxy/base_panels.mako +++ b/templates/webapps/galaxy/base_panels.mako @@ -63,7 +63,7 @@ <ul class="nav navbar-nav" border="0" cellspacing="0"> - <%def name="tab( id, display, href, target='_parent', visible=True, extra_class='', menu_options=None )"> + <%def name="tab( id, display, href, onclick=False, target='_parent', visible=True, extra_class='', menu_options=None )"> ## Create a tab at the top of the panels. menu_options is a list of 2-elements lists of [name, link] ## that are options in the menu. @@ -100,7 +100,11 @@ ${menu_item[0]} %elif len ( menu_item ) == 2: <% name, link = menu_item %> - <a href="${link}">${name}</a> + %if onclick: + <a href="${link}" onclick="Galaxy.frame_manager.frame_new({title: '${name}', type: 'url', content: '${link}'}); return false;">${name}</a> + %else: + <a href="${link}">${name}</a> + %endif %else: <% name, link, target = menu_item %><a target="${target}" href="${link}">${name}</a> @@ -117,7 +121,7 @@ ${tab( "analysis", _("Analyze Data"), h.url_for( controller='/root', action='index' ) )} ## Workflow tab. - ${tab( "workflow", _("Workflow"), "javascript:Galaxy.frame_manager.frame_new({title: 'Workflow', type: 'url', content: '" + h.url_for( controller='/workflow', action='index' ) + "'});")} + ${tab( "workflow", _("Workflow"), h.url_for( controller='/workflow', action='index' ) )} ## 'Shared Items' or Libraries tab. <% @@ -147,10 +151,10 @@ ## Visualization menu. <% menu_options = [ - [_('New Track Browser'), "javascript:Galaxy.frame_manager.frame_new({title: 'Trackster', type: 'url', content: '" + h.url_for( controller='/visualization', action='trackster' ) + "'});"], - [_('Saved Visualizations'), "javascript:Galaxy.frame_manager.frame_new({ type: 'url', content : '" + h.url_for( controller='/visualization', action='list' ) + "'});" ] + [_('New Track Browser'), h.url_for( controller='/visualization', action='trackster' )], + [_('Saved Visualizations'), h.url_for( controller='/visualization', action='list' )] ] - tab( "visualization", _("Visualization"), "javascript:Galaxy.frame_manager.frame_new({title: 'Trackster', type: 'url', content: '" + h.url_for( controller='/visualization', action='list' ) + "'});", menu_options=menu_options ) + tab( "visualization", _("Visualization"), h.url_for( controller='/visualization', action='list' ), menu_options=menu_options, onclick=True ) %> ## Cloud menu. diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 templates/webapps/galaxy/galaxy.masthead.mako --- a/templates/webapps/galaxy/galaxy.masthead.mako +++ b/templates/webapps/galaxy/galaxy.masthead.mako @@ -51,7 +51,7 @@ <ul class="nav navbar-nav" border="0" cellspacing="0"> - <%def name="tab( id, display, href, target='_parent', visible=True, extra_class='', menu_options=None )"> + <%def name="tab( id, display, href, onclick=False, target='_parent', visible=True, extra_class='', menu_options=None )"> ## Create a tab at the top of the panels. menu_options is a list of 2-elements lists of [name, link] ## that are options in the menu. @@ -61,8 +61,6 @@ extra = "" if extra_class: cls += " " + extra_class - ##if self.active_view == id: - ## cls += " active" if menu_options: cls += " dropdown" a_cls += " dropdown-toggle" @@ -88,7 +86,11 @@ ${menu_item[0]} %elif len ( menu_item ) == 2: <% name, link = menu_item %> - <a href="${link}">${name}</a> + %if onclick: + <a href="${link}" onclick="Galaxy.frame_manager.frame_new({title: '${name}', type: 'url', content: '${link}'}); return false;">${name}</a> + %else: + <a href="${link}">${name}</a> + %endif %else: <% name, link, target = menu_item %><a target="${target}" href="${link}">${name}</a> @@ -105,7 +107,7 @@ ${tab( "analysis", _("Analyze Data"), h.url_for( controller='/root', action='index' ) )} ## Workflow tab. - ${tab( "workflow", _("Workflow"), "javascript:Galaxy.frame_manager.frame_new({title: 'Workflow', type: 'url', content: '" + h.url_for( controller='/workflow', action='index' ) + "'});")} + ${tab( "workflow", _("Workflow"), h.url_for( controller='/workflow', action='index' ) )} ## 'Shared Items' or Libraries tab. <% @@ -135,10 +137,10 @@ ## Visualization menu. <% menu_options = [ - [_('New Track Browser'), "javascript:Galaxy.frame_manager.frame_new({title: 'Trackster', type: 'url', content: '" + h.url_for( controller='/visualization', action='trackster' ) + "'});"], - [_('Saved Visualizations'), "javascript:Galaxy.frame_manager.frame_new({ type: 'url', content : '" + h.url_for( controller='/visualization', action='list' ) + "'});" ] + [_('New Track Browser'), h.url_for( controller='/visualization', action='trackster' )], + [_('Saved Visualizations'), h.url_for( controller='/visualization', action='list' )] ] - tab( "visualization", _("Visualization"), "javascript:Galaxy.frame_manager.frame_new({title: 'Trackster', type: 'url', content: '" + h.url_for( controller='/visualization', action='list' ) + "'});", menu_options=menu_options ) + tab( "visualization", _("Visualization"), h.url_for( controller='/visualization', action='list' ), menu_options=menu_options, onclick=True ) %> ## Cloud menu. diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 templates/webapps/galaxy/galaxy.panels.mako --- a/templates/webapps/galaxy/galaxy.panels.mako +++ b/templates/webapps/galaxy/galaxy.panels.mako @@ -182,7 +182,6 @@ %if self.galaxy_config['message_box']: <div id="messagebox" class="panel-message"></div> %endif - ## left panel %if self.galaxy_config['left_panel']: <div id="left"> diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 templates/webapps/tool_shed/repository/common.mako --- a/templates/webapps/tool_shed/repository/common.mako +++ b/templates/webapps/tool_shed/repository/common.mako @@ -585,10 +585,8 @@ <%def name="render_readme( readme, pad, parent, row_counter )"><% - from tool_shed.util.shed_util_common import to_html_string - from galaxy.util import rst_to_html encoded_id = trans.security.encode_id( readme.id ) - render_rst = readme.name.endswith( '.rst' ) + from galaxy.util import unicodify %><tr class="datasetRow" %if parent is not None: @@ -597,11 +595,15 @@ id="libraryItem-rr-${encoded_id}"><td style="padding-left: ${pad+20}px;"><table id="readme_files"> - %if render_rst: - <tr><td>${ rst_to_html( readme.text ) }</td></tr> - %else: - <tr><td>${ to_html_string( readme.text ) }</td></tr> - %endif + <tr> + <td> + % if readme.name.endswith( '.rst' ): + ${ unicodify( readme.text ) } + %else: + ${ readme.text } + %endif + </td> + </tr></table></td></tr> diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 test/tool_shed/base/twilltestcase.py --- a/test/tool_shed/base/twilltestcase.py +++ b/test/tool_shed/base/twilltestcase.py @@ -723,7 +723,6 @@ post_submit_strings_not_displayed=[] ): self.display_manage_repository_page( repository ) self.check_for_strings( strings_displayed, strings_not_displayed ) - tc.fv( "user_access", "allow_push", '-Select one' ) for username in usernames: tc.fv( "user_access", "allow_push", '+%s' % username ) tc.submit( 'user_access_button' ) diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 test/tool_shed/functional/test_1140_tool_help_images.py --- a/test/tool_shed/functional/test_1140_tool_help_images.py +++ b/test/tool_shed/functional/test_1140_tool_help_images.py @@ -109,10 +109,10 @@ In the center tool panel, look for the image string similar to the following string where the encoded_repository_id is previously determined as the installed ToolShedRepository id: - src="/tool_runner/static/images/<id>/count_modes.png" + src="/admin_toolshed/static/images/<id>/count_modes.png" ''' repository = test_db_util.get_installed_repository_by_name_owner( repository_name, common.test_user_1_name ) - image_path = 'src="/tool_runner/static/images/%s/count_modes.png"' % self.security.encode_id( repository.id ) + image_path = 'src="/admin_toolshed/static/images/%s/count_modes.png"' % self.security.encode_id( repository.id ) # The repository uploaded in this test should only define one tool, which should be the tool that contains a link to an image. repository_metadata = repository.metadata tool_id = repository_metadata[ 'tools' ][ 0 ][ 'guid' ] diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 test/tool_shed/functional/test_1400_review_migration_stages.py --- a/test/tool_shed/functional/test_1400_review_migration_stages.py +++ b/test/tool_shed/functional/test_1400_review_migration_stages.py @@ -20,14 +20,26 @@ def test_0005_load_migration_stages_page( self ): '''Load the migration page and check for the appropriate migration stages.''' - stages = [ - 'emboss_5', 'emboss_datatypes', 'emboss', '5.0.0', '0002_tools.sh', - 'freebayes', '0.9.4_9696d0ce8a962f7bb61c4791be5ce44312b81cf8', 'samtools', '0.1.18', 'FreeBayes requires g++', - 'ncurses', 'zlib', '0003_tools.sh', - 'ncbi_blast_plus', 'blast_datatypes', 'blast+', '2.2.26+', 'blast.ncbi.nlm.nih.gov', 'NCBI BLAST+ tools', '0004_tools.sh', - 'bwa_wrappers', '0.5.9', 'zlib and libpthread', 'Map with BWA for Illumina', '0005_tools.sh', - 'picard', '1.56.0', 'FASTQ to BAM', '0006_tools.sh', - 'lastz', '1.02.00', 'bowtie', '0.12.7', 'Map with Bowtie for Illumina', 'Bowtie requires libpthread', '0007_tools.sh' - ] + stages = [] + migration_message_strings = [ 'The Emboss 5.0.0 tools have been eliminated', + 'The freebayes tool has been eliminated', + 'The NCBI BLAST+ tools', + 'The tools "Map with BWA for Illumina"', + 'FASTQ to BAM, SAM to FASTQ, BAM ', + 'Map with Bowtie for Illumina, ', + 'BAM-to-SAM converts BAM format' ] + migrated_repository_names = [ 'emboss_5', 'emboss_datatypes', 'freebayes', 'ncbi_blast_plus', + 'blast_datatypes', 'bwa_wrappers', 'picard', 'lastz', + 'lastz_paired_reads', 'bowtie_color_wrappers', 'bowtie_wrappers', + 'xy_plot', 'bam_to_sam' ] + migrated_tool_dependencies = [ 'emboss', '5.0.0', 'freebayes', '0.9.4_9696d0ce8a962f7bb61c4791be5ce44312b81cf8', + 'samtools', '0.1.18', 'blast+', '2.2.26+', 'bwa', '0.5.9', 'picard', '1.56.0', + 'lastz', '1.02.00', 'bowtie', '0.12.7', 'FreeBayes requires g++', 'ncurses', 'zlib', + 'zlib and libpthread', 'blast.ncbi.nlm.nih.gov', 'fastx_toolkit', '0.0.13', + 'samtools', '0.1.16', 'cufflinks', '2.1.1', 'R', '2.11.0' ] + migration_scripts = [ '0002_tools.sh', '0003_tools.sh', '0004_tools.sh', '0005_tools.sh', '0006_tools.sh', + '0007_tools.sh', '0008_tools.sh' ] + stages.extend( migration_scripts + migrated_tool_dependencies + migrated_repository_names ) + stages.extend( migration_message_strings ) self.load_galaxy_tool_migrations_page( strings_displayed=stages ) diff -r 8e6d9ee6bb644adaa4598ee306fda54141123f31 -r e2839b5b365966a09b6ad9fd4140b0f067621e93 universe_wsgi.ini.sample --- a/universe_wsgi.ini.sample +++ b/universe_wsgi.ini.sample @@ -269,11 +269,11 @@ # Datasets in an error state include a link to report the error. Those reports # will be sent to this address. Error reports are disabled if no address is set. +# Also this email is shown as a contact to user in case of Galaxy misconfiguration and other events user may encounter. #error_email_to = None -# Administrator's email is shown to user in case of Galaxy misconfiguration or a generic error. -# It is also used as a sender for the account activation mail. -#admin_email = None +# Activation email is used as a sender ('from' field) for the account activation mail. +#activation_email = None # E-mail domains blacklist is used for filtering out users that are using disposable email address # during the registration. If their address domain matches any domain in the BL they are refused the registration. @@ -299,7 +299,7 @@ # Used for warning box for inactive accounts (unable to run jobs). # In use only if activation_grace_period is set. -#inactivity_box_content = Your account has not been activated yet. Please activate your account by verifying your email address. For now you can access everything at Galaxy but your jobs won't run. +#inactivity_box_content = Your account has not been activated yet. Feel free to browse around and see what's available, but you won't be able to upload data or run jobs until you have verified your email address. # -- Display sites 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.