commit/galaxy-central: 5 new changesets
5 new commits in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/2f208d50dfe4/ Changeset: 2f208d50dfe4 User: carlfeberhard Date: 2015-01-31 17:12:30+00:00 Summary: Managers: move Deletable and Purgable mixins into deletable.py, move Accesible and Owned mixins to secured.py Affected #: 7 files diff -r c8da6ac751e7ba5c7eddd5dd3231bd914ecd5ba2 -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb lib/galaxy/managers/base.py --- a/lib/galaxy/managers/base.py +++ b/lib/galaxy/managers/base.py @@ -993,237 +993,3 @@ #TODO: move id decoding out id_list = [ self.app.security.decode_id( id_ ) for id_ in id_list_string.split( sep ) ] return id_list - - -# ==== Security Mixins -class AccessibleManagerMixin( object ): - """ - A security interface to check if a User can read/view an item's. - - This can also be thought of as 'read but not modify' privileges. - """ - - # don't want to override by_id since consumers will also want to fetch w/o any security checks - def is_accessible( self, trans, item, user ): - """ - Return True if the item accessible to user. - """ - # override in subclasses - raise exceptions.NotImplemented( "Abstract interface Method" ) - - def get_accessible( self, trans, id, user, **kwargs ): - """ - Return the item with the given id if it's accessible to user, - otherwise raise an error. - - :raises exceptions.ItemAccessibilityException: - """ - item = ModelManager.by_id( self, trans, id ) - return self.error_unless_accessible( trans, item, user ) - - def error_unless_accessible( self, trans, item, user ): - """ - Raise an error if the item is NOT accessible to user, otherwise return the item. - - :raises exceptions.ItemAccessibilityException: - """ - if self.is_accessible( trans, item, user ): - return item - raise exceptions.ItemAccessibilityException( "%s is not accessible by user" % ( self.model_class.__name__ ) ) - - # TODO:?? are these even useful? - def list_accessible( self, trans, user, **kwargs ): - """ - Return a list of items accessible to the user, raising an error if ANY - are inaccessible. - - :raises exceptions.ItemAccessibilityException: - """ - raise exceptions.NotImplemented( "Abstract interface Method" ) - # NOTE: this will be a large, inefficient list if filters are not passed in kwargs - # items = ModelManager.list( self, trans, **kwargs ) - # return [ self.error_unless_accessible( trans, item, user ) for item in items ] - - def filter_accessible( self, trans, user, **kwargs ): - """ - Return a list of items accessible to the user. - """ - raise exceptions.NotImplemented( "Abstract interface Method" ) - # NOTE: this will be a large, inefficient list if filters are not passed in kwargs - # items = ModelManager.list( self, trans, **kwargs ) - # return filter( lambda item: self.is_accessible( trans, item, user ), items ) - - -class OwnableManagerMixin( object ): - """ - A security interface to check if a User is an item's owner. - - Some resources are associated with the User that created or imported them - and these Users can be considered the models' owner. - - This can also be thought of as write/edit privileges. - """ - - def is_owner( self, trans, item, user ): - """ - Return True if user owns the item. - """ - # override in subclasses - raise exceptions.NotImplemented( "Abstract interface Method" ) - - def get_owned( self, trans, id, user, **kwargs ): - """ - Return the item with the given id if owned by the user, - otherwise raise an error. - - :raises exceptions.ItemOwnershipException: - """ - item = ModelManager.by_id( self, trans, id ) - return self.error_unless_owner( trans, item, user ) - - def error_unless_owner( self, trans, item, user ): - """ - Raise an error if the item is NOT owned by user, otherwise return the item. - - :raises exceptions.ItemAccessibilityException: - """ - if self.is_owner( trans, item, user ): - return item - raise exceptions.ItemOwnershipException( "%s is not owned by user" % ( self.model_class.__name__ ) ) - - def list_owned( self, trans, user, **kwargs ): - """ - Return a list of items owned by the user, raising an error if ANY - are not. - - :raises exceptions.ItemAccessibilityException: - """ - raise exceptions.NotImplemented( "Abstract interface Method" ) - # just alias to by_user (easier/same thing) - #return self.by_user( trans, user, **kwargs ) - - def filter_owned( self, trans, user, **kwargs ): - """ - Return a list of items owned by the user. - """ - # just alias to list_owned - return self.list_owned( trans, user, **kwargs ) - - -# ---- Deletable and Purgable models -class DeletableManagerMixin( object ): - """ - A mixin/interface for a model that is deletable (i.e. has a 'deleted' attr). - - Many resources in Galaxy can be marked as deleted - meaning (in most cases) - that they are no longer needed, should not be displayed, or may be actually - removed by an admin/script. - """ - - def delete( self, trans, item, flush=True, **kwargs ): - """ - Mark as deleted and return. - """ - trans.sa_session.add( item ) - item.deleted = True - if flush: - trans.sa_session.flush() - return item - - def undelete( self, trans, item, flush=True, **kwargs ): - """ - Mark as not deleted and return. - """ - trans.sa_session.add( item ) - item.deleted = False - if flush: - trans.sa_session.flush() - return item - - -class DeletableSerializerMixin( object ): - - def add_serializers( self ): - pass - - -# TODO: these are of questionable value if we don't want to enable users to delete/purge via update -class DeletableDeserializerMixin( object ): - - def add_deserializers( self ): - self.deserializers[ 'deleted' ] = self.deserialize_deleted - - def deserialize_deleted( self, trans, item, key, val ): - """ - Delete or undelete `item` based on `val` then return `item.deleted`. - """ - new_deleted = self.validate.bool( key, val ) - if new_deleted == item.deleted: - return item.deleted - # TODO:?? flush=False? - if new_deleted: - self.manager.delete( trans, item, flush=False ) - else: - self.manager.undelete( trans, item, flush=False ) - return item.deleted - - -class DeletableFiltersMixin( object ): - - def _add_parsers( self ): - self.orm_filter_parsers.update({ - 'deleted' : { 'op': ( 'eq' ), 'val': self.parse_bool } - }) - - -class PurgableManagerMixin( DeletableManagerMixin ): - """ - A manager interface/mixin for a resource that allows deleting and purging where - purging is often removal of some additional, non-db resource (e.g. a dataset's - file). - """ - - def purge( self, trans, item, flush=True, **kwargs ): - """ - Mark as purged and return. - - Override this in subclasses to do the additional resource removal. - """ - trans.sa_session.add( item ) - item.purged = True - if flush: - trans.sa_session.flush() - return item - - -class PurgableSerializerMixin( DeletableSerializerMixin ): - - def add_serializers( self ): - DeletableSerializerMixin.add_serializers( self ) - - -class PurgableDeserializerMixin( DeletableDeserializerMixin ): - - def add_deserializers( self ): - DeletableDeserializerMixin.add_deserializers( self ) - self.deserializers[ 'purged' ] = self.deserialize_purged - - def deserialize_purged( self, trans, item, key, val ): - """ - If `val` is True, purge `item` and return `item.purged`. - """ - new_purged = self.validate.bool( key, val ) - if new_purged == item.purged: - return item.purged - if new_purged: - self.manager.purge( trans, item, flush=False ) - return self.purged - - -class PurgableFiltersMixin( DeletableFiltersMixin ): - - def _add_parsers( self ): - DeletableFiltersMixin._add_parsers( self ) - self.orm_filter_parsers.update({ - 'purged' : { 'op': ( 'eq' ), 'val': self.parse_bool } - }) diff -r c8da6ac751e7ba5c7eddd5dd3231bd914ecd5ba2 -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb lib/galaxy/managers/datasets.py --- a/lib/galaxy/managers/datasets.py +++ b/lib/galaxy/managers/datasets.py @@ -5,13 +5,15 @@ from galaxy import exceptions from galaxy.managers import base +from galaxy.managers import secured +from galaxy.managers import deletable from galaxy.managers import users import logging log = logging.getLogger( __name__ ) -class DatasetManager( base.ModelManager, base.AccessibleManagerMixin, base.PurgableManagerMixin ): +class DatasetManager( base.ModelManager, secured.AccessibleManagerMixin, deletable.PurgableManagerMixin ): """ Manipulate datasets: the components contained in DatasetAssociations/DatasetInstances/HDAs/LDDAs """ @@ -185,7 +187,7 @@ # .... data, object_store -class DatasetSerializer( base.ModelSerializer, base.PurgableSerializerMixin ): +class DatasetSerializer( base.ModelSerializer, deletable.PurgableSerializerMixin ): def __init__( self, app ): super( DatasetSerializer, self ).__init__( app ) @@ -213,7 +215,7 @@ def add_serializers( self ): super( DatasetSerializer, self ).add_serializers() - base.PurgableSerializerMixin.add_serializers( self ) + deletable.PurgableSerializerMixin.add_serializers( self ) self.serializers.update({ 'id' : self.serialize_id, 'create_time' : self.serialize_date, @@ -222,16 +224,15 @@ }) -class DatasetDeserializer( base.ModelDeserializer, base.PurgableDeserializerMixin ): +class DatasetDeserializer( base.ModelDeserializer, deletable.PurgableDeserializerMixin ): model_manager_class = DatasetManager def add_deserializers( self ): super( DatasetDeserializer, self ).add_deserializers() - base.PurgableDeserializerMixin.add_deserializers( self ) + deletable.PurgableDeserializerMixin.add_deserializers( self ) - -class DatasetAssociationManager( base.ModelManager, base.AccessibleManagerMixin, base.PurgableManagerMixin ): +class DatasetAssociationManager( base.ModelManager, secured.AccessibleManagerMixin, deletable.PurgableManagerMixin ): """ DatasetAssociation/DatasetInstances are intended to be working proxies to a Dataset, associated with either a library or a @@ -266,15 +267,15 @@ # pass -class DatasetAssociationSerializer( base.ModelSerializer, base.PurgableSerializerMixin ): +class DatasetAssociationSerializer( base.ModelSerializer, deletable.PurgableSerializerMixin ): def add_serializers( self ): super( DatasetAssociationSerializer, self ).add_serializers() - base.PurgableSerializerMixin.add_serializers( self ) + deletable.PurgableSerializerMixin.add_serializers( self ) -class DatasetAssociationDeserializer( base.ModelDeserializer, base.PurgableDeserializerMixin ): +class DatasetAssociationDeserializer( base.ModelDeserializer, deletable.PurgableDeserializerMixin ): def add_deserializers( self ): super( DatasetAssociationDeserializer, self ).add_deserializers() - base.PurgableDeserializerMixin.add_deserializers( self ) + deletable.PurgableDeserializerMixin.add_deserializers( self ) diff -r c8da6ac751e7ba5c7eddd5dd3231bd914ecd5ba2 -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb lib/galaxy/managers/deletable.py --- /dev/null +++ b/lib/galaxy/managers/deletable.py @@ -0,0 +1,128 @@ +""" +Many models in Galaxy are not meant to be removed from the database but only +marked as deleted. These models have the boolean attribute 'deleted'. + +Other models are deletable and also may be purged. Most often these are +models have some backing/supporting resources that can be removed as well +(e.g. Datasets have data files on a drive). Purging these models removes +the supporting resources as well. These models also have the boolean +attribute 'purged'. +""" + +# ---- Deletable and Purgable models +class DeletableManagerMixin( object ): + """ + A mixin/interface for a model that is deletable (i.e. has a 'deleted' attr). + + Many resources in Galaxy can be marked as deleted - meaning (in most cases) + that they are no longer needed, should not be displayed, or may be actually + removed by an admin/script. + """ + + def delete( self, trans, item, flush=True, **kwargs ): + """ + Mark as deleted and return. + """ + trans.sa_session.add( item ) + item.deleted = True + if flush: + trans.sa_session.flush() + return item + + def undelete( self, trans, item, flush=True, **kwargs ): + """ + Mark as not deleted and return. + """ + trans.sa_session.add( item ) + item.deleted = False + if flush: + trans.sa_session.flush() + return item + + +class DeletableSerializerMixin( object ): + + def add_serializers( self ): + pass + + +# TODO: these are of questionable value if we don't want to enable users to delete/purge via update +class DeletableDeserializerMixin( object ): + + def add_deserializers( self ): + self.deserializers[ 'deleted' ] = self.deserialize_deleted + + def deserialize_deleted( self, trans, item, key, val ): + """ + Delete or undelete `item` based on `val` then return `item.deleted`. + """ + new_deleted = self.validate.bool( key, val ) + if new_deleted == item.deleted: + return item.deleted + # TODO:?? flush=False? + if new_deleted: + self.manager.delete( trans, item, flush=False ) + else: + self.manager.undelete( trans, item, flush=False ) + return item.deleted + + +class DeletableFiltersMixin( object ): + + def _add_parsers( self ): + self.orm_filter_parsers.update({ + 'deleted' : { 'op': ( 'eq' ), 'val': self.parse_bool } + }) + + +class PurgableManagerMixin( DeletableManagerMixin ): + """ + A manager interface/mixin for a resource that allows deleting and purging where + purging is often removal of some additional, non-db resource (e.g. a dataset's + file). + """ + + def purge( self, trans, item, flush=True, **kwargs ): + """ + Mark as purged and return. + + Override this in subclasses to do the additional resource removal. + """ + trans.sa_session.add( item ) + item.purged = True + if flush: + trans.sa_session.flush() + return item + + +class PurgableSerializerMixin( DeletableSerializerMixin ): + + def add_serializers( self ): + DeletableSerializerMixin.add_serializers( self ) + + +class PurgableDeserializerMixin( DeletableDeserializerMixin ): + + def add_deserializers( self ): + DeletableDeserializerMixin.add_deserializers( self ) + self.deserializers[ 'purged' ] = self.deserialize_purged + + def deserialize_purged( self, trans, item, key, val ): + """ + If `val` is True, purge `item` and return `item.purged`. + """ + new_purged = self.validate.bool( key, val ) + if new_purged == item.purged: + return item.purged + if new_purged: + self.manager.purge( trans, item, flush=False ) + return self.purged + + +class PurgableFiltersMixin( DeletableFiltersMixin ): + + def _add_parsers( self ): + DeletableFiltersMixin._add_parsers( self ) + self.orm_filter_parsers.update({ + 'purged' : { 'op': ( 'eq' ), 'val': self.parse_bool } + }) diff -r c8da6ac751e7ba5c7eddd5dd3231bd914ecd5ba2 -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb lib/galaxy/managers/hdas.py --- a/lib/galaxy/managers/hdas.py +++ b/lib/galaxy/managers/hdas.py @@ -16,7 +16,7 @@ from galaxy import objectstore from galaxy.managers import datasets -from galaxy.managers import base +from galaxy.managers import secured from galaxy.managers import taggable from galaxy.managers import annotatable from galaxy.managers import users @@ -25,7 +25,7 @@ log = logging.getLogger( __name__ ) -class HDAManager( datasets.DatasetAssociationManager, base.OwnableManagerMixin, +class HDAManager( datasets.DatasetAssociationManager, secured.OwnableManagerMixin, taggable.TaggableManagerMixin, annotatable.AnnotatableManagerMixin ): """ Interface/service object for interacting with HDAs. diff -r c8da6ac751e7ba5c7eddd5dd3231bd914ecd5ba2 -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb lib/galaxy/managers/histories.py --- a/lib/galaxy/managers/histories.py +++ b/lib/galaxy/managers/histories.py @@ -8,15 +8,16 @@ import galaxy.web from galaxy import model from galaxy.managers import base +from galaxy.managers import sharable +from galaxy.managers import deletable from galaxy.managers import hdas -from galaxy.managers import sharable from galaxy.managers.collections_util import dictify_dataset_collection_instance import logging log = logging.getLogger( __name__ ) -class HistoryManager( sharable.SharableModelManager, base.PurgableManagerMixin ): +class HistoryManager( sharable.SharableModelManager, deletable.PurgableManagerMixin ): model_class = model.History foreign_key_name = 'history' @@ -234,7 +235,7 @@ return state -class HistorySerializer( sharable.SharableModelSerializer, base.PurgableSerializerMixin ): +class HistorySerializer( sharable.SharableModelSerializer, deletable.PurgableSerializerMixin ): """ Interface/service object for serializing histories into dictionaries. """ @@ -288,7 +289,7 @@ #assumes: outgoing to json.dumps and sanitized def add_serializers( self ): super( HistorySerializer, self ).add_serializers() - base.PurgableSerializerMixin.add_serializers( self ) + deletable.PurgableSerializerMixin.add_serializers( self ) self.serializers.update({ 'model_class' : lambda *a: 'History', @@ -333,7 +334,7 @@ security=self.app.security, parent=dataset_collection_instance.history, view="element" ) -class HistoryDeserializer( sharable.SharableModelDeserializer, base.PurgableDeserializerMixin ): +class HistoryDeserializer( sharable.SharableModelDeserializer, deletable.PurgableDeserializerMixin ): """ Interface/service object for validating and deserializing dictionaries into histories. """ @@ -345,7 +346,7 @@ def add_deserializers( self ): super( HistoryDeserializer, self ).add_deserializers() - base.PurgableDeserializerMixin.add_deserializers( self ) + deletable.PurgableDeserializerMixin.add_deserializers( self ) self.deserializers.update({ 'name' : self.deserialize_basestring, @@ -353,12 +354,12 @@ }) -class HistoryFilters( sharable.SharableModelFilters, base.PurgableFiltersMixin ): +class HistoryFilters( sharable.SharableModelFilters, deletable.PurgableFiltersMixin ): model_class = model.History def _add_parsers( self ): super( HistoryFilters, self )._add_parsers() - base.PurgableFiltersMixin._add_parsers( self ) + deletable.PurgableFiltersMixin._add_parsers( self ) self.orm_filter_parsers.update({ # history specific diff -r c8da6ac751e7ba5c7eddd5dd3231bd914ecd5ba2 -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb lib/galaxy/managers/secured.py --- /dev/null +++ b/lib/galaxy/managers/secured.py @@ -0,0 +1,122 @@ +""" +Accessible models can be read and copied but not modified or deleted. + +Owned models can be modified and deleted. +""" + +from galaxy import exceptions +from galaxy.managers import base + + +class AccessibleManagerMixin( object ): + """ + A security interface to check if a User can read/view an item's. + + This can also be thought of as 'read but not modify' privileges. + """ + + # don't want to override by_id since consumers will also want to fetch w/o any security checks + def is_accessible( self, trans, item, user ): + """ + Return True if the item accessible to user. + """ + # override in subclasses + raise exceptions.NotImplemented( "Abstract interface Method" ) + + def get_accessible( self, trans, id, user, **kwargs ): + """ + Return the item with the given id if it's accessible to user, + otherwise raise an error. + + :raises exceptions.ItemAccessibilityException: + """ + item = self.by_id( trans, id ) + return self.error_unless_accessible( trans, item, user ) + + def error_unless_accessible( self, trans, item, user ): + """ + Raise an error if the item is NOT accessible to user, otherwise return the item. + + :raises exceptions.ItemAccessibilityException: + """ + if self.is_accessible( trans, item, user ): + return item + raise exceptions.ItemAccessibilityException( "%s is not accessible by user" % ( self.model_class.__name__ ) ) + + # TODO:?? are these even useful? + def list_accessible( self, trans, user, **kwargs ): + """ + Return a list of items accessible to the user, raising an error if ANY + are inaccessible. + + :raises exceptions.ItemAccessibilityException: + """ + raise exceptions.NotImplemented( "Abstract interface Method" ) + # NOTE: this will be a large, inefficient list if filters are not passed in kwargs + # items = ModelManager.list( self, trans, **kwargs ) + # return [ self.error_unless_accessible( trans, item, user ) for item in items ] + + def filter_accessible( self, trans, user, **kwargs ): + """ + Return a list of items accessible to the user. + """ + raise exceptions.NotImplemented( "Abstract interface Method" ) + # NOTE: this will be a large, inefficient list if filters are not passed in kwargs + # items = ModelManager.list( self, trans, **kwargs ) + # return filter( lambda item: self.is_accessible( trans, item, user ), items ) + + +class OwnableManagerMixin( object ): + """ + A security interface to check if a User is an item's owner. + + Some resources are associated with the User that created or imported them + and these Users can be considered the models' owner. + + This can also be thought of as write/edit privileges. + """ + + def is_owner( self, trans, item, user ): + """ + Return True if user owns the item. + """ + # override in subclasses + raise exceptions.NotImplemented( "Abstract interface Method" ) + + def get_owned( self, trans, id, user, **kwargs ): + """ + Return the item with the given id if owned by the user, + otherwise raise an error. + + :raises exceptions.ItemOwnershipException: + """ + item = self.by_id( trans, id ) + return self.error_unless_owner( trans, item, user ) + + def error_unless_owner( self, trans, item, user ): + """ + Raise an error if the item is NOT owned by user, otherwise return the item. + + :raises exceptions.ItemAccessibilityException: + """ + if self.is_owner( trans, item, user ): + return item + raise exceptions.ItemOwnershipException( "%s is not owned by user" % ( self.model_class.__name__ ) ) + + def list_owned( self, trans, user, **kwargs ): + """ + Return a list of items owned by the user, raising an error if ANY + are not. + + :raises exceptions.ItemAccessibilityException: + """ + raise exceptions.NotImplemented( "Abstract interface Method" ) + # just alias to by_user (easier/same thing) + #return self.by_user( trans, user, **kwargs ) + + def filter_owned( self, trans, user, **kwargs ): + """ + Return a list of items owned by the user. + """ + # just alias to list_owned + return self.list_owned( trans, user, **kwargs ) diff -r c8da6ac751e7ba5c7eddd5dd3231bd914ecd5ba2 -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb lib/galaxy/managers/sharable.py --- a/lib/galaxy/managers/sharable.py +++ b/lib/galaxy/managers/sharable.py @@ -15,6 +15,7 @@ from galaxy import exceptions from galaxy.managers import base +from galaxy.managers import secured from galaxy.managers import taggable from galaxy.managers import annotatable from galaxy.managers import ratable @@ -24,7 +25,7 @@ log = logging.getLogger( __name__ ) -class SharableModelManager( base.ModelManager, base.OwnableManagerMixin, base.AccessibleManagerMixin, +class SharableModelManager( base.ModelManager, secured.OwnableManagerMixin, secured.AccessibleManagerMixin, taggable.TaggableManagerMixin, annotatable.AnnotatableManagerMixin, ratable.RatableManagerMixin ): # e.g. histories, pages, stored workflows, visualizations # base.DeleteableModelMixin? (all four are deletable) https://bitbucket.org/galaxy/galaxy-central/commits/f6fb30a976ab/ Changeset: f6fb30a976ab User: carlfeberhard Date: 2015-02-01 13:40:09+00:00 Summary: Managers: work towards a removing trans from managers Affected #: 7 files diff -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c lib/galaxy/managers/annotatable.py --- a/lib/galaxy/managers/annotatable.py +++ b/lib/galaxy/managers/annotatable.py @@ -42,8 +42,8 @@ """ Get and serialize an `item`'s annotation. """ - # TODO: have to assume trans.user here... # user = item.user + #TODO: trans user = trans.user sa_session = self.app.model.context returned = item.get_item_annotation_str( sa_session, user, item ) @@ -63,7 +63,7 @@ val = self.validate.nullable_basestring( key, val ) sa_session = self.app.model.context - # TODO: have to assume trans.user here... + #TODO: trans user = trans.user if val is None: item.delete_item_annotation( sa_session, user, item ) diff -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c lib/galaxy/managers/base.py --- a/lib/galaxy/managers/base.py +++ b/lib/galaxy/managers/base.py @@ -159,6 +159,20 @@ def __init__( self, app ): self.app = app + def session( self ): + return self.app.model.context + + def _session_setattr( self, item, attr, val, fn=None, flush=True ): + if fn: + fn( item, attr, val ) + else: + setattr( item, attr, val ) + + self.session().add( item ) + if flush: + self.session().flush() + return item + # .... query foundation wrapper def query( self, trans, eagerloads=True, filters=None, order_by=None, limit=None, offset=None, **kwargs ): """ @@ -166,7 +180,7 @@ Set eagerloads to False to disable them for this query. """ - query = trans.sa_session.query( self.model_class ) + query = self.session().query( self.model_class ) # joined table loading if eagerloads is False: query = query.enable_eagerloads( False ) @@ -371,7 +385,7 @@ # TODO: this does not order by the original 'ids' array # ...could use get (supposedly since found are in the session, the db won't be hit twice) - # return map( trans.sa_session.query( self.model_class ).get, ids ) + # return map( self.session().query( self.model_class ).get, ids ) # ...could implement own version here - slow? return self._order_items_by_id( ids, found ) @@ -407,9 +421,9 @@ """ # override in subclasses item = self.model_class( *args, **kwargs ) - trans.sa_session.add( item ) + self.session().add( item ) if flush: - trans.sa_session.flush() + self.session().flush() return item def copy( self, trans, item, **kwargs ): @@ -424,12 +438,12 @@ ..note: NO validation or deserialization occurs here. """ - trans.sa_session.add( item ) + self.session().add( item ) for key, value in new_values.items(): if hasattr( item, key ): setattr( item, key, value ) if flush: - trans.sa_session.flush() + self.session().flush() return item # TODO: yagni? @@ -447,7 +461,7 @@ """ foreign_key_name = foreign_key_name or self.foreign_key_name foreign_key = getattr( associated_model_class, foreign_key_name ) - return trans.sa_session.query( associated_model_class ).filter( foreign_key == item ) + return self.session().query( associated_model_class ).filter( foreign_key == item ) # a rename of sql DELETE to differentiate from the Galaxy notion of mark_as_deleted # def destroy( self, trans, item, **kwargs ): diff -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c lib/galaxy/managers/deletable.py --- a/lib/galaxy/managers/deletable.py +++ b/lib/galaxy/managers/deletable.py @@ -23,21 +23,13 @@ """ Mark as deleted and return. """ - trans.sa_session.add( item ) - item.deleted = True - if flush: - trans.sa_session.flush() - return item + return self._session_setattr( item, 'deleted', True, flush=flush ) def undelete( self, trans, item, flush=True, **kwargs ): """ Mark as not deleted and return. """ - trans.sa_session.add( item ) - item.deleted = False - if flush: - trans.sa_session.flush() - return item + return self._session_setattr( item, 'deleted', False, flush=flush ) class DeletableSerializerMixin( object ): @@ -88,11 +80,7 @@ Override this in subclasses to do the additional resource removal. """ - trans.sa_session.add( item ) - item.purged = True - if flush: - trans.sa_session.flush() - return item + return self._session_setattr( item, 'purged', True, flush=flush ) class PurgableSerializerMixin( DeletableSerializerMixin ): diff -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c lib/galaxy/managers/histories.py --- a/lib/galaxy/managers/histories.py +++ b/lib/galaxy/managers/histories.py @@ -49,6 +49,7 @@ """ # handle default and/or anonymous user (which still may not have a history yet) if self.user_manager.is_anonymous( user ): + #TODO: trans current_history = trans.get_history() return [ current_history ] if current_history else [] @@ -59,6 +60,7 @@ True if the current user is the owner of the given history. """ # anon users are only allowed to view their current history + #TODO: trans if self.user_manager.is_anonymous( user ) and history == trans.get_history(): return True return super( HistoryManager, self ).is_owner( trans, history, user ) @@ -68,11 +70,12 @@ """ Return the most recently update history for the user. """ - #TODO: normalize this return value + #TODO: trans if not user: return None if trans.history.deleted else trans.history desc_update_time = self.model_class.table.c.update_time filters = self._munge_filters( filters, self.model_class.user_id == user.id ) + #TODO: normalize this return value return self.query( trans, filters=filters, order_by=desc_update_time, limit=1, **kwargs ).first() # .... purgable @@ -95,12 +98,14 @@ """ Return the current history. """ + #TODO: trans return trans.get_history() def set_current( self, trans, history ): """ Set the current history. """ + #TODO: trans trans.set_history( history ) return history diff -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c lib/galaxy/managers/sharable.py --- a/lib/galaxy/managers/sharable.py +++ b/lib/galaxy/managers/sharable.py @@ -88,12 +88,8 @@ Does not flush/commit changes, however. Item must have name, user, importable, and slug attributes. """ - self.app.model.context.add( item ) - item.importable = True self.create_unique_slug( trans, item, flush=False ) - if flush: - self.app.model.context.flush() - return item + return self._session_setattr( item, 'importable', True, flush=flush ) def make_non_importable( self, trans, item, flush=True ): """ @@ -102,26 +98,9 @@ importable, and slug attributes. """ # item must be unpublished if non-importable - self.app.model.context.add( item ) if item.published: self.unpublish( trans, item, flush=False ) - item.importable = False - if flush: - self.app.model.context.flush() - return item - - #def _query_importable( self, trans, filters=None, **kwargs ): - # """ - # """ - # importable_filter = self.model_class.importable == True - # filters = self._munge_filters( importable_filter, filters ) - # return self.list( trans, filters=filters, **kwargs ) - # - #def list_importable( self, trans, **kwargs ): - # """ - # """ - # query = self._query_importable( trans, user, **kwargs ) - # return self.list( trans, query=query, **kwargs ) + return self._session_setattr( item, 'importable', False, flush=flush ) # .... published def publish( self, trans, item, flush=True ): @@ -131,21 +110,13 @@ # item must be importable to be published if not item.importable: self.make_importable( trans, item, flush=False ) - self.app.model.context.add( item ) - item.published = True - if flush: - self.app.model.context.flush() - return item + return self._session_setattr( item, 'published', True, flush=flush ) def unpublish( self, trans, item, flush=True ): """ Set the published flag on `item` to False. """ - self.app.model.context.add( item ) - item.published = False - if flush: - self.app.model.context.flush() - return item + return self._session_setattr( item, 'published', False, flush=flush ) #def _query_published( self, trans, filters=None, **kwargs ): # """ diff -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c lib/galaxy/managers/taggable.py --- a/lib/galaxy/managers/taggable.py +++ b/lib/galaxy/managers/taggable.py @@ -53,13 +53,13 @@ """ new_tags_list = self.validate.basestring_list( key, val ) #TODO: have to assume trans.user here... + #TODO: trans user = trans.user #TODO: duped from tags manager - de-dupe when moved to taggable mixin - tag_handler = trans.app.tag_handler + tag_handler = self.app.tag_handler tag_handler.delete_item_tags( user, item ) new_tags_str = ','.join( new_tags_list ) tag_handler.apply_item_tags( user, item, unicode( new_tags_str.encode( 'utf-8' ), 'utf-8' ) ) - #trans.sa_session.flush() #TODO:!! does the creation of new_tags_list mean there are now more and more unused tag rows in the db? return item.tags diff -r 2f208d50dfe43fbfba876a172a3ae3c7524102eb -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c lib/galaxy/managers/users.py --- a/lib/galaxy/managers/users.py +++ b/lib/galaxy/managers/users.py @@ -58,7 +58,7 @@ # can throw an sqlalx.IntegrityError if username not unique self.app.security_agent.create_private_user_role( user ) -#TODO: any other route to webapp? + #TODO: trans if trans.webapp.name == 'galaxy': # We set default user permissions, before we log in and set the default history permissions permissions = self.app.config.new_user_dataset_access_role_default_private https://bitbucket.org/galaxy/galaxy-central/commits/e7560410c0f2/ Changeset: e7560410c0f2 User: carlfeberhard Date: 2015-02-01 13:46:21+00:00 Summary: Branch merge Affected #: 56 files diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef client/galaxy/scripts/galaxy.workflow_editor.canvas.js --- a/client/galaxy/scripts/galaxy.workflow_editor.canvas.js +++ b/client/galaxy/scripts/galaxy.workflow_editor.canvas.js @@ -1870,6 +1870,9 @@ left: x, top: y }); + self.cv.css( { "background-position-x": x, + "background-position-y": y + }); self.update_viewport_overlay(); }; // Dragging within canvas background diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef client/galaxy/scripts/mvc/history/history-panel-edit.js --- a/client/galaxy/scripts/mvc/history/history-panel-edit.js +++ b/client/galaxy/scripts/mvc/history/history-panel-edit.js @@ -75,13 +75,17 @@ /** Override to handle history as drag-drop target */ _setUpListeners : function(){ - _super.prototype._setUpListeners.call( this ); + var panel = this; + _super.prototype._setUpListeners.call( panel ); - this.on( 'drop', function( ev, data ){ - this.dataDropped( data ); + panel.on( 'drop', function( ev, data ){ + panel.dataDropped( data ); // remove the drop target - this.dropTargetOff(); + panel.dropTargetOff(); }); + panel.on( 'view:attached view:removed', function(){ + panel._renderCounts(); + }, panel ); }, // ------------------------------------------------------------------------ listeners @@ -332,7 +336,10 @@ // override to control where the view is added, how/whether it's rendered panel.views.unshift( view ); panel.$list().prepend( view.render( 0 ).$el.hide() ); - view.$el.slideDown( panel.fxSpeed ); + panel.trigger( 'view:attached', view ); + view.$el.slideDown( panel.fxSpeed, function(){ + panel.trigger( 'view:attached:rendered' ); + }); }, /** In this override, add purgeAllowed and whether tags/annotation editors should be shown */ diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef client/galaxy/scripts/mvc/list/list-panel.js --- a/client/galaxy/scripts/mvc/list/list-panel.js +++ b/client/galaxy/scripts/mvc/list/list-panel.js @@ -477,7 +477,10 @@ // override to control where the view is added, how/whether it's rendered panel.views.push( view ); panel.$list().append( view.render( 0 ).$el.hide() ); - view.$el.slideDown( panel.fxSpeed ); + panel.trigger( 'view:attached', view ); + view.$el.slideDown( panel.fxSpeed, function(){ + panel.trigger( 'view:attached:rendered' ); + }); }, /** Remove a view from the panel (if found) */ @@ -487,6 +490,7 @@ view = panel.viewFromModel( model ); if( !view ){ return undefined; } panel.views = _.without( panel.views, view ); + panel.trigger( 'view:removed', view ); // potentially show the empty message if no views left // use anonymous queue here - since remove can happen multiple times @@ -494,6 +498,7 @@ function( next ){ view.$el.fadeOut( panel.fxSpeed, next ); }, function( next ){ view.remove(); + panel.trigger( 'view:removed:rendered' ); if( !panel.views.length ){ panel._renderEmptyMessage().fadeIn( panel.fxSpeed, next ); } else { diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef client/galaxy/scripts/mvc/upload/upload-row.js --- a/client/galaxy/scripts/mvc/upload/upload-row.js +++ b/client/galaxy/scripts/mvc/upload/upload-row.js @@ -69,7 +69,7 @@ css: 'genome', onchange : function(genome) { self.model.set('genome', genome); - self.app.updateGenome(genome); + self.app.updateGenome(genome, true); }, data: self.app.list_genomes, container: it.find('#genome'), @@ -87,7 +87,7 @@ css: 'extension', onchange : function(extension) { self.model.set('extension', extension); - self.app.updateExtension(extension); + self.app.updateExtension(extension, true); }, data: self.app.list_extensions, container: it.find('#extension'), diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef client/galaxy/scripts/mvc/upload/upload-view.js --- a/client/galaxy/scripts/mvc/upload/upload-view.js +++ b/client/galaxy/scripts/mvc/upload/upload-view.js @@ -26,6 +26,10 @@ nginx_upload_path : '' }, + // default for selection fields + default_extension : 'auto', + default_genome : '?', + // own modal modal : null, @@ -130,16 +134,13 @@ // sort self.list_extensions.sort(function(a, b) { - return a.id > b.id ? 1 : a.id < b.id ? -1 : 0; + return a.text > b.text ? 1 : a.text < b.text ? -1 : 0; }); // add auto field if (!self.options.datatypes_disable_auto) { self.list_extensions.unshift(self.auto); } - - // set default extension - self.default_extension = self.list_extensions[0] && self.list_extensions[0].id; } }); @@ -156,11 +157,10 @@ // sort self.list_genomes.sort(function(a, b) { - return a.id > b.id ? 1 : a.id < b.id ? -1 : 0; + if (a.id == self.default_genome) { return -1; } + if (b.id == self.default_genome) { return 1; } + return a.text > b.text ? 1 : a.text < b.text ? -1 : 0; }); - - // set default genome - self.default_genome = self.list_genomes[0] && self.list_genomes[0].id; } }); @@ -585,20 +585,20 @@ }, // update extension for all models - updateExtension: function(extension) { + updateExtension: function(extension, defaults_only) { var self = this; this.collection.each(function(item) { - if (item.get('status') == 'init' && item.get('extension') == self.default_extension) { + if (item.get('status') == 'init' && (item.get('extension') == self.default_extension || !defaults_only)) { item.set('extension', extension); } }); }, // update genome for all models - updateGenome: function(genome) { + updateGenome: function(genome, defaults_only) { var self = this; this.collection.each(function(item) { - if (item.get('status') == 'init' && item.get('genome') == self.default_genome) { + if (item.get('status') == 'init' && (item.get('genome') == self.default_genome || !defaults_only)) { item.set('genome', genome); } }); @@ -724,10 +724,10 @@ '</table>' + '</div>' + '<div id="upload-header" class="upload-header">' + - '<span class="header-title">Type (default):</span>' + + '<span class="header-title">Type (set all):</span>' + '<span id="header-extension"/>' + '<span id="header-extension-info" class="upload-icon-button fa fa-search"/> ' + - '<span class="header-title">Genome (default):</span>' + + '<span class="header-title">Genome (set all):</span>' + '<span id="header-genome"/>' + '</div>'; } diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef config/reports_wsgi.ini.sample --- a/config/reports_wsgi.ini.sample +++ b/config/reports_wsgi.ini.sample @@ -22,9 +22,9 @@ # database_connection = postgres:///galaxy_test?user=postgres&password=postgres # Where dataset files are saved -file_path = database/files +#file_path = database/files # Temporary storage for additional datasets, this should be shared through the cluster -new_file_path = database/tmp +#new_file_path = database/tmp # Mako templates are compiled as needed and cached for reuse, this directory is # used for the cache @@ -64,10 +64,10 @@ use_new_layout = true # Serving static files (needed if running standalone) -static_enabled = True -static_cache_time = 360 -static_dir = %(here)s/static/ -static_images_dir = %(here)s/static/images -static_favicon_dir = %(here)s/static/favicon.ico -static_scripts_dir = %(here)s/static/scripts/ -static_style_dir = %(here)s/static/june_2007_style/blue +# static_enabled = True +# static_cache_time = 360 +# static_dir = %(here)s/static/ +# static_images_dir = %(here)s/static/images +# static_favicon_dir = %(here)s/static/favicon.ico +# static_scripts_dir = %(here)s/static/scripts/ +# static_style_dir = %(here)s/static/june_2007_style/blue diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef config/tool_shed.ini.sample --- a/config/tool_shed.ini.sample +++ b/config/tool_shed.ini.sample @@ -125,3 +125,6 @@ #static_favicon_dir = static/favicon.ico #static_scripts_dir = static/scripts/ #static_style_dir = static/style/blue + +# Sentry (getsentry.com) DSN for catching bugs. +#sentry_dsn = None diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef cron/parse_builds.py --- a/cron/parse_builds.py +++ b/cron/parse_builds.py @@ -8,12 +8,7 @@ import sys import urllib -if sys.version_info[:2] >= ( 2, 5 ): - import xml.etree.ElementTree as ElementTree -else: - from galaxy import eggs - import pkg_resources; pkg_resources.require( "elementtree" ) - from elementtree import ElementTree +import xml.etree.ElementTree as ElementTree URL = "http://genome.cse.ucsc.edu/cgi-bin/das/dsn" @@ -38,7 +33,7 @@ for dsn in tree: build = dsn.find("SOURCE").attrib['id'] description = dsn.find("DESCRIPTION").text.replace(" - Genome at UCSC","").replace(" Genome at UCSC","") - + fields = description.split(" ") temp = fields[0] for i in range(len(fields)-1): diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef cron/parse_builds_3_sites.py --- a/cron/parse_builds_3_sites.py +++ b/cron/parse_builds_3_sites.py @@ -5,12 +5,7 @@ import sys import urllib -if sys.version_info[:2] >= ( 2, 5 ): - import xml.etree.ElementTree as ElementTree -else: - from galaxy import eggs - import pkg_resources; pkg_resources.require( "elementtree" ) - from elementtree import ElementTree +import xml.etree.ElementTree as ElementTree sites = ['http://genome.ucsc.edu/cgi-bin/', 'http://archaea.ucsc.edu/cgi-bin/', @@ -38,7 +33,7 @@ print "#Invalid xml passed back from " + site continue print "#Harvested from",site - + for dsn in tree: build = dsn.find("SOURCE").attrib['id'] builds.append(build) diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef eggs.ini --- a/eggs.ini +++ b/eggs.ini @@ -38,7 +38,6 @@ decorator = 3.1.2 docutils = 0.7 drmaa = 0.7.6 -elementtree = 1.2.6_20050316 Fabric = 1.7.0 importlib = 1.0.3 kombu = 3.0.13 @@ -74,7 +73,7 @@ wsgiref = 0.1.2 Babel = 1.3 wchartype = 0.1 -Whoosh = 2.5.7 +Whoosh = 2.4.1 ; fluent_logger = 0.3.3 raven = 3.1.8 diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/datatypes/dataproviders/hierarchy.py --- a/lib/galaxy/datatypes/dataproviders/hierarchy.py +++ b/lib/galaxy/datatypes/dataproviders/hierarchy.py @@ -3,7 +3,7 @@ """ import line -import xml.etree.ElementTree as elementtree +from xml.etree.ElementTree import Element, iterparse _TODO = """ """ @@ -30,7 +30,7 @@ """ Data provider that converts selected XML elements to dictionaries. """ - # using elementtree's iterparse method to keep mem down + # using xml.etree's iterparse method to keep mem down #TODO: this, however (AFAIK), prevents the use of xpath settings = { 'selector' : 'str', #urlencoded @@ -54,7 +54,7 @@ """ Returns true if the ``element`` matches the ``selector``. - :param element: an XML ``ElementTree.Element`` + :param element: an XML ``Element`` :param selector: some partial string in the desired tags to return Change point for more sophisticated selectors. @@ -64,13 +64,13 @@ #TODO: fails with '#' - browser thinks it's an anchor - use urlencode #TODO: need removal/replacement of etree namespacing here - then move to string match return bool( ( selector == None ) - or ( isinstance( element, elementtree.Element ) and selector in element.tag ) ) + or ( isinstance( element, Element ) and selector in element.tag ) ) def element_as_dict( self, element ): """ Converts an XML element (its text, tag, and attributes) to dictionary form. - :param element: an XML ``ElementTree.Element`` + :param element: an XML ``Element`` """ #TODO: Key collision is unlikely here, but still should be better handled return { @@ -84,7 +84,7 @@ """ Yield all children of element (and their children - recursively) in dictionary form. - :param element: an XML ``ElementTree.Element`` + :param element: an XML ``Element`` :param max_depth: the number of generations of descendents to return """ if not isinstance( max_depth, int ) or max_depth >= 1: @@ -99,7 +99,7 @@ yield child_data def __iter__( self ): - context = elementtree.iterparse( self.source, events=self.ITERPARSE_ALL_EVENTS ) + context = iterparse( self.source, events=self.ITERPARSE_ALL_EVENTS ) context = iter( context ) selected_element = None diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/datatypes/registry.py --- a/lib/galaxy/datatypes/registry.py +++ b/lib/galaxy/datatypes/registry.py @@ -346,7 +346,7 @@ """ sniffer_elem_classes = [ e.attrib[ 'type' ] for e in self.sniffer_elems ] sniffers = root.find( 'sniffers' ) - if sniffers: + if sniffers is not None: for elem in sniffers.findall( 'sniffer' ): # Keep a status of the process steps to enable stopping the process of handling the sniffer if necessary. ok = True diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/sample_tracking/external_service_types.py --- a/lib/galaxy/sample_tracking/external_service_types.py +++ b/lib/galaxy/sample_tracking/external_service_types.py @@ -4,7 +4,6 @@ from galaxy import util, jobs, model from galaxy.forms.forms import form_factory from galaxy.external_services.service import ExternalServiceActionsGroup -from elementtree.ElementTree import XML from galaxy.sample_tracking.data_transfer import data_transfer_factories log = logging.getLogger( __name__ ) diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/tools/__init__.py --- a/lib/galaxy/tools/__init__.py +++ b/lib/galaxy/tools/__init__.py @@ -18,12 +18,11 @@ eggs.require( "MarkupSafe" ) # MarkupSafe must load before mako eggs.require( "Mako" ) -eggs.require( "elementtree" ) eggs.require( "Paste" ) eggs.require( "SQLAlchemy >= 0.4" ) from cgi import FieldStorage -from elementtree import ElementTree +from xml.etree import ElementTree from mako.template import Template from paste import httpexceptions @@ -2280,7 +2279,7 @@ trans.response.status = 500 log.error('Failed to get job information.') return { 'error': 'Failed to get job information.' } - + # load job parameters into incoming tool_message = '' if job: @@ -2295,7 +2294,7 @@ # create parameter object params = galaxy.util.Params( kwd, sanitize = False ) - + # convert value to jsonifiable value def jsonify(v): # check if value is numeric @@ -2344,16 +2343,16 @@ # update and return dict[key] = value - + # check the current state of a value and update it if necessary def check_state(trans, input, default_value, context): value = default_value error = 'State validation failed.' - + # skip dynamic fields if deactivated if not is_dynamic and input.is_dynamic: return [value, None] - + # validate value content try: # resolves the inconsistent definition of boolean parameters (see base.py) without modifying shared code @@ -2365,7 +2364,7 @@ log.error('Checking parameter failed. %s', str(err)) pass return [value, error] - + # populates state with incoming url parameters def populate_state(trans, inputs, state, errors, incoming, prefix="", context=None ): context = ExpressionContext(state, context) @@ -2410,7 +2409,7 @@ if error: errors[key] = error state[input.name] = value - + # builds tool model including all attributes def iterate(group_inputs, inputs, state_inputs, other_values=None): other_values = ExpressionContext( state_inputs, other_values ) @@ -2442,13 +2441,13 @@ tool_dict = input.to_dict(trans, other_values=other_values) except Exception: pass - + # identify name input_name = tool_dict.get('name') if input_name: # backup default value tool_dict['default_value'] = input.get_initial_value(trans, other_values) - + # update input value from tool state if input_name in state_inputs: tool_dict['value'] = state_inputs[input_name] @@ -2481,7 +2480,7 @@ # do param translation here, used by datasource tools if self.input_translator: self.input_translator.translate( params ) - + # initialize and populate tool state state_inputs = {} state_errors = {} @@ -2490,7 +2489,7 @@ # create basic tool model tool_model = self.to_dict(trans) tool_model['inputs'] = {} - + # build tool model and tool state iterate(tool_model['inputs'], self.inputs, state_inputs, '') @@ -2504,18 +2503,18 @@ tool_help = tool_help.render( static_path=url_for( '/static' ), host_url=url_for('/', qualified=True) ) if type( tool_help ) is not unicode: tool_help = unicode( tool_help, 'utf-8') - + # check if citations exist tool_citations = False if self.citations: tool_citations = True - + # get tool versions tool_versions = [] tools = self.app.toolbox.get_loaded_tools_by_lineage(self.id) for t in tools: tool_versions.append(t.version) - + ## add information with underlying requirements and their versions tool_requirements = [] if self.requirements: @@ -2577,13 +2576,13 @@ except Exception, error: trans.response.status = 500 return { 'error': str (error) } - + # can't rerun upload, external data sources, et cetera. workflow compatible will proxy this for now #if not self.is_workflow_compatible: # trans.response.status = 500 # return { 'error': 'The \'%s\' tool does currently not support re-running.' % self.name } return message - + def get_default_history_by_trans( self, trans, create=False ): return trans.get_history( create=create ) diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/tools/loader.py --- a/lib/galaxy/tools/loader.py +++ b/lib/galaxy/tools/loader.py @@ -120,6 +120,7 @@ # HACK for elementtree, newer implementations (etree/lxml) won't # require this parent_map data structure but elementtree does not # track parents or recongnize .find('..'). + # TODO fix this now that we're not using elementtree parent_map = dict((c, p) for p in element.getiterator() for c in p) _xml_replace(expand_el, macro_def, parent_map) diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/tools/parameters/basic.py --- a/lib/galaxy/tools/parameters/basic.py +++ b/lib/galaxy/tools/parameters/basic.py @@ -8,7 +8,7 @@ import os import os.path import urllib -from elementtree.ElementTree import XML, Element +from xml.etree.ElementTree import XML from galaxy import config, datatypes, util from galaxy.web import form_builder from galaxy.util.bunch import Bunch diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/tools/parameters/input_translation.py --- a/lib/galaxy/tools/parameters/input_translation.py +++ b/lib/galaxy/tools/parameters/input_translation.py @@ -13,7 +13,7 @@ This is used for data source tools >>> from galaxy.util import Params - >>> from elementtree.ElementTree import XML + >>> from xml.etree.ElementTree import XML >>> translator = ToolInputTranslator.from_element( XML( ... ''' ... <request_param_translation> diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/tools/parameters/sanitize.py --- a/lib/galaxy/tools/parameters/sanitize.py +++ b/lib/galaxy/tools/parameters/sanitize.py @@ -12,7 +12,7 @@ """ Handles tool parameter specific sanitizing. - >>> from elementtree.ElementTree import XML + >>> from xml.etree.ElementTree import XML >>> sanitizer = ToolParameterSanitizer.from_element( XML( ... ''' ... <sanitizer invalid_char=""> diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/tools/parameters/validation.py --- a/lib/galaxy/tools/parameters/validation.py +++ b/lib/galaxy/tools/parameters/validation.py @@ -3,7 +3,7 @@ """ import os, re, logging -from elementtree.ElementTree import XML +from xml.etree.ElementTree import XML from galaxy import model log = logging.getLogger( __name__ ) diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/util/__init__.py --- a/lib/galaxy/util/__init__.py +++ b/lib/galaxy/util/__init__.py @@ -40,8 +40,7 @@ import docutils.core import docutils.writers.html4css1 -eggs.require( 'elementtree' ) -from elementtree import ElementTree, ElementInclude +from xml.etree import ElementTree, ElementInclude eggs.require( "wchartype" ) import wchartype @@ -372,7 +371,7 @@ if second_diff < 86400: return str(second_diff / 3600) + " hours ago" if day_diff == 1: - return "Yesterday" + return "yesterday" if day_diff < 7: return str( day_diff ) + " days ago" if day_diff < 31: @@ -382,16 +381,16 @@ return str( day_diff / 365 ) + " years ago" else: if day_diff == 0: - return "Today" + return "today" if day_diff == 1: - return "Yesterday" + return "yesterday" if day_diff < 7: - return "this week" + return "less than a week" if day_diff < 31: - return "this month" + return "less than a month" if day_diff < 365: - return "this year" - return str( day_diff / 365 ) + " years ago" + return "less than a year" + return "a few years ago" def pretty_print_json(json_data, is_json_string=False): diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/web/base/controller.py --- a/lib/galaxy/web/base/controller.py +++ b/lib/galaxy/web/base/controller.py @@ -40,6 +40,7 @@ from galaxy.managers import tags from galaxy.managers import workflows from galaxy.managers import base as managers_base +from galaxy.managers import users from galaxy.datatypes.metadata import FileParameter from galaxy.tools.parameters import visit_input_values from galaxy.tools.parameters.basic import DataToolParameter @@ -69,6 +70,7 @@ """Initialize an interface for application 'app'""" self.app = app self.sa_session = app.model.context + self.user_manager = users.UserManager( app ) def get_toolbox(self): """Returns the application toolbox""" @@ -103,9 +105,11 @@ # ---- parsing query params def decode_id( self, id ): try: - return self.app.security.decode_id( id ) - except: - msg = "Malformed History id ( %s ) specified, unable to decode" % ( str( id ) ) + # note: use str - occasionally a fully numeric id will be placed in post body and parsed as int via JSON + # resulting in error for valid id + return self.app.security.decode_id( str( id ) ) + except ( ValueError, TypeError ): + msg = "Malformed id ( %s ) specified, unable to decode" % ( str( id ) ) raise exceptions.MalformedId( msg, id=str( id ) ) def encode_all_ids( self, trans, rval, recursive=False ): diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/web/framework/webapp.py --- a/lib/galaxy/web/framework/webapp.py +++ b/lib/galaxy/web/framework/webapp.py @@ -814,3 +814,26 @@ template = Template( source=template_string, searchList=[context or kwargs, dict(caller=self)] ) return str(template) + + +def build_url_map( app, global_conf, local_conf ): + from paste.urlmap import URLMap + from galaxy.web.framework.middleware.static import CacheableStaticURLParser as Static + urlmap = URLMap() + # Merge the global and local configurations + conf = global_conf.copy() + conf.update(local_conf) + # Get cache time in seconds + cache_time = conf.get( "static_cache_time", None ) + if cache_time is not None: + cache_time = int( cache_time ) + # Send to dynamic app by default + urlmap["/"] = app + # Define static mappings from config + urlmap["/static"] = Static( conf.get( "static_dir", "./static/" ), cache_time ) + urlmap["/images"] = Static( conf.get( "static_images_dir", "./static/images" ), cache_time ) + urlmap["/static/scripts"] = Static( conf.get( "static_scripts_dir", "./static/scripts/" ), cache_time ) + urlmap["/static/style"] = Static( conf.get( "static_style_dir", "./static/style/blue" ), cache_time ) + urlmap["/favicon.ico"] = Static( conf.get( "static_favicon_dir", "./static/favicon.ico" ), cache_time ) + urlmap["/robots.txt"] = Static( conf.get( "static_robots_txt", "./static/robots.txt" ), cache_time ) + return urlmap, cache_time diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/webapps/galaxy/api/forms.py --- a/lib/galaxy/webapps/galaxy/api/forms.py +++ b/lib/galaxy/webapps/galaxy/api/forms.py @@ -5,7 +5,7 @@ from galaxy.web.base.controller import BaseAPIController, url_for from galaxy import web from galaxy.forms.forms import form_factory -from elementtree.ElementTree import XML +from xml.etree.ElementTree import XML log = logging.getLogger( __name__ ) diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/webapps/galaxy/api/provenance.py --- a/lib/galaxy/webapps/galaxy/api/provenance.py +++ b/lib/galaxy/webapps/galaxy/api/provenance.py @@ -52,15 +52,21 @@ if item.copied_from_library_dataset_dataset_association: item = item.copied_from_library_dataset_dataset_association job = item.creating_job - return { - "id": trans.security.encode_id(item.id), - "uuid": ( lambda uuid: str( uuid ) if uuid else None )( item.dataset.uuid), - "job_id": trans.security.encode_id( job.id ), - "tool_id": job.tool_id, - "parameters": self._get_job_record(trans, job, follow), - "stderr": job.stderr, - "stdout": job.stdout, - } + if job is not None: + return { + "id": trans.security.encode_id(item.id), + "uuid": ( lambda uuid: str( uuid ) if uuid else None )( item.dataset.uuid), + "job_id": trans.security.encode_id( job.id ), + "tool_id": job.tool_id, + "parameters": self._get_job_record(trans, job, follow), + "stderr": job.stderr, + "stdout": job.stdout, + } + else: + return { + "id": trans.security.encode_id(item.id), + "uuid": ( lambda uuid: str( uuid ) if uuid else None )( item.dataset.uuid) + } return None def _get_job_record(self, trans, job, follow): diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/webapps/galaxy/api/request_types.py --- a/lib/galaxy/webapps/galaxy/api/request_types.py +++ b/lib/galaxy/webapps/galaxy/api/request_types.py @@ -5,7 +5,7 @@ from galaxy.web.base.controller import BaseAPIController, url_for from galaxy import web from galaxy.sample_tracking.request_types import request_type_factory -from elementtree.ElementTree import XML +from xml.etree.ElementTree import XML log = logging.getLogger( __name__ ) diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/webapps/galaxy/buildapp.py --- a/lib/galaxy/webapps/galaxy/buildapp.py +++ b/lib/galaxy/webapps/galaxy/buildapp.py @@ -610,26 +610,8 @@ return app def wrap_in_static( app, global_conf, plugin_frameworks=None, **local_conf ): - from paste.urlmap import URLMap from galaxy.web.framework.middleware.static import CacheableStaticURLParser as Static - urlmap = URLMap() - # Merge the global and local configurations - conf = global_conf.copy() - conf.update(local_conf) - # Get cache time in seconds - cache_time = conf.get( "static_cache_time", None ) - if cache_time is not None: - cache_time = int( cache_time ) - # Send to dynamic app by default - urlmap["/"] = app - # Define static mappings from config - urlmap["/static"] = Static( conf.get( "static_dir", "./static/" ), cache_time ) - urlmap["/images"] = Static( conf.get( "static_images_dir", "./static/images" ), cache_time ) - urlmap["/static/scripts"] = Static( conf.get( "static_scripts_dir", "./static/scripts/" ), cache_time ) - urlmap["/static/style"] = Static( conf.get( "static_style_dir", "./static/style/blue" ), cache_time ) - urlmap["/favicon.ico"] = Static( conf.get( "static_favicon_dir", "./static/favicon.ico" ), cache_time ) - urlmap["/robots.txt"] = Static( conf.get( "static_robots_txt", "./static/robots.txt" ), cache_time ) - + urlmap, cache_time = galaxy.web.framework.webapp.build_url_map( app, global_conf, local_conf ) # wrap any static dirs for plugins plugin_frameworks = plugin_frameworks or [] for framework in plugin_frameworks: diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/webapps/galaxy/controllers/forms.py --- a/lib/galaxy/webapps/galaxy/controllers/forms.py +++ b/lib/galaxy/webapps/galaxy/controllers/forms.py @@ -5,7 +5,7 @@ import logging, os, sys from galaxy.web.form_builder import * from galaxy.tools.parameters.basic import parameter_types -from elementtree.ElementTree import XML, Element +from xml.etree.ElementTree import XML, Element from galaxy.util.odict import odict import copy from galaxy.web.framework.helpers import time_ago, iff, grids @@ -65,7 +65,7 @@ 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) diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/webapps/galaxy/controllers/history.py --- a/lib/galaxy/webapps/galaxy/controllers/history.py +++ b/lib/galaxy/webapps/galaxy/controllers/history.py @@ -761,7 +761,9 @@ histories=histories, email=email, send_to_err=send_to_err ) - histories, send_to_users, send_to_err = self._get_histories_and_users( trans, user, id, email ) + + histories = self._get_histories( trans, id ) + send_to_users, send_to_err = self._get_users( trans, user, email ) if not send_to_users: if not send_to_err: send_to_err += "%s is not a valid Galaxy user. %s" % ( email, err_msg ) @@ -769,14 +771,21 @@ histories=histories, email=email, send_to_err=send_to_err ) + if params.get( 'share_button', False ): + # The user has not yet made a choice about how to share, so dictionaries will be built for display can_change, cannot_change, no_change_needed, unique_no_change_needed, send_to_err = \ self._populate_restricted( trans, user, histories, send_to_users, None, send_to_err, unique=True ) + send_to_err += err_msg if cannot_change and not no_change_needed and not can_change: send_to_err = "The histories you are sharing do not contain any datasets that can be accessed by the users with which you are sharing." - return trans.fill_template( "/history/share.mako", histories=histories, email=email, send_to_err=send_to_err ) + return trans.fill_template( "/history/share.mako", + histories=histories, + email=email, + send_to_err=send_to_err ) + if can_change or cannot_change: return trans.fill_template( "/history/share.mako", histories=histories, @@ -785,12 +794,18 @@ can_change=can_change, cannot_change=cannot_change, no_change_needed=unique_no_change_needed ) + if no_change_needed: return self._share_histories( trans, user, send_to_err, histories=no_change_needed ) + elif not send_to_err: # User seems to be sharing an empty history send_to_err = "You cannot share an empty history. " - return trans.fill_template( "/history/share.mako", histories=histories, email=email, send_to_err=send_to_err ) + + return trans.fill_template( "/history/share.mako", + histories=histories, + email=email, + send_to_err=send_to_err ) @web.expose @web.require_login( "share restricted histories with other users" ) @@ -807,7 +822,8 @@ share_button=True ) ) user = trans.get_user() user_roles = user.all_roles() - histories, send_to_users, send_to_err = self._get_histories_and_users( trans, user, id, email ) + histories = self._get_histories( trans, id ) + send_to_users, send_to_err = self._get_users( trans, user, email ) send_to_err = '' # The user has made a choice, so dictionaries will be built for sharing can_change, cannot_change, no_change_needed, unique_no_change_needed, send_to_err = \ @@ -856,31 +872,49 @@ histories_for_sharing[ send_to_user ].append( history ) return self._share_histories( trans, user, send_to_err, histories=histories_for_sharing ) - def _get_histories_and_users( self, trans, user, id, email ): - if not id: + def _get_histories( self, trans, ids ): + if not ids: # Default to the current history - id = trans.security.encode_id( trans.history.id ) - id = galaxy.util.listify( id ) + ids = trans.security.encode_id( trans.history.id ) + ids = galaxy.util.listify( ids ) + histories = [] + for history_id in ids: + histories.append( self.history_manager.get_owned( trans, self.decode_id( history_id ), trans.user ) ) + return histories + + def _get_users( self, trans, user, emails_or_ids ): + send_to_users = [] send_to_err = "" - histories = [] - for history_id in id: - histories.append( self.history_manager.get_owned( trans, self.decode_id( history_id ), trans.user ) ) - send_to_users = [] - for email_address in galaxy.util.listify( email ): - email_address = email_address.strip() - if email_address: - if email_address == user.email: - send_to_err += "You cannot send histories to yourself. " - else: - send_to_user = trans.sa_session.query( trans.app.model.User ) \ - .filter( and_( trans.app.model.User.table.c.email==email_address, - trans.app.model.User.table.c.deleted==False ) ) \ - .first() - if send_to_user: - send_to_users.append( send_to_user ) - else: - send_to_err += "%s is not a valid Galaxy user. " % email_address - return histories, send_to_users, send_to_err + for string in galaxy.util.listify( emails_or_ids ): + string = string.strip() + if not string: + continue + + send_to_user = None + if '@' in string: + email_address = string + send_to_user = self.user_manager.by_email( trans, email_address, + filters=[ trans.app.model.User.table.c.deleted == False ] ) + + else: + user_id = string + try: + decoded_user_id = self.decode_id( string ) + send_to_user = self.user_manager.by_id( trans, decoded_user_id ) + if send_to_user.deleted: + send_to_user = None + #TODO: in an ideal world, we would let this bubble up to web.expose which would handle it + except exceptions.MalformedId: + send_to_user = None + + if not send_to_user: + send_to_err += "%s is not a valid Galaxy user. " % string + elif send_to_user == user: + send_to_err += "You cannot send histories to yourself. " + else: + send_to_users.append( send_to_user ) + + return send_to_users, send_to_err def _populate( self, trans, histories_for_sharing, other, send_to_err ): # This method will populate the histories_for_sharing dictionary with the users and @@ -1466,7 +1500,6 @@ trans.set_history( history ) return self.history_serializer.serialize_to_view( trans, history, view='detailed' ) except exceptions.MessageException, msg_exc: - print type( msg_exc ) trans.response.status = msg_exc.err_code.code return { 'err_msg': msg_exc.err_msg, 'err_code': msg_exc.err_code.code } diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/webapps/reports/buildapp.py --- a/lib/galaxy/webapps/reports/buildapp.py +++ b/lib/galaxy/webapps/reports/buildapp.py @@ -16,6 +16,7 @@ import galaxy.model import galaxy.model.mapping import galaxy.web.framework.webapp +from galaxy.util.properties import load_app_properties log = logging.getLogger( __name__ ) @@ -46,6 +47,9 @@ def app_factory( global_conf, **kwargs ): """Return a wsgi application serving the root object""" # Create the Galaxy application unless passed in + kwargs = load_app_properties( + kwds=kwargs + ) if 'app' in kwargs: app = kwargs.pop( 'app' ) else: @@ -62,7 +66,7 @@ # Wrap the webapp in some useful middleware if kwargs.get( 'middleware', True ): webapp = wrap_in_middleware( webapp, global_conf, **kwargs ) - if kwargs.get( 'static_enabled', True ): + if asbool( kwargs.get( 'static_enabled', True ) ): webapp = wrap_in_static( webapp, global_conf, **kwargs ) # Close any pooled database connections before forking try: @@ -135,25 +139,7 @@ return app def wrap_in_static( app, global_conf, **local_conf ): - from paste.urlmap import URLMap - from galaxy.web.framework.middleware.static import CacheableStaticURLParser as Static - urlmap = URLMap() - # Merge the global and local configurations - conf = global_conf.copy() - conf.update(local_conf) - # Get cache time in seconds - cache_time = conf.get( "static_cache_time", None ) - if cache_time is not None: - cache_time = int( cache_time ) - # Send to dynamic app by default - urlmap["/"] = app - # Define static mappings from config - urlmap["/static"] = Static( conf.get( "static_dir" ), cache_time ) - urlmap["/images"] = Static( conf.get( "static_images_dir" ), cache_time ) - urlmap["/static/scripts"] = Static( conf.get( "static_scripts_dir" ), cache_time ) - urlmap["/static/style"] = Static( conf.get( "static_style_dir" ), cache_time ) - urlmap["/favicon.ico"] = Static( conf.get( "static_favicon_dir" ), cache_time ) - # URL mapper becomes the root webapp + urlmap, _ = galaxy.web.framework.webapp.build_url_map( app, global_conf, local_conf ) return urlmap def build_template_error_formatters(): diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/webapps/tool_shed/buildapp.py --- a/lib/galaxy/webapps/tool_shed/buildapp.py +++ b/lib/galaxy/webapps/tool_shed/buildapp.py @@ -134,7 +134,7 @@ # Wrap the webapp in some useful middleware if kwargs.get( 'middleware', True ): webapp = wrap_in_middleware( webapp, global_conf, **kwargs ) - if kwargs.get( 'static_enabled', True ): + if asbool( kwargs.get( 'static_enabled', True ) ): webapp = wrap_in_static( webapp, global_conf, **kwargs ) # Close any pooled database connections before forking try: @@ -171,6 +171,14 @@ from paste import recursive app = recursive.RecursiveMiddleware( app, conf ) log.debug( "Enabling 'recursive' middleware" ) + # If sentry logging is enabled, log here before propogating up to + # the error middleware + # TODO sentry config is duplicated between tool_shed/galaxy, refactor this. + sentry_dsn = conf.get( 'sentry_dsn', None ) + if sentry_dsn: + from galaxy.web.framework.middleware.sentry import Sentry + log.debug( "Enabling 'sentry' middleware" ) + app = Sentry( app, sentry_dsn ) # Various debug middleware that can only be turned on if the debug # flag is set, either because they are insecure or greatly hurt # performance @@ -219,26 +227,7 @@ return app def wrap_in_static( app, global_conf, **local_conf ): - from paste.urlmap import URLMap - from galaxy.web.framework.middleware.static import CacheableStaticURLParser as Static - urlmap = URLMap() - # Merge the global and local configurations - conf = global_conf.copy() - conf.update(local_conf) - # Get cache time in seconds - cache_time = conf.get( "static_cache_time", None ) - if cache_time is not None: - cache_time = int( cache_time ) - # Send to dynamic app by default - urlmap["/"] = app - # Define static mappings from config - urlmap["/static"] = Static( conf.get( "static_dir", "./static/" ), cache_time ) - urlmap["/images"] = Static( conf.get( "static_images_dir", "./static/images" ), cache_time ) - urlmap["/static/scripts"] = Static( conf.get( "static_scripts_dir", "./static/scripts/" ), cache_time ) - urlmap["/static/style"] = Static( conf.get( "static_style_dir", "./static/style/blue" ), cache_time ) - urlmap["/favicon.ico"] = Static( conf.get( "static_favicon_dir", "./static/favicon.ico" ), cache_time ) - urlmap["/robots.txt"] = Static( conf.get( "static_robots_txt", "./static/robots.txt" ), cache_time ) - # URL mapper becomes the root webapp + urlmap, _ = galaxy.web.framework.webapp.build_url_map( app, global_conf, local_conf ) return urlmap def build_template_error_formatters(): diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/webapps/tool_shed/config.py --- a/lib/galaxy/webapps/tool_shed/config.py +++ b/lib/galaxy/webapps/tool_shed/config.py @@ -2,11 +2,13 @@ Universe configuration builder. """ import os +import re import sys import logging import logging.config from optparse import OptionParser import ConfigParser +from galaxy import eggs from galaxy.util import string_as_bool, listify log = logging.getLogger( __name__ ) @@ -133,6 +135,19 @@ self.citation_cache_data_dir = resolve_path( kwargs.get( "citation_cache_data_dir", "database/tool_shed_citations/data" ), self.root ) self.citation_cache_lock_dir = resolve_path( kwargs.get( "citation_cache_lock_dir", "database/tool_shed_citations/locks" ), self.root ) + @property + def sentry_dsn_public( self ): + """ + Sentry URL with private key removed for use in client side scripts, + sentry server will need to be configured to accept events + """ + # TODO refactor this to a common place between toolshed/galaxy config, along + # with other duplicated methods. + if self.sentry_dsn: + return re.sub( r"^([^:/?#]+:)?//(\w+):(\w+)", r"\1//\2", self.sentry_dsn ) + else: + return None + def __parse_config_file_options( self, kwargs ): defaults = dict( datatypes_config_file = [ 'config/datatypes_conf.xml', 'datatypes_conf.xml', 'config/datatypes_conf.xml.sample' ], @@ -274,3 +289,11 @@ # Hook everything up handler.setFormatter( formatter ) root.addHandler( handler ) + # If sentry is configured, also log to it + if config.sentry_dsn: + eggs.require( "raven" ) + from raven.handlers.logging import SentryHandler + sentry_handler = SentryHandler( config.sentry_dsn ) + sentry_handler.setLevel( logging.WARN ) + root.addHandler( sentry_handler ) + diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/webapps/tool_shed/search/repo_search.py --- a/lib/galaxy/webapps/tool_shed/search/repo_search.py +++ b/lib/galaxy/webapps/tool_shed/search/repo_search.py @@ -45,7 +45,7 @@ use_final = True def final( self, searcher, docnum, score ): - log.debug('score before: ' + str(score) ) + # log.debug('score before: ' + str(score) ) # Arbitrary for now reasonable_hits = 100.0 @@ -59,15 +59,15 @@ if times_downloaded == 0: times_downloaded = 1 popularity_modifier = ( times_downloaded / reasonable_hits ) - log.debug('popularity_modifier: ' + str(popularity_modifier) ) + # log.debug('popularity_modifier: ' + str(popularity_modifier) ) cert_modifier = 2 if searcher.stored_fields( docnum )[ "approved" ] == 'yes' else 1 - log.debug('cert_modifier: ' + str(cert_modifier) ) + # log.debug('cert_modifier: ' + str(cert_modifier) ) # Adjust the computed score for this document by the popularity # and by the certification level. final_score = score * popularity_modifier * cert_modifier - log.debug('score after: ' + str( final_score ) ) + # log.debug('score after: ' + str( final_score ) ) return final_score @@ -108,16 +108,14 @@ 'remote_repository_url', 'repo_owner_username' ], schema = schema ) - # user_query = parser.parse( search_term ) user_query = parser.parse( '*' + search_term + '*' ) + hits = searcher.search_page( user_query, page, pagelen = 10, terms = True ) - # hits = searcher.search( user_query, terms = True ) - hits = searcher.search_page( user_query, page, pagelen = 10, terms = True ) log.debug( 'searching for: #' + str( search_term ) ) log.debug( 'total hits: ' + str( len( hits ) ) ) log.debug( 'scored hits: ' + str( hits.scored_length() ) ) results = {} - results[ 'total_results'] = str( hits.scored_length() ) + results[ 'total_results'] = str( len( hits ) ) results[ 'hits' ] = [] for hit in hits: hit_dict = {} diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef lib/galaxy/workflow/modules.py --- a/lib/galaxy/workflow/modules.py +++ b/lib/galaxy/workflow/modules.py @@ -6,9 +6,8 @@ import re from galaxy import eggs -eggs.require( "elementtree" ) -from elementtree.ElementTree import Element +from xml.etree.ElementTree import Element import galaxy.tools from galaxy import exceptions diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef scripts/loc_files/create_all_fasta_loc.py --- a/scripts/loc_files/create_all_fasta_loc.py +++ b/scripts/loc_files/create_all_fasta_loc.py @@ -1,5 +1,5 @@ import optparse, os, sys -import elementtree.ElementTree as etree +from xml.etree.ElementTree import parse """ Generates a loc file containing names of all the fasta files that match the @@ -212,7 +212,7 @@ # get column names col_values = [] loc_path = None - tree = etree.parse( options.data_table_xml ) + tree = parse( options.data_table_xml ) tables = tree.getroot() for table in tables.getiterator(): name = table.attrib.get( 'name' ) diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef scripts/microbes/ncbi_to_ucsc.py --- a/scripts/microbes/ncbi_to_ucsc.py +++ b/scripts/microbes/ncbi_to_ucsc.py @@ -7,7 +7,7 @@ import sys, os import urllib -from elementtree import ElementTree +from xml.etree import ElementTree from BeautifulSoup import BeautifulSoup from shutil import move @@ -17,7 +17,7 @@ base_dir = sys.argv[1] except: print "using default base_dir:", base_dir - + organisms = {} for result in os.walk(base_dir): this_base_dir,sub_dirs,files = result @@ -76,7 +76,7 @@ org_page.pop(0) if org_page[-1]=="": org_page.pop(-1) - + for row in org_page: chr = row.split("</a>")[0].split(">")[-1] refseq = row.split("</a>")[-2].split(">")[-1] @@ -87,20 +87,20 @@ builds[org]={'chrs':{},'build':build} builds[org]['chrs'][refseq]=chr #print build,org,chr,refseq - + print ext_to_edit = ['bed', 'info', ] for org in builds: print org,"changed to",builds[org]['build'] - + #org info file info_file_old = os.path.join(base_dir+org,org+".info") info_file_new = os.path.join(base_dir+org,builds[org]['build']+".info") - - + + old_dir = base_dir+org new_dir = base_dir+builds[org]['build'] - + #open and edit org info file info_file_contents = open(info_file_old).read() info_file_contents = info_file_contents+"build="+builds[org]['build']+"\n" @@ -114,31 +114,31 @@ old_name = os.path.join(this_base_dir,file) new_name = os.path.join(this_base_dir,builds[org]['chrs'][chrom]+file[len(chrom):]) move(old_name,new_name) - + #edit contents of file, skiping those in list if file.split(".")[-1] not in ext_to_edit: continue - + file_contents = open(new_name).read() file_contents = file_contents.replace(chrom,builds[org]['chrs'][chrom]) - + #special case fixes... if file[-5:] == ".info": file_contents = file_contents.replace("organism="+org,"organism="+builds[org]['build']) file_contents = file_contents.replace("refseq="+builds[org]['chrs'][chrom],"refseq="+chrom) - + #write out new file file_out = open(new_name,'w') file_out.write(file_contents) file_out.close() - - - + + + #write out org info file and remove old file org_info_out = open(info_file_new,'w') org_info_out.write(info_file_contents) org_info_out.close() os.unlink(info_file_old) - + #change org directory name move(old_dir,new_dir) diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef scripts/update_shed_config_path.py --- /dev/null +++ b/scripts/update_shed_config_path.py @@ -0,0 +1,80 @@ +import os +import argparse +import ConfigParser +import sys +new_path = [ os.path.join( os.getcwd(), "lib" ) ] +new_path.extend( sys.path[1:] ) +sys.path = new_path + +import logging +import galaxy.model.tool_shed_install +import galaxy.model.tool_shed_install.mapping as mapping +from galaxy.model.orm import * +from galaxy import eggs +eggs.require('sqlalchemy') +import sqlalchemy + +def main( opts, session, model ): + ''' + Find all tool shed repositories with the bad path and update with the correct path. + ''' + for row in session.query( model.ToolShedRepository ).all(): + if 'shed_config_filename' in row.metadata: + if row.metadata['shed_config_filename'] == opts.bad_filename: + row.metadata['shed_config_filename'] = opts.good_filename + session.add( row ) + session.flush() + return 0 + +def create_database( config_file ): + parser = ConfigParser.SafeConfigParser() + parser.read( config_file ) + # Determine which database connection to use. + database_connection = parser.get( 'app:main', 'install_database_connection' ) + if database_connection is None: + database_connection = parser.get( 'app:main', 'database_connection' ) + if database_connection is None: + database_connection = 'sqlite:///%s' % parser.get( 'app:main', 'database_file' ) + if database_connection is None: + print 'Unable to determine correct database connection.' + exit(1) + + '''Initialize the database file.''' + dialect_to_egg = { + "sqlite" : "pysqlite>=2", + "postgres" : "psycopg2", + "postgresql" : "psycopg2", + "mysql" : "MySQL_python" + } + dialect = ( database_connection.split( ':', 1 ) )[0] + try: + egg = dialect_to_egg[ dialect ] + try: + eggs.require( egg ) + print( "%s egg successfully loaded for %s dialect" % ( egg, dialect ) ) + except: + # If the module is in the path elsewhere (i.e. non-egg), it'll still load. + print( "%s egg not found, but an attempt will be made to use %s anyway" % ( egg, dialect ) ) + except KeyError: + # Let this go, it could possibly work with db's we don't support. + print( "database_connection contains an unknown SQLAlchemy database dialect: %s" % dialect ) + + # Initialize the database connection. + engine = create_engine( database_connection ) + meta = MetaData( bind=engine ) + install_session = Session = scoped_session( sessionmaker( bind=engine, autoflush=False, autocommit=True ) ) + model = mapping.init( database_connection ) + return install_session, model + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument( '--config_file', dest='config_file', required=True, help="The path to your Galaxy configuration .ini file." ) + parser.add_argument( '--from', dest='bad_filename', required=True, help="The old, invalid path to the shed_tool_conf.xml or migrated_tools_conf.xml file." ) + parser.add_argument( '--to', dest='good_filename', required=True, help="The updated path to the shed_tool_conf.xml or migrated_tools_conf.xml file." ) + parser.add_argument( '--force', dest='force', action='store_true', help="Use this flag to set the new path even if the file does not (yet) exist there." ) + opts = parser.parse_args() + if not os.path.exists( opts.good_filename ) and not opts.force: + print 'The file %s does not exist, use the --force option to proceed.' % opts.good_filename + exit(1) + session, model = create_database( opts.config_file ) + exit( main( opts, session, model ) ) diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef static/scripts/galaxy.workflow_editor.canvas.js --- a/static/scripts/galaxy.workflow_editor.canvas.js +++ b/static/scripts/galaxy.workflow_editor.canvas.js @@ -1870,6 +1870,9 @@ left: x, top: y }); + self.cv.css( { "background-position-x": x, + "background-position-y": y + }); self.update_viewport_overlay(); }; // Dragging within canvas background diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef static/scripts/mvc/history/history-panel-edit.js --- a/static/scripts/mvc/history/history-panel-edit.js +++ b/static/scripts/mvc/history/history-panel-edit.js @@ -75,13 +75,17 @@ /** Override to handle history as drag-drop target */ _setUpListeners : function(){ - _super.prototype._setUpListeners.call( this ); + var panel = this; + _super.prototype._setUpListeners.call( panel ); - this.on( 'drop', function( ev, data ){ - this.dataDropped( data ); + panel.on( 'drop', function( ev, data ){ + panel.dataDropped( data ); // remove the drop target - this.dropTargetOff(); + panel.dropTargetOff(); }); + panel.on( 'view:attached view:removed', function(){ + panel._renderCounts(); + }, panel ); }, // ------------------------------------------------------------------------ listeners @@ -332,7 +336,10 @@ // override to control where the view is added, how/whether it's rendered panel.views.unshift( view ); panel.$list().prepend( view.render( 0 ).$el.hide() ); - view.$el.slideDown( panel.fxSpeed ); + panel.trigger( 'view:attached', view ); + view.$el.slideDown( panel.fxSpeed, function(){ + panel.trigger( 'view:attached:rendered' ); + }); }, /** In this override, add purgeAllowed and whether tags/annotation editors should be shown */ diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef static/scripts/mvc/list/list-panel.js --- a/static/scripts/mvc/list/list-panel.js +++ b/static/scripts/mvc/list/list-panel.js @@ -477,7 +477,10 @@ // override to control where the view is added, how/whether it's rendered panel.views.push( view ); panel.$list().append( view.render( 0 ).$el.hide() ); - view.$el.slideDown( panel.fxSpeed ); + panel.trigger( 'view:attached', view ); + view.$el.slideDown( panel.fxSpeed, function(){ + panel.trigger( 'view:attached:rendered' ); + }); }, /** Remove a view from the panel (if found) */ @@ -487,6 +490,7 @@ view = panel.viewFromModel( model ); if( !view ){ return undefined; } panel.views = _.without( panel.views, view ); + panel.trigger( 'view:removed', view ); // potentially show the empty message if no views left // use anonymous queue here - since remove can happen multiple times @@ -494,6 +498,7 @@ function( next ){ view.$el.fadeOut( panel.fxSpeed, next ); }, function( next ){ view.remove(); + panel.trigger( 'view:removed:rendered' ); if( !panel.views.length ){ panel._renderEmptyMessage().fadeIn( panel.fxSpeed, next ); } else { diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef static/scripts/mvc/upload/upload-row.js --- a/static/scripts/mvc/upload/upload-row.js +++ b/static/scripts/mvc/upload/upload-row.js @@ -69,7 +69,7 @@ css: 'genome', onchange : function(genome) { self.model.set('genome', genome); - self.app.updateGenome(genome); + self.app.updateGenome(genome, true); }, data: self.app.list_genomes, container: it.find('#genome'), @@ -87,7 +87,7 @@ css: 'extension', onchange : function(extension) { self.model.set('extension', extension); - self.app.updateExtension(extension); + self.app.updateExtension(extension, true); }, data: self.app.list_extensions, container: it.find('#extension'), diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef static/scripts/mvc/upload/upload-view.js --- a/static/scripts/mvc/upload/upload-view.js +++ b/static/scripts/mvc/upload/upload-view.js @@ -26,6 +26,10 @@ nginx_upload_path : '' }, + // default for selection fields + default_extension : 'auto', + default_genome : '?', + // own modal modal : null, @@ -130,16 +134,13 @@ // sort self.list_extensions.sort(function(a, b) { - return a.id > b.id ? 1 : a.id < b.id ? -1 : 0; + return a.text > b.text ? 1 : a.text < b.text ? -1 : 0; }); // add auto field if (!self.options.datatypes_disable_auto) { self.list_extensions.unshift(self.auto); } - - // set default extension - self.default_extension = self.list_extensions[0] && self.list_extensions[0].id; } }); @@ -156,11 +157,10 @@ // sort self.list_genomes.sort(function(a, b) { - return a.id > b.id ? 1 : a.id < b.id ? -1 : 0; + if (a.id == self.default_genome) { return -1; } + if (b.id == self.default_genome) { return 1; } + return a.text > b.text ? 1 : a.text < b.text ? -1 : 0; }); - - // set default genome - self.default_genome = self.list_genomes[0] && self.list_genomes[0].id; } }); @@ -585,20 +585,20 @@ }, // update extension for all models - updateExtension: function(extension) { + updateExtension: function(extension, defaults_only) { var self = this; this.collection.each(function(item) { - if (item.get('status') == 'init' && item.get('extension') == self.default_extension) { + if (item.get('status') == 'init' && (item.get('extension') == self.default_extension || !defaults_only)) { item.set('extension', extension); } }); }, // update genome for all models - updateGenome: function(genome) { + updateGenome: function(genome, defaults_only) { var self = this; this.collection.each(function(item) { - if (item.get('status') == 'init' && item.get('genome') == self.default_genome) { + if (item.get('status') == 'init' && (item.get('genome') == self.default_genome || !defaults_only)) { item.set('genome', genome); } }); @@ -724,10 +724,10 @@ '</table>' + '</div>' + '<div id="upload-header" class="upload-header">' + - '<span class="header-title">Type (default):</span>' + + '<span class="header-title">Type (set all):</span>' + '<span id="header-extension"/>' + '<span id="header-extension-info" class="upload-icon-button fa fa-search"/> ' + - '<span class="header-title">Genome (default):</span>' + + '<span class="header-title">Genome (set all):</span>' + '<span id="header-genome"/>' + '</div>'; } diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef static/scripts/packed/galaxy.workflow_editor.canvas.js --- a/static/scripts/packed/galaxy.workflow_editor.canvas.js +++ b/static/scripts/packed/galaxy.workflow_editor.canvas.js @@ -1,1 +1,1 @@ -function CollectionTypeDescription(a){this.collectionType=a;this.isCollection=true;this.rank=a.split(":").length}$.extend(CollectionTypeDescription.prototype,{append:function(a){if(a===NULL_COLLECTION_TYPE_DESCRIPTION){return this}if(a===ANY_COLLECTION_TYPE_DESCRIPTION){return otherCollectionType}return new CollectionTypeDescription(this.collectionType+":"+a.collectionType)},canMatch:function(a){if(a===NULL_COLLECTION_TYPE_DESCRIPTION){return false}if(a===ANY_COLLECTION_TYPE_DESCRIPTION){return true}return a.collectionType==this.collectionType},canMapOver:function(b){if(b===NULL_COLLECTION_TYPE_DESCRIPTION){return false}if(b===ANY_COLLECTION_TYPE_DESCRIPTION){return false}if(this.rank<=b.rank){return false}var a=b.collectionType;return this._endsWith(this.collectionType,a)},effectiveMapOver:function(a){var c=a.collectionType;var b=this.collectionType.substring(0,this.collectionType.length-c.length-1);return new CollectionTypeDescription(b)},equal:function(a){return a.collectionType==this.collectionType},toString:function(){return"CollectionType["+this.collectionType+"]"},_endsWith:function(b,a){return b.indexOf(a,b.length-a.length)!==-1}});NULL_COLLECTION_TYPE_DESCRIPTION={isCollection:false,canMatch:function(a){return false},canMapOver:function(a){return false},toString:function(){return"NullCollectionType[]"},append:function(a){return a},equal:function(a){return a===this}};ANY_COLLECTION_TYPE_DESCRIPTION={isCollection:true,canMatch:function(a){return NULL_COLLECTION_TYPE_DESCRIPTION!==a},canMapOver:function(a){return false},toString:function(){return"AnyCollectionType[]"},append:function(a){throw"Cannot append to ANY_COLLECTION_TYPE_DESCRIPTION"},equal:function(a){return a===this}};var TerminalMapping=Backbone.Model.extend({initialize:function(a){this.mapOver=a.mapOver||NULL_COLLECTION_TYPE_DESCRIPTION;this.terminal=a.terminal;this.terminal.terminalMapping=this},disableMapOver:function(){this.setMapOver(NULL_COLLECTION_TYPE_DESCRIPTION)},setMapOver:function(a){this.mapOver=a;this.trigger("change")}});var TerminalMappingView=Backbone.View.extend({tagName:"div",className:"fa-icon-button fa fa-folder-o",initialize:function(b){var a="Run tool in parallel over collection";this.$el.tooltip({delay:500,title:a});this.model.bind("change",_.bind(this.render,this))},render:function(){if(this.model.mapOver.isCollection){this.$el.show()}else{this.$el.hide()}},});var InputTerminalMappingView=TerminalMappingView.extend({events:{click:"onClick",mouseenter:"onMouseEnter",mouseleave:"onMouseLeave",},onMouseEnter:function(b){var a=this.model;if(!a.terminal.connected()&&a.mapOver.isCollection){this.$el.css("color","red")}},onMouseLeave:function(a){this.$el.css("color","black")},onClick:function(b){var a=this.model;if(!a.terminal.connected()&&a.mapOver.isCollection){a.terminal.resetMapping()}},});var InputTerminalMapping=TerminalMapping;var InputCollectionTerminalMapping=TerminalMapping;var OutputTerminalMapping=TerminalMapping;var OutputTerminalMappingView=TerminalMappingView;var InputCollectionTerminalMappingView=InputTerminalMappingView;var OutputCollectionTerminalMapping=TerminalMapping;var OutputCollectionTerminalMappingView=TerminalMappingView;var Terminal=Backbone.Model.extend({initialize:function(a){this.element=a.element;this.connectors=[]},connect:function(a){this.connectors.push(a);if(this.node){this.node.markChanged()}},disconnect:function(a){this.connectors.splice($.inArray(a,this.connectors),1);if(this.node){this.node.markChanged();this.resetMappingIfNeeded()}},redraw:function(){$.each(this.connectors,function(a,b){b.redraw()})},destroy:function(){$.each(this.connectors.slice(),function(a,b){b.destroy()})},destroyInvalidConnections:function(){_.each(this.connectors,function(a){a.destroyIfInvalid()})},setMapOver:function(a){if(this.multiple){return}if(!this.mapOver().equal(a)){this.terminalMapping.setMapOver(a);_.each(this.node.output_terminals,function(b){b.setMapOver(a)})}},mapOver:function(){if(!this.terminalMapping){return NULL_COLLECTION_TYPE_DESCRIPTION}else{return this.terminalMapping.mapOver}},isMappedOver:function(){return this.terminalMapping&&this.terminalMapping.mapOver.isCollection},resetMapping:function(){this.terminalMapping.disableMapOver()},resetMappingIfNeeded:function(){},});var OutputTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.datatypes=a.datatypes},resetMappingIfNeeded:function(){if(!this.node.hasConnectedOutputTerminals()&&!this.node.hasConnectedMappedInputTerminals()){_.each(this.node.mappedInputTerminals(),function(b){b.resetMappingIfNeeded()})}var a=!this.node.hasMappedOverInputTerminals();if(a){this.resetMapping()}},resetMapping:function(){this.terminalMapping.disableMapOver();_.each(this.connectors,function(a){var b=a.handle2;if(b){b.resetMappingIfNeeded();a.destroyIfInvalid()}})}});var BaseInputTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.update(a.input)},canAccept:function(a){if(this._inputFilled()){return false}else{return this.attachable(a)}},resetMappingIfNeeded:function(){var b=this.mapOver();if(!b.isCollection){return}var a=this.node.hasConnectedMappedInputTerminals()||(!this.node.hasConnectedOutputTerminals());if(a){this.resetMapping()}},resetMapping:function(){this.terminalMapping.disableMapOver();if(!this.node.hasMappedOverInputTerminals()){_.each(this.node.output_terminals,function(a){a.resetMapping()})}},connected:function(){return this.connectors.length!==0},_inputFilled:function(){var a;if(!this.connected()){a=false}else{if(this.multiple){if(this._collectionAttached()){inputsFilled=true}else{a=false}}else{a=true}}return a},_collectionAttached:function(){if(!this.connected()){return false}else{var a=this.connectors[0].handle1;if(!a){return false}else{if(a.isDataCollectionInput||a.isMappedOver()||a.datatypes.indexOf("input_collection")>0){return true}else{return false}}}},_mappingConstraints:function(){if(!this.node){return[]}var b=this.mapOver();if(b.isCollection){return[b]}var a=[];if(!this.node.hasConnectedOutputTerminals()){_.each(this.node.connectedMappedInputTerminals(),function(c){a.push(c.mapOver())})}else{a.push(_.first(_.values(this.node.output_terminals)).mapOver())}return a},_producesAcceptableDatatype:function(a){for(var c in this.datatypes){var f=new Array();f=f.concat(a.datatypes);if(a.node.post_job_actions){for(var d in a.node.post_job_actions){var g=a.node.post_job_actions[d];if(g.action_type=="ChangeDatatypeAction"&&(g.output_name==""||g.output_name==a.name)&&g.action_arguments){f.push(g.action_arguments.newtype)}}}for(var b in f){var h=f[b];if(h=="input"||h=="input_collection"||issubtype(f[b],this.datatypes[c])){return true}}}return false},_otherCollectionType:function(a){var c=NULL_COLLECTION_TYPE_DESCRIPTION;if(a.isDataCollectionInput){c=a.collectionType}else{var b=a.mapOver();if(b.isCollection){c=b}}return c},});var InputTerminal=BaseInputTerminal.extend({update:function(a){this.datatypes=a.extensions;this.multiple=a.multiple;this.collection=false},connect:function(a){BaseInputTerminal.prototype.connect.call(this,a);var b=a.handle1;if(!b){return}var c=this._otherCollectionType(b);if(c.isCollection){this.setMapOver(c)}},attachable:function(b){var d=this._otherCollectionType(b);var a=this.mapOver();if(d.isCollection){if(this.multiple){if(this.connected()&&!this._collectionAttached()){return false}if(d.rank==1){return this._producesAcceptableDatatype(b)}else{return false}}if(a.isCollection&&a.canMatch(d)){return this._producesAcceptableDatatype(b)}else{var c=this._mappingConstraints();if(c.every(_.bind(d.canMatch,d))){return this._producesAcceptableDatatype(b)}else{return false}}}else{if(a.isCollection){return false}}return this._producesAcceptableDatatype(b)}});var InputCollectionTerminal=BaseInputTerminal.extend({update:function(a){this.multiple=false;this.collection=true;this.datatypes=a.extensions;if(a.collection_type){this.collectionType=new CollectionTypeDescription(a.collection_type)}else{this.collectionType=ANY_COLLECTION_TYPE_DESCRIPTION}},connect:function(b){BaseInputTerminal.prototype.connect.call(this,b);var a=b.handle1;if(!a){return}var c=this._effectiveMapOver(a);this.setMapOver(c)},_effectiveMapOver:function(a){var b=this.collectionType;var c=this._otherCollectionType(a);if(!b.canMatch(c)){return c.effectiveMapOver(b)}else{return NULL_COLLECTION_TYPE_DESCRIPTION}},_effectiveCollectionType:function(){var b=this.collectionType;var a=this.mapOver();return a.append(b)},attachable:function(b){var g=this._otherCollectionType(b);if(g.isCollection){var f=this._effectiveCollectionType();var a=this.mapOver();if(f.canMatch(g)){return this._producesAcceptableDatatype(b)}else{if(a.isCollection){return false}else{if(g.canMapOver(this.collectionType)){var d=this._effectiveMapOver(b);if(!d.isCollection){return false}var c=this._mappingConstraints();if(c.every(d.canMatch)){return this._producesAcceptableDatatype(b)}}}}}return false}});var OutputCollectionTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.datatypes=a.datatypes;this.collectionType=new CollectionTypeDescription(a.collection_type);this.isDataCollectionInput=true},update:function(a){var b=new CollectionTypeDescription(a.collection_type);if(b.collectionType!=this.collectionType.collectionType){_.each(this.connectors,function(c){c.destroy()})}this.collectionType=b}});function Connector(b,a){this.canvas=null;this.dragging=false;this.inner_color="#FFFFFF";this.outer_color="#D8B365";if(b&&a){this.connect(b,a)}}$.extend(Connector.prototype,{connect:function(b,a){this.handle1=b;if(this.handle1){this.handle1.connect(this)}this.handle2=a;if(this.handle2){this.handle2.connect(this)}},destroy:function(){if(this.handle1){this.handle1.disconnect(this)}if(this.handle2){this.handle2.disconnect(this)}$(this.canvas).remove()},destroyIfInvalid:function(){if(this.handle1&&this.handle2&&!this.handle2.attachable(this.handle1)){this.destroy()}},redraw:function(){var f=$("#canvas-container");if(!this.canvas){this.canvas=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(this.canvas)}f.append($(this.canvas));if(this.dragging){this.canvas.style.zIndex="300"}}var v=function(c){return $(c).offset().left-f.offset().left};var p=function(c){return $(c).offset().top-f.offset().top};if(!this.handle1||!this.handle2){return}var o=v(this.handle1.element)+5;var n=p(this.handle1.element)+5;var x=v(this.handle2.element)+5;var u=p(this.handle2.element)+5;var k=100;var r=Math.min(o,x);var a=Math.max(o,x);var q=Math.min(n,u);var B=Math.max(n,u);var d=Math.min(Math.max(Math.abs(B-q)/2,100),300);var w=r-k;var A=q-k;var y=a-r+2*k;var s=B-q+2*k;this.canvas.style.left=w+"px";this.canvas.style.top=A+"px";this.canvas.setAttribute("width",y);this.canvas.setAttribute("height",s);o-=w;n-=A;x-=w;u-=A;var z=this.canvas.getContext("2d"),h=null,l=null;var g=1;if(this.handle1&&this.handle1.isMappedOver()){var h=[-6,-3,0,3,6];g=5}else{var h=[0]}if(this.handle2&&this.handle2.isMappedOver()){var l=[-6,-3,0,3,6];g=5}else{var l=[0]}var b=this;for(var t=0;t<g;t++){var m=5,j=7;if(h.length>1||l.length>1){m=1;j=3}b.draw_outlined_curve(o,n,x,u,d,m,j,h[t%h.length],l[t%l.length])}},draw_outlined_curve:function(j,i,l,k,a,b,f,g,d){var g=g||0;var d=d||0;var h=this.canvas.getContext("2d");h.lineCap="round";h.strokeStyle=this.outer_color;h.lineWidth=f;h.beginPath();h.moveTo(j,i+g);h.bezierCurveTo(j+a,i+g,l-a,k+d,l,k+d);h.stroke();h.strokeStyle=this.inner_color;h.lineWidth=b;h.beginPath();h.moveTo(j,i+g);h.bezierCurveTo(j+a,i+g,l-a,k+d,l,k+d);h.stroke()}});var Node=Backbone.Model.extend({initialize:function(a){this.element=a.element;this.input_terminals={};this.output_terminals={};this.tool_errors={}},connectedOutputTerminals:function(){return this._connectedTerminals(this.output_terminals)},_connectedTerminals:function(b){var a=[];$.each(b,function(c,d){if(d.connectors.length>0){a.push(d)}});return a},hasConnectedOutputTerminals:function(){var a=this.output_terminals;for(var b in a){if(a[b].connectors.length>0){return true}}return false},connectedMappedInputTerminals:function(){return this._connectedMappedTerminals(this.input_terminals)},hasConnectedMappedInputTerminals:function(){var c=this.input_terminals;for(var b in c){var a=c[b];if(a.connectors.length>0&&a.isMappedOver()){return true}}return false},_connectedMappedTerminals:function(b){var a=[];$.each(b,function(c,d){var f=d.mapOver();if(f.isCollection){if(d.connectors.length>0){a.push(d)}}});return a},mappedInputTerminals:function(){return this._mappedTerminals(this.input_terminals)},_mappedTerminals:function(b){var a=[];$.each(b,function(c,d){var f=d.mapOver();if(f.isCollection){a.push(d)}});return a},hasMappedOverInputTerminals:function(){var a=false;_.each(this.input_terminals,function(b){var c=b.mapOver();if(c.isCollection){a=true}});return a},redraw:function(){$.each(this.input_terminals,function(a,b){b.redraw()});$.each(this.output_terminals,function(a,b){b.redraw()})},destroy:function(){$.each(this.input_terminals,function(a,b){b.destroy()});$.each(this.output_terminals,function(a,b){b.destroy()});workflow.remove_node(this);$(this.element).remove()},make_active:function(){$(this.element).addClass("toolForm-active")},make_inactive:function(){var a=this.element.get(0);(function(b){b.removeChild(a);b.appendChild(a)})(a.parentNode);$(a).removeClass("toolForm-active")},init_field_data:function(b){if(b.type){this.type=b.type}this.name=b.name;this.form_html=b.form_html;this.tool_state=b.tool_state;this.tool_errors=b.tool_errors;this.tooltip=b.tooltip?b.tooltip:"";this.annotation=b.annotation;this.post_job_actions=b.post_job_actions?b.post_job_actions:{};this.label=b.label;this.uuid=b.uuid;this.workflow_outputs=b.workflow_outputs?b.workflow_outputs:[];var a=this;var c=new NodeView({el:this.element[0],node:a,});a.nodeView=c;$.each(b.data_inputs,function(f,d){c.addDataInput(d)});if((b.data_inputs.length>0)&&(b.data_outputs.length>0)){c.addRule()}$.each(b.data_outputs,function(f,d){c.addDataOutput(d)});c.render();workflow.node_changed(this,true)},update_field_data:function(d){var c=this;nodeView=c.nodeView;this.tool_state=d.tool_state;this.form_html=d.form_html;this.tool_errors=d.tool_errors;this.annotation=d.annotation;if("post_job_actions" in d){var f=$.parseJSON(d.post_job_actions);this.post_job_actions=f?f:{}}c.nodeView.renderToolErrors();var g=nodeView.$("div.inputs");var a=nodeView.newInputsDiv();var b={};_.each(d.data_inputs,function(h){var i=c.nodeView.addDataInput(h,a);b[h.name]=i});_.each(_.difference(_.values(nodeView.terminalViews),_.values(b)),function(h){h.el.terminal.destroy()});nodeView.terminalViews=b;if(d.data_outputs.length==1&&"collection_type" in d.data_outputs[0]){nodeView.updateDataOutput(d.data_outputs[0])}g.replaceWith(a);this.markChanged();this.redraw()},error:function(d){var a=$(this.element).find(".toolFormBody");a.find("div").remove();var c="<div style='color: red; text-style: italic;'>"+d+"</div>";this.form_html=c;a.html(c);workflow.node_changed(this)},markChanged:function(){workflow.node_changed(this)}});function Workflow(a){this.canvas_container=a;this.id_counter=0;this.nodes={};this.name=null;this.has_changes=false;this.active_form_has_changes=false}$.extend(Workflow.prototype,{add_node:function(a){a.id=this.id_counter;a.element.attr("id","wf-node-step-"+a.id);this.id_counter++;this.nodes[a.id]=a;this.has_changes=true;a.workflow=this},remove_node:function(a){if(this.active_node==a){this.clear_active_node()}delete this.nodes[a.id];this.has_changes=true},remove_all:function(){wf=this;$.each(this.nodes,function(b,a){a.destroy();wf.remove_node(a)})},rectify_workflow_outputs:function(){var b=false;var a=false;$.each(this.nodes,function(c,d){if(d.workflow_outputs&&d.workflow_outputs.length>0){b=true}$.each(d.post_job_actions,function(g,f){if(f.action_type==="HideDatasetAction"){a=true}})});if(b!==false||a!==false){$.each(this.nodes,function(c,g){if(g.type==="tool"){var f=false;if(g.post_job_actions==null){g.post_job_actions={};f=true}var d=[];$.each(g.post_job_actions,function(i,h){if(h.action_type=="HideDatasetAction"){d.push(i)}});if(d.length>0){$.each(d,function(h,j){f=true;delete g.post_job_actions[j]})}if(b){$.each(g.output_terminals,function(i,j){var h=true;$.each(g.workflow_outputs,function(l,m){if(j.name===m){h=false}});if(h===true){f=true;var k={action_type:"HideDatasetAction",output_name:j.name,action_arguments:{}};g.post_job_actions["HideDatasetAction"+j.name]=null;g.post_job_actions["HideDatasetAction"+j.name]=k}})}if(workflow.active_node==g&&f===true){workflow.reload_active_node()}}})}},to_simple:function(){var a={};$.each(this.nodes,function(c,f){var g={};$.each(f.input_terminals,function(i,j){g[j.name]=null;var h=[];$.each(j.connectors,function(k,l){h[k]={id:l.handle1.node.id,output_name:l.handle1.name};g[j.name]=h})});var b={};if(f.post_job_actions){$.each(f.post_job_actions,function(j,h){var k={action_type:h.action_type,output_name:h.output_name,action_arguments:h.action_arguments};b[h.action_type+h.output_name]=null;b[h.action_type+h.output_name]=k})}if(!f.workflow_outputs){f.workflow_outputs=[]}var d={id:f.id,type:f.type,tool_id:f.tool_id,tool_state:f.tool_state,tool_errors:f.tool_errors,input_connections:g,position:$(f.element).position(),annotation:f.annotation,post_job_actions:f.post_job_actions,uuid:f.uuid,label:f.label,workflow_outputs:f.workflow_outputs};a[f.id]=d});return{steps:a}},from_simple:function(b){wf=this;var c=0;wf.name=b.name;var a=false;$.each(b.steps,function(g,f){var d=prebuild_node(f.type,f.name,f.tool_id);d.init_field_data(f);if(f.position){d.element.css({top:f.position.top,left:f.position.left})}d.id=f.id;wf.nodes[d.id]=d;c=Math.max(c,parseInt(g));if(!a&&d.type==="tool"){if(d.workflow_outputs.length>0){a=true}else{$.each(d.post_job_actions,function(i,h){if(h.action_type==="HideDatasetAction"){a=true}})}}});wf.id_counter=c+1;$.each(b.steps,function(g,f){var d=wf.nodes[g];$.each(f.input_connections,function(i,h){if(h){if(!$.isArray(h)){h=[h]}$.each(h,function(k,j){var m=wf.nodes[j.id];var n=new Connector();n.connect(m.output_terminals[j.output_name],d.input_terminals[i]);n.redraw()})}});if(a&&d.type==="tool"){$.each(d.output_terminals,function(h,i){if(d.post_job_actions["HideDatasetAction"+i.name]===undefined){d.workflow_outputs.push(i.name);callout=$(d.element).find(".callout."+i.name);callout.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png");workflow.has_changes=true}})}})},check_changes_in_active_form:function(){if(this.active_form_has_changes){this.has_changes=true;$("#right-content").find("form").submit();this.active_form_has_changes=false}},reload_active_node:function(){if(this.active_node){var a=this.active_node;this.clear_active_node();this.activate_node(a)}},clear_active_node:function(){if(this.active_node){this.active_node.make_inactive();this.active_node=null}parent.show_form_for_tool("<div>No node selected</div>")},activate_node:function(a){if(this.active_node!=a){this.check_changes_in_active_form();this.clear_active_node();if(parent.__NEWTOOLFORM__){parent.show_form_for_tool(a.form_html,a)}else{parent.show_form_for_tool(a.form_html+a.tooltip,a)}a.make_active();this.active_node=a}},node_changed:function(a,b){this.has_changes=true;if(this.active_node==a&&(!parent.__NEWTOOLFORM__||b)){this.check_changes_in_active_form();if(parent.__NEWTOOLFORM__){parent.show_form_for_tool(a.form_html,a)}else{parent.show_form_for_tool(a.form_html+a.tooltip,a)}}},layout:function(){this.check_changes_in_active_form();this.has_changes=true;var i={};var b={};$.each(this.nodes,function(l,k){if(i[l]===undefined){i[l]=0}if(b[l]===undefined){b[l]=[]}});$.each(this.nodes,function(l,k){$.each(k.input_terminals,function(m,n){$.each(n.connectors,function(p,q){var o=q.handle1.node;i[k.id]+=1;b[o.id].push(k.id)})})});node_ids_by_level=[];while(true){level_parents=[];for(var a in i){if(i[a]==0){level_parents.push(a)}}if(level_parents.length==0){break}node_ids_by_level.push(level_parents);for(var f in level_parents){var j=level_parents[f];delete i[j];for(var g in b[j]){i[b[j][g]]-=1}}}if(i.length){return}var d=this.nodes;var h=80;v_pad=30;var c=h;$.each(node_ids_by_level,function(k,l){l.sort(function(p,o){return $(d[p].element).position().top-$(d[o].element).position().top});var m=0;var n=v_pad;$.each(l,function(o,r){var q=d[r];var p=$(q.element);$(p).css({top:n,left:c});m=Math.max(m,$(p).width());n+=$(p).height()+v_pad});c+=m+h});$.each(d,function(k,l){l.redraw()})},bounds_for_all_nodes:function(){var d=Infinity,b=-Infinity,c=Infinity,a=-Infinity,f;$.each(this.nodes,function(h,g){e=$(g.element);f=e.position();d=Math.min(d,f.left);b=Math.max(b,f.left+e.width());c=Math.min(c,f.top);a=Math.max(a,f.top+e.width())});return{xmin:d,xmax:b,ymin:c,ymax:a}},fit_canvas_to_nodes:function(){var a=this.bounds_for_all_nodes();var f=this.canvas_container.position();var i=this.canvas_container.parent();var d=fix_delta(a.xmin,100);var h=fix_delta(a.ymin,100);d=Math.max(d,f.left);h=Math.max(h,f.top);var c=f.left-d;var g=f.top-h;var b=round_up(a.xmax+100,100)+d;var j=round_up(a.ymax+100,100)+h;b=Math.max(b,-c+i.width());j=Math.max(j,-g+i.height());this.canvas_container.css({left:c,top:g,width:b,height:j});this.canvas_container.children().each(function(){var k=$(this).position();$(this).css("left",k.left+d);$(this).css("top",k.top+h)})}});function fix_delta(a,b){if(a<b||a>3*b){new_pos=(Math.ceil(((a%b))/b)+1)*b;return(-(a-new_pos))}return 0}function round_up(a,b){return Math.ceil(a/b)*b}function prebuild_node(l,j,r){var i=$("<div class='toolForm toolFormInCanvas'></div>");var g=new Node({element:i});g.type=l;if(l=="tool"){g.tool_id=r}var n=$("<div class='toolFormTitle unselectable'>"+j+"</div>");i.append(n);i.css("left",$(window).scrollLeft()+20);i.css("top",$(window).scrollTop()+20);var m=$("<div class='toolFormBody'></div>");var h="<div><img height='16' align='middle' src='"+galaxy_config.root+"static/images/loading_small_white_bg.gif'/> loading tool info...</div>";m.append(h);g.form_html=h;i.append(m);var k=$("<div class='buttons' style='float: right;'></div>");k.append($("<div>").addClass("fa-icon-button fa fa-times").click(function(b){g.destroy()}));i.appendTo("#canvas-container");var d=$("#canvas-container").position();var c=$("#canvas-container").parent();var a=i.width();var q=i.height();i.css({left:(-d.left)+(c.width()/2)-(a/2),top:(-d.top)+(c.height()/2)-(q/2)});k.prependTo(n);a+=(k.width()+10);i.css("width",a);$(i).bind("dragstart",function(){workflow.activate_node(g)}).bind("dragend",function(){workflow.node_changed(this);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview()}).bind("dragclickonly",function(){workflow.activate_node(g)}).bind("drag",function(o,p){var f=$(this).offsetParent().offset(),b=p.offsetX-f.left,s=p.offsetY-f.top;$(this).css({left:b,top:s});$(this).find(".terminal").each(function(){this.terminal.redraw()})});return g}function add_node(b,d,a){var c=prebuild_node(b,d,a);workflow.add_node(c);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview();workflow.activate_node(c);return c}var ext_to_type=null;var type_to_type=null;function issubtype(b,a){b=ext_to_type[b];a=ext_to_type[a];return(type_to_type[b])&&(a in type_to_type[b])}function populate_datatype_info(a){ext_to_type=a.ext_to_class_name;type_to_type=a.class_to_classes}var NodeView=Backbone.View.extend({initialize:function(a){this.node=a.node;this.output_width=Math.max(150,this.$el.width());this.tool_body=this.$el.find(".toolFormBody");this.tool_body.find("div").remove();this.newInputsDiv().appendTo(this.tool_body);this.terminalViews={};this.outputTerminlViews={}},render:function(){this.renderToolErrors();this.$el.css("width",Math.min(250,Math.max(this.$el.width(),this.output_width)))},renderToolErrors:function(){if(this.node.tool_errors){this.$el.addClass("tool-node-error")}else{this.$el.removeClass("tool-node-error")}},newInputsDiv:function(){return $("<div class='inputs'></div>")},updateMaxWidth:function(a){this.output_width=Math.max(this.output_width,a)},addRule:function(){this.tool_body.append($("<div class='rule'></div>"))},addDataInput:function(i,d){var j=true;if(!d){d=this.$(".inputs");j=false}var f=this.terminalViews[i.name];var h=(i.input_type=="dataset_collection")?InputCollectionTerminalView:InputTerminalView;if(f&&!(f instanceof h)){f.el.terminal.destroy();f=null}if(!f){f=new h({node:this.node,input:i})}else{var g=f.el.terminal;g.update(i);g.destroyInvalidConnections()}this.terminalViews[i.name]=f;var c=f.el;var b=new DataInputView({terminalElement:c,input:i,nodeView:this,skipResize:j});var a=b.$el;d.append(a.prepend(f.terminalElements()));return f},addDataOutput:function(a){var d=(a.collection_type)?OutputCollectionTerminalView:OutputTerminalView;var c=new d({node:this.node,output:a});var b=new DataOutputView({output:a,terminalElement:c.el,nodeView:this,});this.tool_body.append(b.$el.append(c.terminalElements()))},updateDataOutput:function(b){var a=this.node.output_terminals[b.name];a.update(b)}});var DataInputView=Backbone.View.extend({className:"form-row dataRow input-data-row",initialize:function(a){this.input=a.input;this.nodeView=a.nodeView;this.terminalElement=a.terminalElement;this.$el.attr("name",this.input.name).html(this.input.label);if(!a.skipResize){this.$el.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(this.el);this.nodeView.updateMaxWidth(this.$el.outerWidth());this.$el.css({position:"",left:"",top:"",display:""});this.$el.remove()}},});var OutputCalloutView=Backbone.View.extend({tagName:"div",initialize:function(b){this.label=b.label;this.node=b.node;this.output=b.output;var a=this;this.$el.attr("class","callout "+this.label).css({display:"none"}).append($("<div class='buttons'></div>").append($("<img/>").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png").click(function(){if($.inArray(a.output.name,a.node.workflow_outputs)!=-1){a.node.workflow_outputs.splice($.inArray(a.output.name,a.node.workflow_outputs),1);a.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{a.node.workflow_outputs.push(a.output.name);a.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}workflow.has_changes=true;canvas_manager.draw_overview()}))).tooltip({delay:500,title:"Mark dataset as a workflow output. All unmarked datasets will be hidden."});this.$el.css({top:"50%",margin:"-8px 0px 0px 0px",right:8});this.$el.show();this.resetImage()},resetImage:function(){if($.inArray(this.output.name,this.node.workflow_outputs)===-1){this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}},hoverImage:function(){this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-yellow.png")}});var DataOutputView=Backbone.View.extend({className:"form-row dataRow",initialize:function(c){this.output=c.output;this.terminalElement=c.terminalElement;this.nodeView=c.nodeView;var a=this.output;var b=a.name;var f=this.nodeView.node;var d=a.extensions.indexOf("input")>=0||a.extensions.indexOf("input_collection")>=0;if(!d){b=b+" ("+a.extensions.join(", ")+")"}this.$el.html(b);if(f.type=="tool"){var g=new OutputCalloutView({label:b,output:a,node:f,});this.$el.append(g.el);this.$el.hover(function(){g.hoverImage()},function(){g.resetImage()})}this.$el.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(this.el);this.nodeView.updateMaxWidth(this.$el.outerWidth()+17);this.$el.css({position:"",left:"",top:"",display:""}).detach()}});var TerminalView=Backbone.View.extend({setupMappingView:function(b){var c=new this.terminalMappingClass({terminal:b});var a=new this.terminalMappingViewClass({model:c});a.render();b.terminalMappingView=a;this.terminalMappingView=a},terminalElements:function(){if(this.terminalMappingView){return[this.terminalMappingView.el,this.el]}else{return[this.el]}}});var BaseInputTerminalView=TerminalView.extend({className:"terminal input-terminal",initialize:function(c){var f=c.node;var a=c.input;var b=a.name;var d=this.terminalForInput(a);if(!d.multiple){this.setupMappingView(d)}this.el.terminal=d;d.node=f;d.name=b;f.input_terminals[b]=d},events:{dropinit:"onDropInit",dropstart:"onDropStart",dropend:"onDropEnd",drop:"onDrop",hover:"onHover",},onDropInit:function(b,c){var a=this.el.terminal;return $(c.drag).hasClass("output-terminal")&&a.canAccept(c.drag.terminal)},onDropStart:function(a,b){if(b.proxy.terminal){b.proxy.terminal.connectors[0].inner_color="#BBFFBB"}},onDropEnd:function(a,b){if(b.proxy.terminal){b.proxy.terminal.connectors[0].inner_color="#FFFFFF"}},onDrop:function(b,c){var a=this.el.terminal;new Connector(c.drag.terminal,a).redraw()},onHover:function(){var c=this.el;var b=c.terminal;if(b.connectors.length>0){var a=$("<div class='callout'></div>").css({display:"none"}).appendTo("body").append($("<div class='button'></div>").append($("<div/>").addClass("fa-icon-button fa fa-times").click(function(){$.each(b.connectors,function(f,d){if(d){d.destroy()}});a.remove()}))).bind("mouseleave",function(){$(this).remove()});a.css({top:$(c).offset().top-2,left:$(c).offset().left-a.width(),"padding-right":$(c).width()}).show()}},});var InputTerminalView=BaseInputTerminalView.extend({terminalMappingClass:InputTerminalMapping,terminalMappingViewClass:InputTerminalMappingView,terminalForInput:function(a){return new InputTerminal({element:this.el,input:a})},});var InputCollectionTerminalView=BaseInputTerminalView.extend({terminalMappingClass:InputCollectionTerminalMapping,terminalMappingViewClass:InputCollectionTerminalMappingView,terminalForInput:function(a){return new InputCollectionTerminal({element:this.el,input:a})},});var BaseOutputTerminalView=TerminalView.extend({className:"terminal output-terminal",initialize:function(c){var f=c.node;var a=c.output;var b=a.name;var d=this.terminalForOutput(a);this.setupMappingView(d);this.el.terminal=d;d.node=f;d.name=b;f.output_terminals[b]=d},events:{drag:"onDrag",dragstart:"onDragStart",dragend:"onDragEnd",},onDrag:function(b,c){var a=function(){var f=$(c.proxy).offsetParent().offset(),d=c.offsetX-f.left,g=c.offsetY-f.top;$(c.proxy).css({left:d,top:g});c.proxy.terminal.redraw();canvas_manager.update_viewport_overlay()};a();$("#canvas-container").get(0).scroll_panel.test(b,a)},onDragStart:function(b,f){$(f.available).addClass("input-terminal-active");workflow.check_changes_in_active_form();var a=$('<div class="drag-terminal" style="position: absolute;"></div>').appendTo("#canvas-container").get(0);a.terminal=new OutputTerminal({element:a});var g=new Connector();g.dragging=true;g.connect(this.el.terminal,a.terminal);return a},onDragEnd:function(b,c){var a=c.proxy.terminal.connectors[0];if(a){a.destroy()}$(c.proxy).remove();$(c.available).removeClass("input-terminal-active");$("#canvas-container").get(0).scroll_panel.stop()}});var OutputTerminalView=BaseOutputTerminalView.extend({terminalMappingClass:OutputTerminalMapping,terminalMappingViewClass:OutputTerminalMappingView,terminalForOutput:function(a){var c=a.extensions;var b=new OutputTerminal({element:this.el,datatypes:c});return b},});var OutputCollectionTerminalView=BaseOutputTerminalView.extend({terminalMappingClass:OutputCollectionTerminalMapping,terminalMappingViewClass:OutputCollectionTerminalMappingView,terminalForOutput:function(a){var c=a.collection_type;var b=new OutputCollectionTerminal({element:this.el,collection_type:c,datatypes:a.extensions});return b},});function ScrollPanel(a){this.panel=a}$.extend(ScrollPanel.prototype,{test:function(v,d){clearTimeout(this.timeout);var k=v.pageX,j=v.pageY,l=$(this.panel),c=l.position(),b=l.width(),i=l.height(),w=l.parent(),s=w.width(),a=w.height(),r=w.offset(),p=r.left,m=r.top,A=p+w.width(),u=m+w.height(),B=-(b-(s/2)),z=-(i-(a/2)),g=(s/2),f=(a/2),h=false,q=5,o=23;if(k-q<p){if(c.left<g){var n=Math.min(o,g-c.left);l.css("left",c.left+n);h=true}}else{if(k+q>A){if(c.left>B){var n=Math.min(o,c.left-B);l.css("left",c.left-n);h=true}}else{if(j-q<m){if(c.top<f){var n=Math.min(o,f-c.top);l.css("top",c.top+n);h=true}}else{if(j+q>u){if(c.top>z){var n=Math.min(o,c.top-B);l.css("top",(c.top-n)+"px");h=true}}}}}if(h){d();var l=this;this.timeout=setTimeout(function(){l.test(v,d)},50)}},stop:function(b,a){clearTimeout(this.timeout)}});function CanvasManager(b,a){this.cv=b;this.cc=this.cv.find("#canvas-container");this.oc=a.find("#overview-canvas");this.ov=a.find("#overview-viewport");this.init_drag()}$.extend(CanvasManager.prototype,{init_drag:function(){var b=this;var a=function(f,g){f=Math.min(f,b.cv.width()/2);f=Math.max(f,-b.cc.width()+b.cv.width()/2);g=Math.min(g,b.cv.height()/2);g=Math.max(g,-b.cc.height()+b.cv.height()/2);b.cc.css({left:f,top:g});b.update_viewport_overlay()};this.cc.each(function(){this.scroll_panel=new ScrollPanel(this)});var d,c;this.cv.bind("dragstart",function(){var g=$(this).offset();var f=b.cc.position();c=f.top-g.top;d=f.left-g.left}).bind("drag",function(f,g){a(g.offsetX+d,g.offsetY+c)}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});this.ov.bind("drag",function(k,l){var h=b.cc.width(),n=b.cc.height(),m=b.oc.width(),j=b.oc.height(),f=$(this).offsetParent().offset(),i=l.offsetX-f.left,g=l.offsetY-f.top;a(-(i/m*h),-(g/j*n))}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});$("#overview-border").bind("drag",function(g,i){var j=$(this).offsetParent();var h=j.offset();var f=Math.max(j.width()-(i.offsetX-h.left),j.height()-(i.offsetY-h.top));$(this).css({width:f,height:f});b.draw_overview()});$("#overview-border div").bind("drag",function(){})},update_viewport_overlay:function(){var b=this.cc,f=this.cv,a=this.oc,c=this.ov,d=b.width(),j=b.height(),i=a.width(),g=a.height(),h=b.position();c.css({left:-(h.left/d*i),top:-(h.top/j*g),width:(f.width()/d*i)-2,height:(f.height()/j*g)-2})},draw_overview:function(){var j=$("#overview-canvas"),m=j.parent().parent().width(),i=j.get(0).getContext("2d"),d=$("#canvas-container").width(),l=$("#canvas-container").height();var g,a,k,f;var h=this.cv.width();var b=this.cv.height();if(d<h&&l<b){k=d/h*m;f=(m-k)/2;g=l/b*m;a=(m-g)/2}else{if(d<l){a=0;g=m;k=Math.ceil(g*d/l);f=(m-k)/2}else{k=m;f=0;g=Math.ceil(k*l/d);a=(m-g)/2}}j.parent().css({left:f,top:a,width:k,height:g});j.attr("width",k);j.attr("height",g);$.each(workflow.nodes,function(t,q){i.fillStyle="#D2C099";i.strokeStyle="#D8B365";i.lineWidth=1;var s=$(q.element),n=s.position(),c=n.left/d*k,r=n.top/l*g,o=s.width()/d*k,p=s.height()/l*g;if(q.tool_errors){i.fillStyle="#FFCCCC";i.strokeStyle="#AA6666"}else{if(q.workflow_outputs!=undefined&&q.workflow_outputs.length>0){i.fillStyle="#E8A92D";i.strokeStyle="#E8A92D"}}i.fillRect(c,r,o,p);i.strokeRect(c,r,o,p)});this.update_viewport_overlay()}}); \ No newline at end of file +function CollectionTypeDescription(a){this.collectionType=a;this.isCollection=true;this.rank=a.split(":").length}$.extend(CollectionTypeDescription.prototype,{append:function(a){if(a===NULL_COLLECTION_TYPE_DESCRIPTION){return this}if(a===ANY_COLLECTION_TYPE_DESCRIPTION){return otherCollectionType}return new CollectionTypeDescription(this.collectionType+":"+a.collectionType)},canMatch:function(a){if(a===NULL_COLLECTION_TYPE_DESCRIPTION){return false}if(a===ANY_COLLECTION_TYPE_DESCRIPTION){return true}return a.collectionType==this.collectionType},canMapOver:function(b){if(b===NULL_COLLECTION_TYPE_DESCRIPTION){return false}if(b===ANY_COLLECTION_TYPE_DESCRIPTION){return false}if(this.rank<=b.rank){return false}var a=b.collectionType;return this._endsWith(this.collectionType,a)},effectiveMapOver:function(a){var c=a.collectionType;var b=this.collectionType.substring(0,this.collectionType.length-c.length-1);return new CollectionTypeDescription(b)},equal:function(a){return a.collectionType==this.collectionType},toString:function(){return"CollectionType["+this.collectionType+"]"},_endsWith:function(b,a){return b.indexOf(a,b.length-a.length)!==-1}});NULL_COLLECTION_TYPE_DESCRIPTION={isCollection:false,canMatch:function(a){return false},canMapOver:function(a){return false},toString:function(){return"NullCollectionType[]"},append:function(a){return a},equal:function(a){return a===this}};ANY_COLLECTION_TYPE_DESCRIPTION={isCollection:true,canMatch:function(a){return NULL_COLLECTION_TYPE_DESCRIPTION!==a},canMapOver:function(a){return false},toString:function(){return"AnyCollectionType[]"},append:function(a){throw"Cannot append to ANY_COLLECTION_TYPE_DESCRIPTION"},equal:function(a){return a===this}};var TerminalMapping=Backbone.Model.extend({initialize:function(a){this.mapOver=a.mapOver||NULL_COLLECTION_TYPE_DESCRIPTION;this.terminal=a.terminal;this.terminal.terminalMapping=this},disableMapOver:function(){this.setMapOver(NULL_COLLECTION_TYPE_DESCRIPTION)},setMapOver:function(a){this.mapOver=a;this.trigger("change")}});var TerminalMappingView=Backbone.View.extend({tagName:"div",className:"fa-icon-button fa fa-folder-o",initialize:function(b){var a="Run tool in parallel over collection";this.$el.tooltip({delay:500,title:a});this.model.bind("change",_.bind(this.render,this))},render:function(){if(this.model.mapOver.isCollection){this.$el.show()}else{this.$el.hide()}},});var InputTerminalMappingView=TerminalMappingView.extend({events:{click:"onClick",mouseenter:"onMouseEnter",mouseleave:"onMouseLeave",},onMouseEnter:function(b){var a=this.model;if(!a.terminal.connected()&&a.mapOver.isCollection){this.$el.css("color","red")}},onMouseLeave:function(a){this.$el.css("color","black")},onClick:function(b){var a=this.model;if(!a.terminal.connected()&&a.mapOver.isCollection){a.terminal.resetMapping()}},});var InputTerminalMapping=TerminalMapping;var InputCollectionTerminalMapping=TerminalMapping;var OutputTerminalMapping=TerminalMapping;var OutputTerminalMappingView=TerminalMappingView;var InputCollectionTerminalMappingView=InputTerminalMappingView;var OutputCollectionTerminalMapping=TerminalMapping;var OutputCollectionTerminalMappingView=TerminalMappingView;var Terminal=Backbone.Model.extend({initialize:function(a){this.element=a.element;this.connectors=[]},connect:function(a){this.connectors.push(a);if(this.node){this.node.markChanged()}},disconnect:function(a){this.connectors.splice($.inArray(a,this.connectors),1);if(this.node){this.node.markChanged();this.resetMappingIfNeeded()}},redraw:function(){$.each(this.connectors,function(a,b){b.redraw()})},destroy:function(){$.each(this.connectors.slice(),function(a,b){b.destroy()})},destroyInvalidConnections:function(){_.each(this.connectors,function(a){a.destroyIfInvalid()})},setMapOver:function(a){if(this.multiple){return}if(!this.mapOver().equal(a)){this.terminalMapping.setMapOver(a);_.each(this.node.output_terminals,function(b){b.setMapOver(a)})}},mapOver:function(){if(!this.terminalMapping){return NULL_COLLECTION_TYPE_DESCRIPTION}else{return this.terminalMapping.mapOver}},isMappedOver:function(){return this.terminalMapping&&this.terminalMapping.mapOver.isCollection},resetMapping:function(){this.terminalMapping.disableMapOver()},resetMappingIfNeeded:function(){},});var OutputTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.datatypes=a.datatypes},resetMappingIfNeeded:function(){if(!this.node.hasConnectedOutputTerminals()&&!this.node.hasConnectedMappedInputTerminals()){_.each(this.node.mappedInputTerminals(),function(b){b.resetMappingIfNeeded()})}var a=!this.node.hasMappedOverInputTerminals();if(a){this.resetMapping()}},resetMapping:function(){this.terminalMapping.disableMapOver();_.each(this.connectors,function(a){var b=a.handle2;if(b){b.resetMappingIfNeeded();a.destroyIfInvalid()}})}});var BaseInputTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.update(a.input)},canAccept:function(a){if(this._inputFilled()){return false}else{return this.attachable(a)}},resetMappingIfNeeded:function(){var b=this.mapOver();if(!b.isCollection){return}var a=this.node.hasConnectedMappedInputTerminals()||(!this.node.hasConnectedOutputTerminals());if(a){this.resetMapping()}},resetMapping:function(){this.terminalMapping.disableMapOver();if(!this.node.hasMappedOverInputTerminals()){_.each(this.node.output_terminals,function(a){a.resetMapping()})}},connected:function(){return this.connectors.length!==0},_inputFilled:function(){var a;if(!this.connected()){a=false}else{if(this.multiple){if(this._collectionAttached()){inputsFilled=true}else{a=false}}else{a=true}}return a},_collectionAttached:function(){if(!this.connected()){return false}else{var a=this.connectors[0].handle1;if(!a){return false}else{if(a.isDataCollectionInput||a.isMappedOver()||a.datatypes.indexOf("input_collection")>0){return true}else{return false}}}},_mappingConstraints:function(){if(!this.node){return[]}var b=this.mapOver();if(b.isCollection){return[b]}var a=[];if(!this.node.hasConnectedOutputTerminals()){_.each(this.node.connectedMappedInputTerminals(),function(c){a.push(c.mapOver())})}else{a.push(_.first(_.values(this.node.output_terminals)).mapOver())}return a},_producesAcceptableDatatype:function(a){for(var c in this.datatypes){var f=new Array();f=f.concat(a.datatypes);if(a.node.post_job_actions){for(var d in a.node.post_job_actions){var g=a.node.post_job_actions[d];if(g.action_type=="ChangeDatatypeAction"&&(g.output_name==""||g.output_name==a.name)&&g.action_arguments){f.push(g.action_arguments.newtype)}}}for(var b in f){var h=f[b];if(h=="input"||h=="input_collection"||issubtype(f[b],this.datatypes[c])){return true}}}return false},_otherCollectionType:function(a){var c=NULL_COLLECTION_TYPE_DESCRIPTION;if(a.isDataCollectionInput){c=a.collectionType}else{var b=a.mapOver();if(b.isCollection){c=b}}return c},});var InputTerminal=BaseInputTerminal.extend({update:function(a){this.datatypes=a.extensions;this.multiple=a.multiple;this.collection=false},connect:function(a){BaseInputTerminal.prototype.connect.call(this,a);var b=a.handle1;if(!b){return}var c=this._otherCollectionType(b);if(c.isCollection){this.setMapOver(c)}},attachable:function(b){var d=this._otherCollectionType(b);var a=this.mapOver();if(d.isCollection){if(this.multiple){if(this.connected()&&!this._collectionAttached()){return false}if(d.rank==1){return this._producesAcceptableDatatype(b)}else{return false}}if(a.isCollection&&a.canMatch(d)){return this._producesAcceptableDatatype(b)}else{var c=this._mappingConstraints();if(c.every(_.bind(d.canMatch,d))){return this._producesAcceptableDatatype(b)}else{return false}}}else{if(a.isCollection){return false}}return this._producesAcceptableDatatype(b)}});var InputCollectionTerminal=BaseInputTerminal.extend({update:function(a){this.multiple=false;this.collection=true;this.datatypes=a.extensions;if(a.collection_type){this.collectionType=new CollectionTypeDescription(a.collection_type)}else{this.collectionType=ANY_COLLECTION_TYPE_DESCRIPTION}},connect:function(b){BaseInputTerminal.prototype.connect.call(this,b);var a=b.handle1;if(!a){return}var c=this._effectiveMapOver(a);this.setMapOver(c)},_effectiveMapOver:function(a){var b=this.collectionType;var c=this._otherCollectionType(a);if(!b.canMatch(c)){return c.effectiveMapOver(b)}else{return NULL_COLLECTION_TYPE_DESCRIPTION}},_effectiveCollectionType:function(){var b=this.collectionType;var a=this.mapOver();return a.append(b)},attachable:function(b){var g=this._otherCollectionType(b);if(g.isCollection){var f=this._effectiveCollectionType();var a=this.mapOver();if(f.canMatch(g)){return this._producesAcceptableDatatype(b)}else{if(a.isCollection){return false}else{if(g.canMapOver(this.collectionType)){var d=this._effectiveMapOver(b);if(!d.isCollection){return false}var c=this._mappingConstraints();if(c.every(d.canMatch)){return this._producesAcceptableDatatype(b)}}}}}return false}});var OutputCollectionTerminal=Terminal.extend({initialize:function(a){Terminal.prototype.initialize.call(this,a);this.datatypes=a.datatypes;this.collectionType=new CollectionTypeDescription(a.collection_type);this.isDataCollectionInput=true},update:function(a){var b=new CollectionTypeDescription(a.collection_type);if(b.collectionType!=this.collectionType.collectionType){_.each(this.connectors,function(c){c.destroy()})}this.collectionType=b}});function Connector(b,a){this.canvas=null;this.dragging=false;this.inner_color="#FFFFFF";this.outer_color="#D8B365";if(b&&a){this.connect(b,a)}}$.extend(Connector.prototype,{connect:function(b,a){this.handle1=b;if(this.handle1){this.handle1.connect(this)}this.handle2=a;if(this.handle2){this.handle2.connect(this)}},destroy:function(){if(this.handle1){this.handle1.disconnect(this)}if(this.handle2){this.handle2.disconnect(this)}$(this.canvas).remove()},destroyIfInvalid:function(){if(this.handle1&&this.handle2&&!this.handle2.attachable(this.handle1)){this.destroy()}},redraw:function(){var f=$("#canvas-container");if(!this.canvas){this.canvas=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(this.canvas)}f.append($(this.canvas));if(this.dragging){this.canvas.style.zIndex="300"}}var v=function(c){return $(c).offset().left-f.offset().left};var p=function(c){return $(c).offset().top-f.offset().top};if(!this.handle1||!this.handle2){return}var o=v(this.handle1.element)+5;var n=p(this.handle1.element)+5;var x=v(this.handle2.element)+5;var u=p(this.handle2.element)+5;var k=100;var r=Math.min(o,x);var a=Math.max(o,x);var q=Math.min(n,u);var B=Math.max(n,u);var d=Math.min(Math.max(Math.abs(B-q)/2,100),300);var w=r-k;var A=q-k;var y=a-r+2*k;var s=B-q+2*k;this.canvas.style.left=w+"px";this.canvas.style.top=A+"px";this.canvas.setAttribute("width",y);this.canvas.setAttribute("height",s);o-=w;n-=A;x-=w;u-=A;var z=this.canvas.getContext("2d"),h=null,l=null;var g=1;if(this.handle1&&this.handle1.isMappedOver()){var h=[-6,-3,0,3,6];g=5}else{var h=[0]}if(this.handle2&&this.handle2.isMappedOver()){var l=[-6,-3,0,3,6];g=5}else{var l=[0]}var b=this;for(var t=0;t<g;t++){var m=5,j=7;if(h.length>1||l.length>1){m=1;j=3}b.draw_outlined_curve(o,n,x,u,d,m,j,h[t%h.length],l[t%l.length])}},draw_outlined_curve:function(j,i,l,k,a,b,f,g,d){var g=g||0;var d=d||0;var h=this.canvas.getContext("2d");h.lineCap="round";h.strokeStyle=this.outer_color;h.lineWidth=f;h.beginPath();h.moveTo(j,i+g);h.bezierCurveTo(j+a,i+g,l-a,k+d,l,k+d);h.stroke();h.strokeStyle=this.inner_color;h.lineWidth=b;h.beginPath();h.moveTo(j,i+g);h.bezierCurveTo(j+a,i+g,l-a,k+d,l,k+d);h.stroke()}});var Node=Backbone.Model.extend({initialize:function(a){this.element=a.element;this.input_terminals={};this.output_terminals={};this.tool_errors={}},connectedOutputTerminals:function(){return this._connectedTerminals(this.output_terminals)},_connectedTerminals:function(b){var a=[];$.each(b,function(c,d){if(d.connectors.length>0){a.push(d)}});return a},hasConnectedOutputTerminals:function(){var a=this.output_terminals;for(var b in a){if(a[b].connectors.length>0){return true}}return false},connectedMappedInputTerminals:function(){return this._connectedMappedTerminals(this.input_terminals)},hasConnectedMappedInputTerminals:function(){var c=this.input_terminals;for(var b in c){var a=c[b];if(a.connectors.length>0&&a.isMappedOver()){return true}}return false},_connectedMappedTerminals:function(b){var a=[];$.each(b,function(c,d){var f=d.mapOver();if(f.isCollection){if(d.connectors.length>0){a.push(d)}}});return a},mappedInputTerminals:function(){return this._mappedTerminals(this.input_terminals)},_mappedTerminals:function(b){var a=[];$.each(b,function(c,d){var f=d.mapOver();if(f.isCollection){a.push(d)}});return a},hasMappedOverInputTerminals:function(){var a=false;_.each(this.input_terminals,function(b){var c=b.mapOver();if(c.isCollection){a=true}});return a},redraw:function(){$.each(this.input_terminals,function(a,b){b.redraw()});$.each(this.output_terminals,function(a,b){b.redraw()})},destroy:function(){$.each(this.input_terminals,function(a,b){b.destroy()});$.each(this.output_terminals,function(a,b){b.destroy()});workflow.remove_node(this);$(this.element).remove()},make_active:function(){$(this.element).addClass("toolForm-active")},make_inactive:function(){var a=this.element.get(0);(function(b){b.removeChild(a);b.appendChild(a)})(a.parentNode);$(a).removeClass("toolForm-active")},init_field_data:function(b){if(b.type){this.type=b.type}this.name=b.name;this.form_html=b.form_html;this.tool_state=b.tool_state;this.tool_errors=b.tool_errors;this.tooltip=b.tooltip?b.tooltip:"";this.annotation=b.annotation;this.post_job_actions=b.post_job_actions?b.post_job_actions:{};this.label=b.label;this.uuid=b.uuid;this.workflow_outputs=b.workflow_outputs?b.workflow_outputs:[];var a=this;var c=new NodeView({el:this.element[0],node:a,});a.nodeView=c;$.each(b.data_inputs,function(f,d){c.addDataInput(d)});if((b.data_inputs.length>0)&&(b.data_outputs.length>0)){c.addRule()}$.each(b.data_outputs,function(f,d){c.addDataOutput(d)});c.render();workflow.node_changed(this,true)},update_field_data:function(d){var c=this;nodeView=c.nodeView;this.tool_state=d.tool_state;this.form_html=d.form_html;this.tool_errors=d.tool_errors;this.annotation=d.annotation;if("post_job_actions" in d){var f=$.parseJSON(d.post_job_actions);this.post_job_actions=f?f:{}}c.nodeView.renderToolErrors();var g=nodeView.$("div.inputs");var a=nodeView.newInputsDiv();var b={};_.each(d.data_inputs,function(h){var i=c.nodeView.addDataInput(h,a);b[h.name]=i});_.each(_.difference(_.values(nodeView.terminalViews),_.values(b)),function(h){h.el.terminal.destroy()});nodeView.terminalViews=b;if(d.data_outputs.length==1&&"collection_type" in d.data_outputs[0]){nodeView.updateDataOutput(d.data_outputs[0])}g.replaceWith(a);this.markChanged();this.redraw()},error:function(d){var a=$(this.element).find(".toolFormBody");a.find("div").remove();var c="<div style='color: red; text-style: italic;'>"+d+"</div>";this.form_html=c;a.html(c);workflow.node_changed(this)},markChanged:function(){workflow.node_changed(this)}});function Workflow(a){this.canvas_container=a;this.id_counter=0;this.nodes={};this.name=null;this.has_changes=false;this.active_form_has_changes=false}$.extend(Workflow.prototype,{add_node:function(a){a.id=this.id_counter;a.element.attr("id","wf-node-step-"+a.id);this.id_counter++;this.nodes[a.id]=a;this.has_changes=true;a.workflow=this},remove_node:function(a){if(this.active_node==a){this.clear_active_node()}delete this.nodes[a.id];this.has_changes=true},remove_all:function(){wf=this;$.each(this.nodes,function(b,a){a.destroy();wf.remove_node(a)})},rectify_workflow_outputs:function(){var b=false;var a=false;$.each(this.nodes,function(c,d){if(d.workflow_outputs&&d.workflow_outputs.length>0){b=true}$.each(d.post_job_actions,function(g,f){if(f.action_type==="HideDatasetAction"){a=true}})});if(b!==false||a!==false){$.each(this.nodes,function(c,g){if(g.type==="tool"){var f=false;if(g.post_job_actions==null){g.post_job_actions={};f=true}var d=[];$.each(g.post_job_actions,function(i,h){if(h.action_type=="HideDatasetAction"){d.push(i)}});if(d.length>0){$.each(d,function(h,j){f=true;delete g.post_job_actions[j]})}if(b){$.each(g.output_terminals,function(i,j){var h=true;$.each(g.workflow_outputs,function(l,m){if(j.name===m){h=false}});if(h===true){f=true;var k={action_type:"HideDatasetAction",output_name:j.name,action_arguments:{}};g.post_job_actions["HideDatasetAction"+j.name]=null;g.post_job_actions["HideDatasetAction"+j.name]=k}})}if(workflow.active_node==g&&f===true){workflow.reload_active_node()}}})}},to_simple:function(){var a={};$.each(this.nodes,function(c,f){var g={};$.each(f.input_terminals,function(i,j){g[j.name]=null;var h=[];$.each(j.connectors,function(k,l){h[k]={id:l.handle1.node.id,output_name:l.handle1.name};g[j.name]=h})});var b={};if(f.post_job_actions){$.each(f.post_job_actions,function(j,h){var k={action_type:h.action_type,output_name:h.output_name,action_arguments:h.action_arguments};b[h.action_type+h.output_name]=null;b[h.action_type+h.output_name]=k})}if(!f.workflow_outputs){f.workflow_outputs=[]}var d={id:f.id,type:f.type,tool_id:f.tool_id,tool_state:f.tool_state,tool_errors:f.tool_errors,input_connections:g,position:$(f.element).position(),annotation:f.annotation,post_job_actions:f.post_job_actions,uuid:f.uuid,label:f.label,workflow_outputs:f.workflow_outputs};a[f.id]=d});return{steps:a}},from_simple:function(b){wf=this;var c=0;wf.name=b.name;var a=false;$.each(b.steps,function(g,f){var d=prebuild_node(f.type,f.name,f.tool_id);d.init_field_data(f);if(f.position){d.element.css({top:f.position.top,left:f.position.left})}d.id=f.id;wf.nodes[d.id]=d;c=Math.max(c,parseInt(g));if(!a&&d.type==="tool"){if(d.workflow_outputs.length>0){a=true}else{$.each(d.post_job_actions,function(i,h){if(h.action_type==="HideDatasetAction"){a=true}})}}});wf.id_counter=c+1;$.each(b.steps,function(g,f){var d=wf.nodes[g];$.each(f.input_connections,function(i,h){if(h){if(!$.isArray(h)){h=[h]}$.each(h,function(k,j){var m=wf.nodes[j.id];var n=new Connector();n.connect(m.output_terminals[j.output_name],d.input_terminals[i]);n.redraw()})}});if(a&&d.type==="tool"){$.each(d.output_terminals,function(h,i){if(d.post_job_actions["HideDatasetAction"+i.name]===undefined){d.workflow_outputs.push(i.name);callout=$(d.element).find(".callout."+i.name);callout.find("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png");workflow.has_changes=true}})}})},check_changes_in_active_form:function(){if(this.active_form_has_changes){this.has_changes=true;$("#right-content").find("form").submit();this.active_form_has_changes=false}},reload_active_node:function(){if(this.active_node){var a=this.active_node;this.clear_active_node();this.activate_node(a)}},clear_active_node:function(){if(this.active_node){this.active_node.make_inactive();this.active_node=null}parent.show_form_for_tool("<div>No node selected</div>")},activate_node:function(a){if(this.active_node!=a){this.check_changes_in_active_form();this.clear_active_node();if(parent.__NEWTOOLFORM__){parent.show_form_for_tool(a.form_html,a)}else{parent.show_form_for_tool(a.form_html+a.tooltip,a)}a.make_active();this.active_node=a}},node_changed:function(a,b){this.has_changes=true;if(this.active_node==a&&(!parent.__NEWTOOLFORM__||b)){this.check_changes_in_active_form();if(parent.__NEWTOOLFORM__){parent.show_form_for_tool(a.form_html,a)}else{parent.show_form_for_tool(a.form_html+a.tooltip,a)}}},layout:function(){this.check_changes_in_active_form();this.has_changes=true;var i={};var b={};$.each(this.nodes,function(l,k){if(i[l]===undefined){i[l]=0}if(b[l]===undefined){b[l]=[]}});$.each(this.nodes,function(l,k){$.each(k.input_terminals,function(m,n){$.each(n.connectors,function(p,q){var o=q.handle1.node;i[k.id]+=1;b[o.id].push(k.id)})})});node_ids_by_level=[];while(true){level_parents=[];for(var a in i){if(i[a]==0){level_parents.push(a)}}if(level_parents.length==0){break}node_ids_by_level.push(level_parents);for(var f in level_parents){var j=level_parents[f];delete i[j];for(var g in b[j]){i[b[j][g]]-=1}}}if(i.length){return}var d=this.nodes;var h=80;v_pad=30;var c=h;$.each(node_ids_by_level,function(k,l){l.sort(function(p,o){return $(d[p].element).position().top-$(d[o].element).position().top});var m=0;var n=v_pad;$.each(l,function(o,r){var q=d[r];var p=$(q.element);$(p).css({top:n,left:c});m=Math.max(m,$(p).width());n+=$(p).height()+v_pad});c+=m+h});$.each(d,function(k,l){l.redraw()})},bounds_for_all_nodes:function(){var d=Infinity,b=-Infinity,c=Infinity,a=-Infinity,f;$.each(this.nodes,function(h,g){e=$(g.element);f=e.position();d=Math.min(d,f.left);b=Math.max(b,f.left+e.width());c=Math.min(c,f.top);a=Math.max(a,f.top+e.width())});return{xmin:d,xmax:b,ymin:c,ymax:a}},fit_canvas_to_nodes:function(){var a=this.bounds_for_all_nodes();var f=this.canvas_container.position();var i=this.canvas_container.parent();var d=fix_delta(a.xmin,100);var h=fix_delta(a.ymin,100);d=Math.max(d,f.left);h=Math.max(h,f.top);var c=f.left-d;var g=f.top-h;var b=round_up(a.xmax+100,100)+d;var j=round_up(a.ymax+100,100)+h;b=Math.max(b,-c+i.width());j=Math.max(j,-g+i.height());this.canvas_container.css({left:c,top:g,width:b,height:j});this.canvas_container.children().each(function(){var k=$(this).position();$(this).css("left",k.left+d);$(this).css("top",k.top+h)})}});function fix_delta(a,b){if(a<b||a>3*b){new_pos=(Math.ceil(((a%b))/b)+1)*b;return(-(a-new_pos))}return 0}function round_up(a,b){return Math.ceil(a/b)*b}function prebuild_node(l,j,r){var i=$("<div class='toolForm toolFormInCanvas'></div>");var g=new Node({element:i});g.type=l;if(l=="tool"){g.tool_id=r}var n=$("<div class='toolFormTitle unselectable'>"+j+"</div>");i.append(n);i.css("left",$(window).scrollLeft()+20);i.css("top",$(window).scrollTop()+20);var m=$("<div class='toolFormBody'></div>");var h="<div><img height='16' align='middle' src='"+galaxy_config.root+"static/images/loading_small_white_bg.gif'/> loading tool info...</div>";m.append(h);g.form_html=h;i.append(m);var k=$("<div class='buttons' style='float: right;'></div>");k.append($("<div>").addClass("fa-icon-button fa fa-times").click(function(b){g.destroy()}));i.appendTo("#canvas-container");var d=$("#canvas-container").position();var c=$("#canvas-container").parent();var a=i.width();var q=i.height();i.css({left:(-d.left)+(c.width()/2)-(a/2),top:(-d.top)+(c.height()/2)-(q/2)});k.prependTo(n);a+=(k.width()+10);i.css("width",a);$(i).bind("dragstart",function(){workflow.activate_node(g)}).bind("dragend",function(){workflow.node_changed(this);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview()}).bind("dragclickonly",function(){workflow.activate_node(g)}).bind("drag",function(o,p){var f=$(this).offsetParent().offset(),b=p.offsetX-f.left,s=p.offsetY-f.top;$(this).css({left:b,top:s});$(this).find(".terminal").each(function(){this.terminal.redraw()})});return g}function add_node(b,d,a){var c=prebuild_node(b,d,a);workflow.add_node(c);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview();workflow.activate_node(c);return c}var ext_to_type=null;var type_to_type=null;function issubtype(b,a){b=ext_to_type[b];a=ext_to_type[a];return(type_to_type[b])&&(a in type_to_type[b])}function populate_datatype_info(a){ext_to_type=a.ext_to_class_name;type_to_type=a.class_to_classes}var NodeView=Backbone.View.extend({initialize:function(a){this.node=a.node;this.output_width=Math.max(150,this.$el.width());this.tool_body=this.$el.find(".toolFormBody");this.tool_body.find("div").remove();this.newInputsDiv().appendTo(this.tool_body);this.terminalViews={};this.outputTerminlViews={}},render:function(){this.renderToolErrors();this.$el.css("width",Math.min(250,Math.max(this.$el.width(),this.output_width)))},renderToolErrors:function(){if(this.node.tool_errors){this.$el.addClass("tool-node-error")}else{this.$el.removeClass("tool-node-error")}},newInputsDiv:function(){return $("<div class='inputs'></div>")},updateMaxWidth:function(a){this.output_width=Math.max(this.output_width,a)},addRule:function(){this.tool_body.append($("<div class='rule'></div>"))},addDataInput:function(i,d){var j=true;if(!d){d=this.$(".inputs");j=false}var f=this.terminalViews[i.name];var h=(i.input_type=="dataset_collection")?InputCollectionTerminalView:InputTerminalView;if(f&&!(f instanceof h)){f.el.terminal.destroy();f=null}if(!f){f=new h({node:this.node,input:i})}else{var g=f.el.terminal;g.update(i);g.destroyInvalidConnections()}this.terminalViews[i.name]=f;var c=f.el;var b=new DataInputView({terminalElement:c,input:i,nodeView:this,skipResize:j});var a=b.$el;d.append(a.prepend(f.terminalElements()));return f},addDataOutput:function(a){var d=(a.collection_type)?OutputCollectionTerminalView:OutputTerminalView;var c=new d({node:this.node,output:a});var b=new DataOutputView({output:a,terminalElement:c.el,nodeView:this,});this.tool_body.append(b.$el.append(c.terminalElements()))},updateDataOutput:function(b){var a=this.node.output_terminals[b.name];a.update(b)}});var DataInputView=Backbone.View.extend({className:"form-row dataRow input-data-row",initialize:function(a){this.input=a.input;this.nodeView=a.nodeView;this.terminalElement=a.terminalElement;this.$el.attr("name",this.input.name).html(this.input.label);if(!a.skipResize){this.$el.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(this.el);this.nodeView.updateMaxWidth(this.$el.outerWidth());this.$el.css({position:"",left:"",top:"",display:""});this.$el.remove()}},});var OutputCalloutView=Backbone.View.extend({tagName:"div",initialize:function(b){this.label=b.label;this.node=b.node;this.output=b.output;var a=this;this.$el.attr("class","callout "+this.label).css({display:"none"}).append($("<div class='buttons'></div>").append($("<img/>").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png").click(function(){if($.inArray(a.output.name,a.node.workflow_outputs)!=-1){a.node.workflow_outputs.splice($.inArray(a.output.name,a.node.workflow_outputs),1);a.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{a.node.workflow_outputs.push(a.output.name);a.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}workflow.has_changes=true;canvas_manager.draw_overview()}))).tooltip({delay:500,title:"Mark dataset as a workflow output. All unmarked datasets will be hidden."});this.$el.css({top:"50%",margin:"-8px 0px 0px 0px",right:8});this.$el.show();this.resetImage()},resetImage:function(){if($.inArray(this.output.name,this.node.workflow_outputs)===-1){this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-outline.png")}else{this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small.png")}},hoverImage:function(){this.$("img").attr("src",galaxy_config.root+"static/images/fugue/asterisk-small-yellow.png")}});var DataOutputView=Backbone.View.extend({className:"form-row dataRow",initialize:function(c){this.output=c.output;this.terminalElement=c.terminalElement;this.nodeView=c.nodeView;var a=this.output;var b=a.name;var f=this.nodeView.node;var d=a.extensions.indexOf("input")>=0||a.extensions.indexOf("input_collection")>=0;if(!d){b=b+" ("+a.extensions.join(", ")+")"}this.$el.html(b);if(f.type=="tool"){var g=new OutputCalloutView({label:b,output:a,node:f,});this.$el.append(g.el);this.$el.hover(function(){g.hoverImage()},function(){g.resetImage()})}this.$el.css({position:"absolute",left:-1000,top:-1000,display:"none"});$("body").append(this.el);this.nodeView.updateMaxWidth(this.$el.outerWidth()+17);this.$el.css({position:"",left:"",top:"",display:""}).detach()}});var TerminalView=Backbone.View.extend({setupMappingView:function(b){var c=new this.terminalMappingClass({terminal:b});var a=new this.terminalMappingViewClass({model:c});a.render();b.terminalMappingView=a;this.terminalMappingView=a},terminalElements:function(){if(this.terminalMappingView){return[this.terminalMappingView.el,this.el]}else{return[this.el]}}});var BaseInputTerminalView=TerminalView.extend({className:"terminal input-terminal",initialize:function(c){var f=c.node;var a=c.input;var b=a.name;var d=this.terminalForInput(a);if(!d.multiple){this.setupMappingView(d)}this.el.terminal=d;d.node=f;d.name=b;f.input_terminals[b]=d},events:{dropinit:"onDropInit",dropstart:"onDropStart",dropend:"onDropEnd",drop:"onDrop",hover:"onHover",},onDropInit:function(b,c){var a=this.el.terminal;return $(c.drag).hasClass("output-terminal")&&a.canAccept(c.drag.terminal)},onDropStart:function(a,b){if(b.proxy.terminal){b.proxy.terminal.connectors[0].inner_color="#BBFFBB"}},onDropEnd:function(a,b){if(b.proxy.terminal){b.proxy.terminal.connectors[0].inner_color="#FFFFFF"}},onDrop:function(b,c){var a=this.el.terminal;new Connector(c.drag.terminal,a).redraw()},onHover:function(){var c=this.el;var b=c.terminal;if(b.connectors.length>0){var a=$("<div class='callout'></div>").css({display:"none"}).appendTo("body").append($("<div class='button'></div>").append($("<div/>").addClass("fa-icon-button fa fa-times").click(function(){$.each(b.connectors,function(f,d){if(d){d.destroy()}});a.remove()}))).bind("mouseleave",function(){$(this).remove()});a.css({top:$(c).offset().top-2,left:$(c).offset().left-a.width(),"padding-right":$(c).width()}).show()}},});var InputTerminalView=BaseInputTerminalView.extend({terminalMappingClass:InputTerminalMapping,terminalMappingViewClass:InputTerminalMappingView,terminalForInput:function(a){return new InputTerminal({element:this.el,input:a})},});var InputCollectionTerminalView=BaseInputTerminalView.extend({terminalMappingClass:InputCollectionTerminalMapping,terminalMappingViewClass:InputCollectionTerminalMappingView,terminalForInput:function(a){return new InputCollectionTerminal({element:this.el,input:a})},});var BaseOutputTerminalView=TerminalView.extend({className:"terminal output-terminal",initialize:function(c){var f=c.node;var a=c.output;var b=a.name;var d=this.terminalForOutput(a);this.setupMappingView(d);this.el.terminal=d;d.node=f;d.name=b;f.output_terminals[b]=d},events:{drag:"onDrag",dragstart:"onDragStart",dragend:"onDragEnd",},onDrag:function(b,c){var a=function(){var f=$(c.proxy).offsetParent().offset(),d=c.offsetX-f.left,g=c.offsetY-f.top;$(c.proxy).css({left:d,top:g});c.proxy.terminal.redraw();canvas_manager.update_viewport_overlay()};a();$("#canvas-container").get(0).scroll_panel.test(b,a)},onDragStart:function(b,f){$(f.available).addClass("input-terminal-active");workflow.check_changes_in_active_form();var a=$('<div class="drag-terminal" style="position: absolute;"></div>').appendTo("#canvas-container").get(0);a.terminal=new OutputTerminal({element:a});var g=new Connector();g.dragging=true;g.connect(this.el.terminal,a.terminal);return a},onDragEnd:function(b,c){var a=c.proxy.terminal.connectors[0];if(a){a.destroy()}$(c.proxy).remove();$(c.available).removeClass("input-terminal-active");$("#canvas-container").get(0).scroll_panel.stop()}});var OutputTerminalView=BaseOutputTerminalView.extend({terminalMappingClass:OutputTerminalMapping,terminalMappingViewClass:OutputTerminalMappingView,terminalForOutput:function(a){var c=a.extensions;var b=new OutputTerminal({element:this.el,datatypes:c});return b},});var OutputCollectionTerminalView=BaseOutputTerminalView.extend({terminalMappingClass:OutputCollectionTerminalMapping,terminalMappingViewClass:OutputCollectionTerminalMappingView,terminalForOutput:function(a){var c=a.collection_type;var b=new OutputCollectionTerminal({element:this.el,collection_type:c,datatypes:a.extensions});return b},});function ScrollPanel(a){this.panel=a}$.extend(ScrollPanel.prototype,{test:function(v,d){clearTimeout(this.timeout);var k=v.pageX,j=v.pageY,l=$(this.panel),c=l.position(),b=l.width(),i=l.height(),w=l.parent(),s=w.width(),a=w.height(),r=w.offset(),p=r.left,m=r.top,A=p+w.width(),u=m+w.height(),B=-(b-(s/2)),z=-(i-(a/2)),g=(s/2),f=(a/2),h=false,q=5,o=23;if(k-q<p){if(c.left<g){var n=Math.min(o,g-c.left);l.css("left",c.left+n);h=true}}else{if(k+q>A){if(c.left>B){var n=Math.min(o,c.left-B);l.css("left",c.left-n);h=true}}else{if(j-q<m){if(c.top<f){var n=Math.min(o,f-c.top);l.css("top",c.top+n);h=true}}else{if(j+q>u){if(c.top>z){var n=Math.min(o,c.top-B);l.css("top",(c.top-n)+"px");h=true}}}}}if(h){d();var l=this;this.timeout=setTimeout(function(){l.test(v,d)},50)}},stop:function(b,a){clearTimeout(this.timeout)}});function CanvasManager(b,a){this.cv=b;this.cc=this.cv.find("#canvas-container");this.oc=a.find("#overview-canvas");this.ov=a.find("#overview-viewport");this.init_drag()}$.extend(CanvasManager.prototype,{init_drag:function(){var b=this;var a=function(f,g){f=Math.min(f,b.cv.width()/2);f=Math.max(f,-b.cc.width()+b.cv.width()/2);g=Math.min(g,b.cv.height()/2);g=Math.max(g,-b.cc.height()+b.cv.height()/2);b.cc.css({left:f,top:g});b.cv.css({"background-position-x":f,"background-position-y":g});b.update_viewport_overlay()};this.cc.each(function(){this.scroll_panel=new ScrollPanel(this)});var d,c;this.cv.bind("dragstart",function(){var g=$(this).offset();var f=b.cc.position();c=f.top-g.top;d=f.left-g.left}).bind("drag",function(f,g){a(g.offsetX+d,g.offsetY+c)}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});this.ov.bind("drag",function(k,l){var h=b.cc.width(),n=b.cc.height(),m=b.oc.width(),j=b.oc.height(),f=$(this).offsetParent().offset(),i=l.offsetX-f.left,g=l.offsetY-f.top;a(-(i/m*h),-(g/j*n))}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});$("#overview-border").bind("drag",function(g,i){var j=$(this).offsetParent();var h=j.offset();var f=Math.max(j.width()-(i.offsetX-h.left),j.height()-(i.offsetY-h.top));$(this).css({width:f,height:f});b.draw_overview()});$("#overview-border div").bind("drag",function(){})},update_viewport_overlay:function(){var b=this.cc,f=this.cv,a=this.oc,c=this.ov,d=b.width(),j=b.height(),i=a.width(),g=a.height(),h=b.position();c.css({left:-(h.left/d*i),top:-(h.top/j*g),width:(f.width()/d*i)-2,height:(f.height()/j*g)-2})},draw_overview:function(){var j=$("#overview-canvas"),m=j.parent().parent().width(),i=j.get(0).getContext("2d"),d=$("#canvas-container").width(),l=$("#canvas-container").height();var g,a,k,f;var h=this.cv.width();var b=this.cv.height();if(d<h&&l<b){k=d/h*m;f=(m-k)/2;g=l/b*m;a=(m-g)/2}else{if(d<l){a=0;g=m;k=Math.ceil(g*d/l);f=(m-k)/2}else{k=m;f=0;g=Math.ceil(k*l/d);a=(m-g)/2}}j.parent().css({left:f,top:a,width:k,height:g});j.attr("width",k);j.attr("height",g);$.each(workflow.nodes,function(t,q){i.fillStyle="#D2C099";i.strokeStyle="#D8B365";i.lineWidth=1;var s=$(q.element),n=s.position(),c=n.left/d*k,r=n.top/l*g,o=s.width()/d*k,p=s.height()/l*g;if(q.tool_errors){i.fillStyle="#FFCCCC";i.strokeStyle="#AA6666"}else{if(q.workflow_outputs!=undefined&&q.workflow_outputs.length>0){i.fillStyle="#E8A92D";i.strokeStyle="#E8A92D"}}i.fillRect(c,r,o,p);i.strokeRect(c,r,o,p)});this.update_viewport_overlay()}}); \ No newline at end of file diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef static/scripts/packed/mvc/history/history-panel-edit.js --- a/static/scripts/packed/mvc/history/history-panel-edit.js +++ b/static/scripts/packed/mvc/history/history-panel-edit.js @@ -1,1 +1,1 @@ -define(["mvc/history/history-panel","mvc/history/history-contents","mvc/dataset/states","mvc/history/hda-model","mvc/history/hda-li-edit","mvc/history/hdca-li-edit","mvc/tags","mvc/annotations","ui/fa-icon-button","mvc/ui/popup-menu","utils/localization"],function(h,j,m,f,e,i,l,c,b,a,d){var k=h.HistoryPanel;var g=k.extend({HDAViewClass:e.HDAListItemEdit,HDCAViewClass:i.HDCAListItemEdit,initialize:function(n){n=n||{};k.prototype.initialize.call(this,n);this.tagsEditor=null;this.annotationEditor=null;this.purgeAllowed=n.purgeAllowed||false;this.annotationEditorShown=n.annotationEditorShown||false;this.tagsEditorShown=n.tagsEditorShown||false;this.multiselectActions=n.multiselectActions||this._getActions()},_setUpListeners:function(){k.prototype._setUpListeners.call(this);this.on("drop",function(n,o){this.dataDropped(o);this.dropTargetOff()})},_setUpCollectionListeners:function(){k.prototype._setUpCollectionListeners.call(this);this.collection.on("change:deleted",this._handleHdaDeletionChange,this);this.collection.on("change:visible",this._handleHdaVisibleChange,this);this.collection.on("change:purged",function(n){this.model.fetch()},this);return this},_setUpModelListeners:function(){k.prototype._setUpModelListeners.call(this);this.model.on("change:nice_size",this.updateHistoryDiskSize,this);return this},_buildNewRender:function(){var n=k.prototype._buildNewRender.call(this);if(!this.model){return n}if(Galaxy&&Galaxy.currUser&&Galaxy.currUser.id&&Galaxy.currUser.id===this.model.get("user_id")){this._renderTags(n);this._renderAnnotation(n)}return n},renderItems:function(o){var n=k.prototype.renderItems.call(this,o);this._renderCounts(o);return n},_renderCounts:function(p){function o(s,t){return['<a class="',s,'" href="javascript:void(0);">',t,"</a>"].join("")}p=p||this.$el;var n=this.collection.where({deleted:true}),r=this.collection.where({visible:false}),q=[];if(this.views.length){q.push([this.views.length,d("shown")].join(" "))}if(n.length){q.push((!this.showDeleted)?([n.length,o("toggle-deleted-link",d("deleted"))].join(" ")):(o("toggle-deleted-link",d("hide deleted"))))}if(r.length){q.push((!this.showHidden)?([r.length,o("toggle-hidden-link",d("hidden"))].join(" ")):(o("toggle-hidden-link",d("hide hidden"))))}return p.find("> .controls .subtitle").html(q.join(", "))},_renderTags:function(n){var o=this;this.tagsEditor=new l.TagsEditor({model:this.model,el:n.find(".controls .tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){o.toggleHDATagEditors(true,o.fxSpeed)},onhide:function(){o.toggleHDATagEditors(false,o.fxSpeed)},$activator:b({title:d("Edit history tags"),classes:"history-tag-btn",faIcon:"fa-tags"}).appendTo(n.find(".controls .actions"))})},_renderAnnotation:function(n){var o=this;this.annotationEditor=new c.AnnotationEditor({model:this.model,el:n.find(".controls .annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){o.toggleHDAAnnotationEditors(true,o.fxSpeed)},onhide:function(){o.toggleHDAAnnotationEditors(false,o.fxSpeed)},$activator:b({title:d("Edit history annotation"),classes:"history-annotate-btn",faIcon:"fa-comment"}).appendTo(n.find(".controls .actions"))})},_setUpBehaviors:function(n){n=n||this.$el;k.prototype._setUpBehaviors.call(this,n);if(!this.model){return}if(this.multiselectActions.length){this.actionsPopup=new a(n.find(".list-action-popup-btn"),this.multiselectActions)}if((!Galaxy.currUser||Galaxy.currUser.isAnonymous())||(Galaxy.currUser.id!==this.model.get("user_id"))){return}var o=this,p=".controls .name";n.find(p).attr("title",d("Click to rename history")).tooltip({placement:"bottom"}).make_text_editable({on_finish:function(q){var r=o.model.get("name");if(q&&q!==r){o.$el.find(p).text(q);o.model.save({name:q}).fail(function(){o.$el.find(p).text(o.model.previous("name"))})}else{o.$el.find(p).text(r)}}})},_getActions:function(){var n=this,o=[{html:d("Hide datasets"),func:function(){var p=f.HistoryDatasetAssociation.prototype.hide;n.getSelectedModels().ajaxQueue(p)}},{html:d("Unhide datasets"),func:function(){var p=f.HistoryDatasetAssociation.prototype.unhide;n.getSelectedModels().ajaxQueue(p)}},{html:d("Delete datasets"),func:function(){var p=f.HistoryDatasetAssociation.prototype["delete"];n.getSelectedModels().ajaxQueue(p)}},{html:d("Undelete datasets"),func:function(){var p=f.HistoryDatasetAssociation.prototype.undelete;n.getSelectedModels().ajaxQueue(p)}}];if(n.purgeAllowed){o.push({html:d("Permanently delete datasets"),func:function(){if(confirm(d("This will permanently remove the data in your datasets. Are you sure?"))){var p=f.HistoryDatasetAssociation.prototype.purge;n.getSelectedModels().ajaxQueue(p)}}})}o.push({html:d("Build Dataset List"),func:function(){n.getSelectedModels().promoteToHistoryDatasetCollection(n.model,"list")}});o.push({html:d("Build Dataset Pair"),func:function(){n.getSelectedModels().promoteToHistoryDatasetCollection(n.model,"paired")}});o.push({html:d("Build List of Dataset Pairs"),func:_.bind(n._showPairedCollectionModal,n)});return o},_showPairedCollectionModal:function(){var n=this,o=n.getSelectedModels().toJSON().filter(function(p){return p.history_content_type==="dataset"&&p.state===m.OK});if(o.length){require(["mvc/collection/paired-collection-creator"],function(p){window.creator=p.pairedCollectionCreatorModal(o,{historyId:n.model.id})})}else{}},_attachItems:function(n){this.$list(n).append(this.views.reverse().map(function(o){return o.$el}));return this},_attachView:function(o){var n=this;n.views.unshift(o);n.$list().prepend(o.render(0).$el.hide());o.$el.slideDown(n.fxSpeed)},_getItemViewOptions:function(o){var n=k.prototype._getItemViewOptions.call(this,o);_.extend(n,{purgeAllowed:this.purgeAllowed,tagsEditorShown:(this.tagsEditor&&!this.tagsEditor.hidden),annotationEditorShown:(this.annotationEditor&&!this.annotationEditor.hidden)});return n},_handleHdaDeletionChange:function(n){if(n.get("deleted")&&!this.showDeleted){this.removeItemView(n)}this._renderCounts()},_handleHdaVisibleChange:function(n){if(n.hidden()&&!this.storage.showHidden){this.removeItemView(n)}this._renderCounts()},toggleHDATagEditors:function(n){var o=Array.prototype.slice.call(arguments,1);_.each(this.views,function(p){if(p.tagsEditor){p.tagsEditor.toggle.apply(p.tagsEditor,o)}})},toggleHDAAnnotationEditors:function(n){var o=Array.prototype.slice.call(arguments,1);_.each(this.views,function(p){if(p.annotationEditor){p.annotationEditor.toggle.apply(p.annotationEditor,o)}})},events:_.extend(_.clone(k.prototype.events),{"click .show-selectors-btn":"toggleSelectors","click .toggle-deleted-link":function(n){this.toggleShowDeleted()},"click .toggle-hidden-link":function(n){this.toggleShowHidden()}}),updateHistoryDiskSize:function(){this.$el.find(".history-size").text(this.model.get("nice_size"))},dropTargetOn:function(){if(this.dropTarget){return this}this.dropTarget=true;var o={dragenter:_.bind(this.dragenter,this),dragover:_.bind(this.dragover,this),dragleave:_.bind(this.dragleave,this),drop:_.bind(this.drop,this)};var p=this._renderDropTarget();this.$list().before([this._renderDropTargetHelp(),p]);for(var n in o){if(o.hasOwnProperty(n)){p.on(n,o[n])}}return this},_renderDropTarget:function(){return $("<div/>").addClass("history-drop-target").css({height:"64px",margin:"0px 10px 10px 10px",border:"1px dashed black","border-radius":"3px"})},_renderDropTargetHelp:function(){return $("<div/>").addClass("history-drop-target-help").css({margin:"10px 10px 4px 10px",color:"grey","font-size":"80%","font-style":"italic"}).text(d("Drag datasets here to copy them to the current history"))},dropTargetOff:function(){if(!this.dropTarget){return this}this.dropTarget=false;this.$(".history-drop-target").remove();this.$(".history-drop-target-help").remove();return this},dropTargetToggle:function(){if(this.dropTarget){this.dropTargetOff()}else{this.dropTargetOn()}return this},dragenter:function(n){n.preventDefault();n.stopPropagation();this.$(".history-drop-target").css("border","2px solid black")},dragover:function(n){n.preventDefault();n.stopPropagation()},dragleave:function(n){n.preventDefault();n.stopPropagation();this.$(".history-drop-target").css("border","1px dashed black")},drop:function(p){p.preventDefault();p.dataTransfer.dropEffect="move";var n=this,q=p.dataTransfer.getData("text");try{q=JSON.parse(q)}catch(o){this.warn("error parsing JSON from drop:",q)}this.trigger("droptarget:drop",p,q,n);return false},dataDropped:function(o){var n=this;if(_.isObject(o)&&o.model_class==="HistoryDatasetAssociation"&&o.id){return n.model.contents.copy(o.id)}return jQuery.when()},toString:function(){return"HistoryPanelEdit("+((this.model)?(this.model.get("name")):(""))+")"}});return{HistoryPanelEdit:g}}); \ No newline at end of file +define(["mvc/history/history-panel","mvc/history/history-contents","mvc/dataset/states","mvc/history/hda-model","mvc/history/hda-li-edit","mvc/history/hdca-li-edit","mvc/tags","mvc/annotations","ui/fa-icon-button","mvc/ui/popup-menu","utils/localization"],function(h,j,m,f,e,i,l,c,b,a,d){var k=h.HistoryPanel;var g=k.extend({HDAViewClass:e.HDAListItemEdit,HDCAViewClass:i.HDCAListItemEdit,initialize:function(n){n=n||{};k.prototype.initialize.call(this,n);this.tagsEditor=null;this.annotationEditor=null;this.purgeAllowed=n.purgeAllowed||false;this.annotationEditorShown=n.annotationEditorShown||false;this.tagsEditorShown=n.tagsEditorShown||false;this.multiselectActions=n.multiselectActions||this._getActions()},_setUpListeners:function(){var n=this;k.prototype._setUpListeners.call(n);n.on("drop",function(o,p){n.dataDropped(p);n.dropTargetOff()});n.on("view:attached view:removed",function(){n._renderCounts()},n)},_setUpCollectionListeners:function(){k.prototype._setUpCollectionListeners.call(this);this.collection.on("change:deleted",this._handleHdaDeletionChange,this);this.collection.on("change:visible",this._handleHdaVisibleChange,this);this.collection.on("change:purged",function(n){this.model.fetch()},this);return this},_setUpModelListeners:function(){k.prototype._setUpModelListeners.call(this);this.model.on("change:nice_size",this.updateHistoryDiskSize,this);return this},_buildNewRender:function(){var n=k.prototype._buildNewRender.call(this);if(!this.model){return n}if(Galaxy&&Galaxy.currUser&&Galaxy.currUser.id&&Galaxy.currUser.id===this.model.get("user_id")){this._renderTags(n);this._renderAnnotation(n)}return n},renderItems:function(o){var n=k.prototype.renderItems.call(this,o);this._renderCounts(o);return n},_renderCounts:function(p){function o(s,t){return['<a class="',s,'" href="javascript:void(0);">',t,"</a>"].join("")}p=p||this.$el;var n=this.collection.where({deleted:true}),r=this.collection.where({visible:false}),q=[];if(this.views.length){q.push([this.views.length,d("shown")].join(" "))}if(n.length){q.push((!this.showDeleted)?([n.length,o("toggle-deleted-link",d("deleted"))].join(" ")):(o("toggle-deleted-link",d("hide deleted"))))}if(r.length){q.push((!this.showHidden)?([r.length,o("toggle-hidden-link",d("hidden"))].join(" ")):(o("toggle-hidden-link",d("hide hidden"))))}return p.find("> .controls .subtitle").html(q.join(", "))},_renderTags:function(n){var o=this;this.tagsEditor=new l.TagsEditor({model:this.model,el:n.find(".controls .tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){o.toggleHDATagEditors(true,o.fxSpeed)},onhide:function(){o.toggleHDATagEditors(false,o.fxSpeed)},$activator:b({title:d("Edit history tags"),classes:"history-tag-btn",faIcon:"fa-tags"}).appendTo(n.find(".controls .actions"))})},_renderAnnotation:function(n){var o=this;this.annotationEditor=new c.AnnotationEditor({model:this.model,el:n.find(".controls .annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){o.toggleHDAAnnotationEditors(true,o.fxSpeed)},onhide:function(){o.toggleHDAAnnotationEditors(false,o.fxSpeed)},$activator:b({title:d("Edit history annotation"),classes:"history-annotate-btn",faIcon:"fa-comment"}).appendTo(n.find(".controls .actions"))})},_setUpBehaviors:function(n){n=n||this.$el;k.prototype._setUpBehaviors.call(this,n);if(!this.model){return}if(this.multiselectActions.length){this.actionsPopup=new a(n.find(".list-action-popup-btn"),this.multiselectActions)}if((!Galaxy.currUser||Galaxy.currUser.isAnonymous())||(Galaxy.currUser.id!==this.model.get("user_id"))){return}var o=this,p=".controls .name";n.find(p).attr("title",d("Click to rename history")).tooltip({placement:"bottom"}).make_text_editable({on_finish:function(q){var r=o.model.get("name");if(q&&q!==r){o.$el.find(p).text(q);o.model.save({name:q}).fail(function(){o.$el.find(p).text(o.model.previous("name"))})}else{o.$el.find(p).text(r)}}})},_getActions:function(){var n=this,o=[{html:d("Hide datasets"),func:function(){var p=f.HistoryDatasetAssociation.prototype.hide;n.getSelectedModels().ajaxQueue(p)}},{html:d("Unhide datasets"),func:function(){var p=f.HistoryDatasetAssociation.prototype.unhide;n.getSelectedModels().ajaxQueue(p)}},{html:d("Delete datasets"),func:function(){var p=f.HistoryDatasetAssociation.prototype["delete"];n.getSelectedModels().ajaxQueue(p)}},{html:d("Undelete datasets"),func:function(){var p=f.HistoryDatasetAssociation.prototype.undelete;n.getSelectedModels().ajaxQueue(p)}}];if(n.purgeAllowed){o.push({html:d("Permanently delete datasets"),func:function(){if(confirm(d("This will permanently remove the data in your datasets. Are you sure?"))){var p=f.HistoryDatasetAssociation.prototype.purge;n.getSelectedModels().ajaxQueue(p)}}})}o.push({html:d("Build Dataset List"),func:function(){n.getSelectedModels().promoteToHistoryDatasetCollection(n.model,"list")}});o.push({html:d("Build Dataset Pair"),func:function(){n.getSelectedModels().promoteToHistoryDatasetCollection(n.model,"paired")}});o.push({html:d("Build List of Dataset Pairs"),func:_.bind(n._showPairedCollectionModal,n)});return o},_showPairedCollectionModal:function(){var n=this,o=n.getSelectedModels().toJSON().filter(function(p){return p.history_content_type==="dataset"&&p.state===m.OK});if(o.length){require(["mvc/collection/paired-collection-creator"],function(p){window.creator=p.pairedCollectionCreatorModal(o,{historyId:n.model.id})})}else{}},_attachItems:function(n){this.$list(n).append(this.views.reverse().map(function(o){return o.$el}));return this},_attachView:function(o){var n=this;n.views.unshift(o);n.$list().prepend(o.render(0).$el.hide());n.trigger("view:attached",o);o.$el.slideDown(n.fxSpeed,function(){n.trigger("view:attached:rendered")})},_getItemViewOptions:function(o){var n=k.prototype._getItemViewOptions.call(this,o);_.extend(n,{purgeAllowed:this.purgeAllowed,tagsEditorShown:(this.tagsEditor&&!this.tagsEditor.hidden),annotationEditorShown:(this.annotationEditor&&!this.annotationEditor.hidden)});return n},_handleHdaDeletionChange:function(n){if(n.get("deleted")&&!this.showDeleted){this.removeItemView(n)}this._renderCounts()},_handleHdaVisibleChange:function(n){if(n.hidden()&&!this.storage.showHidden){this.removeItemView(n)}this._renderCounts()},toggleHDATagEditors:function(n){var o=Array.prototype.slice.call(arguments,1);_.each(this.views,function(p){if(p.tagsEditor){p.tagsEditor.toggle.apply(p.tagsEditor,o)}})},toggleHDAAnnotationEditors:function(n){var o=Array.prototype.slice.call(arguments,1);_.each(this.views,function(p){if(p.annotationEditor){p.annotationEditor.toggle.apply(p.annotationEditor,o)}})},events:_.extend(_.clone(k.prototype.events),{"click .show-selectors-btn":"toggleSelectors","click .toggle-deleted-link":function(n){this.toggleShowDeleted()},"click .toggle-hidden-link":function(n){this.toggleShowHidden()}}),updateHistoryDiskSize:function(){this.$el.find(".history-size").text(this.model.get("nice_size"))},dropTargetOn:function(){if(this.dropTarget){return this}this.dropTarget=true;var o={dragenter:_.bind(this.dragenter,this),dragover:_.bind(this.dragover,this),dragleave:_.bind(this.dragleave,this),drop:_.bind(this.drop,this)};var p=this._renderDropTarget();this.$list().before([this._renderDropTargetHelp(),p]);for(var n in o){if(o.hasOwnProperty(n)){p.on(n,o[n])}}return this},_renderDropTarget:function(){return $("<div/>").addClass("history-drop-target").css({height:"64px",margin:"0px 10px 10px 10px",border:"1px dashed black","border-radius":"3px"})},_renderDropTargetHelp:function(){return $("<div/>").addClass("history-drop-target-help").css({margin:"10px 10px 4px 10px",color:"grey","font-size":"80%","font-style":"italic"}).text(d("Drag datasets here to copy them to the current history"))},dropTargetOff:function(){if(!this.dropTarget){return this}this.dropTarget=false;this.$(".history-drop-target").remove();this.$(".history-drop-target-help").remove();return this},dropTargetToggle:function(){if(this.dropTarget){this.dropTargetOff()}else{this.dropTargetOn()}return this},dragenter:function(n){n.preventDefault();n.stopPropagation();this.$(".history-drop-target").css("border","2px solid black")},dragover:function(n){n.preventDefault();n.stopPropagation()},dragleave:function(n){n.preventDefault();n.stopPropagation();this.$(".history-drop-target").css("border","1px dashed black")},drop:function(p){p.preventDefault();p.dataTransfer.dropEffect="move";var n=this,q=p.dataTransfer.getData("text");try{q=JSON.parse(q)}catch(o){this.warn("error parsing JSON from drop:",q)}this.trigger("droptarget:drop",p,q,n);return false},dataDropped:function(o){var n=this;if(_.isObject(o)&&o.model_class==="HistoryDatasetAssociation"&&o.id){return n.model.contents.copy(o.id)}return jQuery.when()},toString:function(){return"HistoryPanelEdit("+((this.model)?(this.model.get("name")):(""))+")"}});return{HistoryPanelEdit:g}}); \ No newline at end of file diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef static/scripts/packed/mvc/list/list-panel.js --- a/static/scripts/packed/mvc/list/list-panel.js +++ b/static/scripts/packed/mvc/list/list-panel.js @@ -1,1 +1,1 @@ -define(["mvc/list/list-item","ui/loading-indicator","mvc/base-mvc","utils/localization","ui/search-input"],function(d,f,b,c){var e=Backbone.View.extend(b.LoggableMixin).extend({viewClass:d.ListItemView,collectionClass:Backbone.Collection,tagName:"div",className:"list-panel",fxSpeed:"fast",emptyMsg:c("This list is empty"),noneFoundMsg:c("No matching items found"),searchPlaceholder:c("search"),multiselectActions:[],initialize:function(g,h){g=g||{};if(g.logger){this.logger=g.logger}this.log(this+".initialize:",g);this.fxSpeed=_.has(g,"fxSpeed")?(g.fxSpeed):(this.fxSpeed);this.filters=[];this.searchFor=g.searchFor||"";this.indicator=new f(this.$el);this.selecting=(g.selecting!==undefined)?g.selecting:true;this.selected=g.selected||[];this.lastSelected=null;this.dragItems=g.dragItems||false;this.viewClass=g.viewClass||this.viewClass;this.views=[];this.collection=g.collection||(new this.collectionClass([]));this.filters=g.filters||[];this.$scrollContainer=g.$scrollContainer||this.$scrollContainer;this.title=g.title||"";this.subtitle=g.subtitle||"";this.multiselectActions=g.multiselectActions||this.multiselectActions;this.actionsPopup=null;this._setUpListeners()},freeViews:function(){_.each(this.views,function(g){g.off()});this.views=[];return this},_setUpListeners:function(){this.off();this.on("error",function(h,k,g,j,i){console.error(h,k,g,j,i)},this);this.on("loading",function(){this._showLoadingIndicator("loading...",40)},this);this.on("loading-done",function(){this._hideLoadingIndicator(40)},this);this.once("rendered",function(){this.trigger("rendered:initial",this)},this);if(this.logger){this.on("all",function(g){this.log(this+"",arguments)},this)}this._setUpCollectionListeners();this._setUpViewListeners();return this},_setUpCollectionListeners:function(){this.log(this+"._setUpCollectionListeners",this.collection);this.collection.off();this.collection.on("error",function(h,k,g,j,i){this.trigger("error",h,k,g,j,i)},this);this.collection.on("reset",function(){this.renderItems()},this);this.collection.on("add",this.addItemView,this);this.collection.on("remove",this.removeItemView,this);if(this.logger){this.collection.on("all",function(g){this.info(this+"(collection)",arguments)},this)}return this},_setUpViewListeners:function(){this.log(this+"._setUpViewListeners");this.on("view:selected",function(g,h){if(h&&h.shiftKey&&this.lastSelected){var i=this.viewFromModelId(this.lastSelected);if(i){this.selectRange(g,i)}}else{if(h&&h.altKey&&!this.selecting){this.showSelectors()}}this.selected.push(g.model.id);this.lastSelected=g.model.id},this);this.on("view:de-selected",function(g,h){this.selected=_.without(this.selected,g.model.id)},this)},render:function(h){this.log(this+".render",h);var g=this._buildNewRender();this._setUpBehaviors(g);this._queueNewRender(g,h);return this},_buildNewRender:function(){this.debug(this+"(ListPanel)._buildNewRender");var g=$(this.templates.el({},this));this._renderControls(g);this._renderTitle(g);this._renderSubtitle(g);this._renderSearch(g);this.renderItems(g);return g},_renderControls:function(h){this.debug(this+"(ListPanel)._renderControls");var g=$(this.templates.controls({},this));h.find(".controls").replaceWith(g);return g},_renderTitle:function(g){},_renderSubtitle:function(g){},_queueNewRender:function(h,i){i=(i===undefined)?(this.fxSpeed):(i);var g=this;g.log("_queueNewRender:",h,i);$(g).queue("fx",[function(j){this.$el.fadeOut(i,j)},function(j){g._swapNewRender(h);j()},function(j){this.$el.fadeIn(i,j)},function(j){g.trigger("rendered",g);j()}])},_swapNewRender:function(g){this.$el.empty().attr("class",this.className).append(g.children());if(this.selecting){this.showSelectors(0)}return this},_setUpBehaviors:function(g){g=g||this.$el;g.find(".controls [title]").tooltip({placement:"bottom"});return this},$scrollContainer:function(){return this.$el.parent().parent()},$list:function(g){return(g||this.$el).find("> .list-items")},$messages:function(g){return(g||this.$el).find("> .controls .messages")},$emptyMessage:function(g){return(g||this.$el).find("> .empty-message")},renderItems:function(i){i=i||this.$el;var g=this;g.log(this+".renderItems",i);var h=g.$list(i);g.views=g._filterCollection().map(function(j){return g._createItemView(j).render(0)});h.empty();if(g.views.length){g._attachItems(i);g.$emptyMessage(i).hide()}else{g._renderEmptyMessage(i).show()}return g.views},_filterCollection:function(){var g=this;return g.collection.filter(_.bind(g._filterItem,g))},_filterItem:function(h){var g=this;return(_.every(g.filters.map(function(i){return i.call(h)})))&&(!g.searchFor||h.matchesAll(g.searchFor))},_createItemView:function(i){var j=this._getItemViewClass(i),h=_.extend(this._getItemViewOptions(i),{model:i}),g=new j(h);this._setUpItemViewListeners(g);return g},_getItemViewClass:function(g){return this.viewClass},_getItemViewOptions:function(g){return{fxSpeed:this.fxSpeed,expanded:false,selectable:this.selecting,selected:_.contains(this.selected,g.id),draggable:this.dragItems}},_setUpItemViewListeners:function(h){var g=this;h.on("all",function(){var i=Array.prototype.slice.call(arguments,0);i[0]="view:"+i[0];g.trigger.apply(g,i)});h.on("draggable:dragstart",function(l,i){var j={},k=this.getSelectedModels();if(k.length){j=k.toJSON()}else{j=[i.model.toJSON()]}l.dataTransfer.setData("text",JSON.stringify(j))},this);return g},_attachItems:function(g){this.$list(g).append(this.views.map(function(h){return h.$el}));return this},_renderEmptyMessage:function(g){this.debug("_renderEmptyMessage",g,this.searchFor);var h=this.searchFor?this.noneFoundMsg:this.emptyMsg;return this.$emptyMessage(g).text(h)},expandAll:function(){_.each(this.views,function(g){g.expand()})},collapseAll:function(){_.each(this.views,function(g){g.collapse()})},addItemView:function(j,k,i){this.log(this+".addItemView:",j);var h=this;if(!h._filterItem(j)){return undefined}var g=h._createItemView(j);$(g).queue("fx",[function(l){h.$emptyMessage().fadeOut(h.fxSpeed,l)},function(l){h._attachView(g);l()}]);return g},_attachView:function(h){var g=this;g.views.push(h);g.$list().append(h.render(0).$el.hide());h.$el.slideDown(g.fxSpeed)},removeItemView:function(j,k,i){this.log(this+".removeItemView:",j);var h=this,g=h.viewFromModel(j);if(!g){return undefined}h.views=_.without(h.views,g);$({}).queue("fx",[function(l){g.$el.fadeOut(h.fxSpeed,l)},function(l){g.remove();if(!h.views.length){h._renderEmptyMessage().fadeIn(h.fxSpeed,l)}else{l()}}]);return g},viewFromModelId:function(h){for(var g=0;g<this.views.length;g++){if(this.views[g].model.id===h){return this.views[g]}}return undefined},viewFromModel:function(g){if(!g){return undefined}return this.viewFromModelId(g.id)},viewsWhereModel:function(g){return this.views.filter(function(h){var j=h.model.toJSON();for(var i in g){if(g.hasOwnProperty(i)){if(j[i]!==h.model.get(i)){return false}}}return true})},viewRange:function(j,i){if(j===i){return(j)?([j]):([])}var h=this.views.indexOf(j),g=this.views.indexOf(i);if(h===-1||g===-1){if(h===g){return[]}return(h===-1)?([i]):([j])}return(h<g)?this.views.slice(h,g+1):this.views.slice(g,h+1)},_renderSearch:function(g){g.find(".controls .search-input").searchInput({placeholder:this.searchPlaceholder,initialVal:this.searchFor,onfirstsearch:_.bind(this._firstSearch,this),onsearch:_.bind(this.searchItems,this),onclear:_.bind(this.clearSearch,this)});return g},_firstSearch:function(g){this.log("onFirstSearch",g);return this.searchItems(g)},searchItems:function(g){this.searchFor=g;this.trigger("search:searching",g,this);this.renderItems();this.$("> .controls .search-query").val(g);return this},clearSearch:function(g){this.searchFor="";this.trigger("search:clear",this);this.renderItems();this.$("> .controls .search-query").val("");return this},showSelectors:function(g){g=(g!==undefined)?(g):(this.fxSpeed);this.selecting=true;this.$(".list-actions").slideDown(g);_.each(this.views,function(h){h.showSelector(g)})},hideSelectors:function(g){g=(g!==undefined)?(g):(this.fxSpeed);this.selecting=false;this.$(".list-actions").slideUp(g);_.each(this.views,function(h){h.hideSelector(g)});this.selected=[];this.lastSelected=null},toggleSelectors:function(){if(!this.selecting){this.showSelectors()}else{this.hideSelectors()}},selectAll:function(g){_.each(this.views,function(h){h.select(g)})},deselectAll:function(g){this.lastSelected=null;_.each(this.views,function(h){h.deselect(g)})},selectRange:function(i,h){var g=this.viewRange(i,h);_.each(g,function(j){j.select()});return g},getSelectedViews:function(){return _.filter(this.views,function(g){return g.selected})},getSelectedModels:function(){return new this.collection.constructor(_.map(this.getSelectedViews(),function(g){return g.model}))},_showLoadingIndicator:function(h,g,i){this.debug("_showLoadingIndicator",this.indicator,h,g,i);g=(g!==undefined)?(g):(this.fxSpeed);if(!this.indicator){this.indicator=new f(this.$el,this.$el.parent());this.debug("\t created",this.indicator)}if(!this.$el.is(":visible")){this.indicator.show(0,i)}else{this.$el.fadeOut(g);this.indicator.show(h,g,i)}},_hideLoadingIndicator:function(g,h){this.debug("_hideLoadingIndicator",this.indicator,g,h);g=(g!==undefined)?(g):(this.fxSpeed);if(this.indicator){this.indicator.hide(g,h)}},scrollPosition:function(){return this.$scrollContainer().scrollTop()},scrollTo:function(h,g){g=g||0;this.$scrollContainer().animate({scrollTop:h},g);return this},scrollToTop:function(g){return this.scrollTo(0,g)},scrollToItem:function(g,i){if(!g){return this}var h=g.$el.position().top;return this.scrollTo(h,i)},scrollToId:function(h,g){return this.scrollToItem(this.viewFromModelId(h),g)},events:{"click .select-all":"selectAll","click .deselect-all":"deselectAll"},toString:function(){return"ListPanel("+this.collection+")"}});e.prototype.templates=(function(){var h=b.wrapTemplate(["<div>",'<div class="controls"></div>','<div class="list-items"></div>','<div class="empty-message infomessagesmall"></div>',"</div>"]);var g=b.wrapTemplate(['<div class="controls">','<div class="title">','<div class="name"><%= view.title %></div>',"</div>",'<div class="subtitle"><%= view.subtitle %></div>','<div class="actions"></div>','<div class="messages"></div>','<div class="search">','<div class="search-input"></div>',"</div>",'<div class="list-actions">','<div class="btn-group">','<button class="select-all btn btn-default"','data-mode="select">',c("All"),"</button>",'<button class="deselect-all btn btn-default"','data-mode="select">',c("None"),"</button>","</div>","</div>","</div>"]);return{el:h,controls:g}}());var a=e.extend({modelCollectionKey:"contents",initialize:function(g){e.prototype.initialize.call(this,g);this.selecting=(g.selecting!==undefined)?g.selecting:false;this.setModel(this.model,g)},setModel:function(h,g){g=g||{};this.debug(this+".setModel:",h,g);this.freeModel();this.freeViews();if(h){var i=this.model?this.model.get("id"):null;this.model=h;if(this.logger){this.model.logger=this.logger}this._setUpModelListeners();this.collection.off();this.collection=(this.model[this.modelCollectionKey])?this.model[this.modelCollectionKey]:(g.collection||(new this.collectionClass([])));this._setUpCollectionListeners();if(i&&h.get("id")!==i){this.trigger("new-model",this)}}return this},freeModel:function(){if(this.model){this.stopListening(this.model)}return this},_setUpModelListeners:function(){this.log(this+"._setUpModelListeners",this.model);this.model.on("error",function(){var g=Array.prototype.slice.call(arguments,0);g.unshift("error");this.trigger.apply(this,g)},this);return this},_renderControls:function(h){this.debug(this+"(ListPanel)._renderControls");var i=this.model?this.model.toJSON():{},g=$(this.templates.controls(i,this));h.find(".controls").replaceWith(g);return g},toString:function(){return"ModelListPanel("+this.model+")"}});a.prototype.templates=(function(){var g=b.wrapTemplate(['<div class="controls">','<div class="title">','<div class="name"><%= model.name %></div>',"</div>",'<div class="subtitle"><%= view.subtitle %></div>','<div class="actions"></div>','<div class="messages"></div>','<div class="search">','<div class="search-input"></div>',"</div>",'<div class="list-actions">','<div class="btn-group">','<button class="select-all btn btn-default"','data-mode="select">',c("All"),"</button>",'<button class="deselect-all btn btn-default"','data-mode="select">',c("None"),"</button>","</div>","</div>","</div>"]);return _.extend(_.clone(e.prototype.templates),{controls:g})}());return{ListPanel:e,ModelListPanel:a}}); \ No newline at end of file +define(["mvc/list/list-item","ui/loading-indicator","mvc/base-mvc","utils/localization","ui/search-input"],function(d,f,b,c){var e=Backbone.View.extend(b.LoggableMixin).extend({viewClass:d.ListItemView,collectionClass:Backbone.Collection,tagName:"div",className:"list-panel",fxSpeed:"fast",emptyMsg:c("This list is empty"),noneFoundMsg:c("No matching items found"),searchPlaceholder:c("search"),multiselectActions:[],initialize:function(g,h){g=g||{};if(g.logger){this.logger=g.logger}this.log(this+".initialize:",g);this.fxSpeed=_.has(g,"fxSpeed")?(g.fxSpeed):(this.fxSpeed);this.filters=[];this.searchFor=g.searchFor||"";this.indicator=new f(this.$el);this.selecting=(g.selecting!==undefined)?g.selecting:true;this.selected=g.selected||[];this.lastSelected=null;this.dragItems=g.dragItems||false;this.viewClass=g.viewClass||this.viewClass;this.views=[];this.collection=g.collection||(new this.collectionClass([]));this.filters=g.filters||[];this.$scrollContainer=g.$scrollContainer||this.$scrollContainer;this.title=g.title||"";this.subtitle=g.subtitle||"";this.multiselectActions=g.multiselectActions||this.multiselectActions;this.actionsPopup=null;this._setUpListeners()},freeViews:function(){_.each(this.views,function(g){g.off()});this.views=[];return this},_setUpListeners:function(){this.off();this.on("error",function(h,k,g,j,i){console.error(h,k,g,j,i)},this);this.on("loading",function(){this._showLoadingIndicator("loading...",40)},this);this.on("loading-done",function(){this._hideLoadingIndicator(40)},this);this.once("rendered",function(){this.trigger("rendered:initial",this)},this);if(this.logger){this.on("all",function(g){this.log(this+"",arguments)},this)}this._setUpCollectionListeners();this._setUpViewListeners();return this},_setUpCollectionListeners:function(){this.log(this+"._setUpCollectionListeners",this.collection);this.collection.off();this.collection.on("error",function(h,k,g,j,i){this.trigger("error",h,k,g,j,i)},this);this.collection.on("reset",function(){this.renderItems()},this);this.collection.on("add",this.addItemView,this);this.collection.on("remove",this.removeItemView,this);if(this.logger){this.collection.on("all",function(g){this.info(this+"(collection)",arguments)},this)}return this},_setUpViewListeners:function(){this.log(this+"._setUpViewListeners");this.on("view:selected",function(g,h){if(h&&h.shiftKey&&this.lastSelected){var i=this.viewFromModelId(this.lastSelected);if(i){this.selectRange(g,i)}}else{if(h&&h.altKey&&!this.selecting){this.showSelectors()}}this.selected.push(g.model.id);this.lastSelected=g.model.id},this);this.on("view:de-selected",function(g,h){this.selected=_.without(this.selected,g.model.id)},this)},render:function(h){this.log(this+".render",h);var g=this._buildNewRender();this._setUpBehaviors(g);this._queueNewRender(g,h);return this},_buildNewRender:function(){this.debug(this+"(ListPanel)._buildNewRender");var g=$(this.templates.el({},this));this._renderControls(g);this._renderTitle(g);this._renderSubtitle(g);this._renderSearch(g);this.renderItems(g);return g},_renderControls:function(h){this.debug(this+"(ListPanel)._renderControls");var g=$(this.templates.controls({},this));h.find(".controls").replaceWith(g);return g},_renderTitle:function(g){},_renderSubtitle:function(g){},_queueNewRender:function(h,i){i=(i===undefined)?(this.fxSpeed):(i);var g=this;g.log("_queueNewRender:",h,i);$(g).queue("fx",[function(j){this.$el.fadeOut(i,j)},function(j){g._swapNewRender(h);j()},function(j){this.$el.fadeIn(i,j)},function(j){g.trigger("rendered",g);j()}])},_swapNewRender:function(g){this.$el.empty().attr("class",this.className).append(g.children());if(this.selecting){this.showSelectors(0)}return this},_setUpBehaviors:function(g){g=g||this.$el;g.find(".controls [title]").tooltip({placement:"bottom"});return this},$scrollContainer:function(){return this.$el.parent().parent()},$list:function(g){return(g||this.$el).find("> .list-items")},$messages:function(g){return(g||this.$el).find("> .controls .messages")},$emptyMessage:function(g){return(g||this.$el).find("> .empty-message")},renderItems:function(i){i=i||this.$el;var g=this;g.log(this+".renderItems",i);var h=g.$list(i);g.views=g._filterCollection().map(function(j){return g._createItemView(j).render(0)});h.empty();if(g.views.length){g._attachItems(i);g.$emptyMessage(i).hide()}else{g._renderEmptyMessage(i).show()}return g.views},_filterCollection:function(){var g=this;return g.collection.filter(_.bind(g._filterItem,g))},_filterItem:function(h){var g=this;return(_.every(g.filters.map(function(i){return i.call(h)})))&&(!g.searchFor||h.matchesAll(g.searchFor))},_createItemView:function(i){var j=this._getItemViewClass(i),h=_.extend(this._getItemViewOptions(i),{model:i}),g=new j(h);this._setUpItemViewListeners(g);return g},_getItemViewClass:function(g){return this.viewClass},_getItemViewOptions:function(g){return{fxSpeed:this.fxSpeed,expanded:false,selectable:this.selecting,selected:_.contains(this.selected,g.id),draggable:this.dragItems}},_setUpItemViewListeners:function(h){var g=this;h.on("all",function(){var i=Array.prototype.slice.call(arguments,0);i[0]="view:"+i[0];g.trigger.apply(g,i)});h.on("draggable:dragstart",function(l,i){var j={},k=this.getSelectedModels();if(k.length){j=k.toJSON()}else{j=[i.model.toJSON()]}l.dataTransfer.setData("text",JSON.stringify(j))},this);return g},_attachItems:function(g){this.$list(g).append(this.views.map(function(h){return h.$el}));return this},_renderEmptyMessage:function(g){this.debug("_renderEmptyMessage",g,this.searchFor);var h=this.searchFor?this.noneFoundMsg:this.emptyMsg;return this.$emptyMessage(g).text(h)},expandAll:function(){_.each(this.views,function(g){g.expand()})},collapseAll:function(){_.each(this.views,function(g){g.collapse()})},addItemView:function(j,k,i){this.log(this+".addItemView:",j);var h=this;if(!h._filterItem(j)){return undefined}var g=h._createItemView(j);$(g).queue("fx",[function(l){h.$emptyMessage().fadeOut(h.fxSpeed,l)},function(l){h._attachView(g);l()}]);return g},_attachView:function(h){var g=this;g.views.push(h);g.$list().append(h.render(0).$el.hide());g.trigger("view:attached",h);h.$el.slideDown(g.fxSpeed,function(){g.trigger("view:attached:rendered")})},removeItemView:function(j,k,i){this.log(this+".removeItemView:",j);var h=this,g=h.viewFromModel(j);if(!g){return undefined}h.views=_.without(h.views,g);h.trigger("view:removed",g);$({}).queue("fx",[function(l){g.$el.fadeOut(h.fxSpeed,l)},function(l){g.remove();h.trigger("view:removed:rendered");if(!h.views.length){h._renderEmptyMessage().fadeIn(h.fxSpeed,l)}else{l()}}]);return g},viewFromModelId:function(h){for(var g=0;g<this.views.length;g++){if(this.views[g].model.id===h){return this.views[g]}}return undefined},viewFromModel:function(g){if(!g){return undefined}return this.viewFromModelId(g.id)},viewsWhereModel:function(g){return this.views.filter(function(h){var j=h.model.toJSON();for(var i in g){if(g.hasOwnProperty(i)){if(j[i]!==h.model.get(i)){return false}}}return true})},viewRange:function(j,i){if(j===i){return(j)?([j]):([])}var h=this.views.indexOf(j),g=this.views.indexOf(i);if(h===-1||g===-1){if(h===g){return[]}return(h===-1)?([i]):([j])}return(h<g)?this.views.slice(h,g+1):this.views.slice(g,h+1)},_renderSearch:function(g){g.find(".controls .search-input").searchInput({placeholder:this.searchPlaceholder,initialVal:this.searchFor,onfirstsearch:_.bind(this._firstSearch,this),onsearch:_.bind(this.searchItems,this),onclear:_.bind(this.clearSearch,this)});return g},_firstSearch:function(g){this.log("onFirstSearch",g);return this.searchItems(g)},searchItems:function(g){this.searchFor=g;this.trigger("search:searching",g,this);this.renderItems();this.$("> .controls .search-query").val(g);return this},clearSearch:function(g){this.searchFor="";this.trigger("search:clear",this);this.renderItems();this.$("> .controls .search-query").val("");return this},showSelectors:function(g){g=(g!==undefined)?(g):(this.fxSpeed);this.selecting=true;this.$(".list-actions").slideDown(g);_.each(this.views,function(h){h.showSelector(g)})},hideSelectors:function(g){g=(g!==undefined)?(g):(this.fxSpeed);this.selecting=false;this.$(".list-actions").slideUp(g);_.each(this.views,function(h){h.hideSelector(g)});this.selected=[];this.lastSelected=null},toggleSelectors:function(){if(!this.selecting){this.showSelectors()}else{this.hideSelectors()}},selectAll:function(g){_.each(this.views,function(h){h.select(g)})},deselectAll:function(g){this.lastSelected=null;_.each(this.views,function(h){h.deselect(g)})},selectRange:function(i,h){var g=this.viewRange(i,h);_.each(g,function(j){j.select()});return g},getSelectedViews:function(){return _.filter(this.views,function(g){return g.selected})},getSelectedModels:function(){return new this.collection.constructor(_.map(this.getSelectedViews(),function(g){return g.model}))},_showLoadingIndicator:function(h,g,i){this.debug("_showLoadingIndicator",this.indicator,h,g,i);g=(g!==undefined)?(g):(this.fxSpeed);if(!this.indicator){this.indicator=new f(this.$el,this.$el.parent());this.debug("\t created",this.indicator)}if(!this.$el.is(":visible")){this.indicator.show(0,i)}else{this.$el.fadeOut(g);this.indicator.show(h,g,i)}},_hideLoadingIndicator:function(g,h){this.debug("_hideLoadingIndicator",this.indicator,g,h);g=(g!==undefined)?(g):(this.fxSpeed);if(this.indicator){this.indicator.hide(g,h)}},scrollPosition:function(){return this.$scrollContainer().scrollTop()},scrollTo:function(h,g){g=g||0;this.$scrollContainer().animate({scrollTop:h},g);return this},scrollToTop:function(g){return this.scrollTo(0,g)},scrollToItem:function(g,i){if(!g){return this}var h=g.$el.position().top;return this.scrollTo(h,i)},scrollToId:function(h,g){return this.scrollToItem(this.viewFromModelId(h),g)},events:{"click .select-all":"selectAll","click .deselect-all":"deselectAll"},toString:function(){return"ListPanel("+this.collection+")"}});e.prototype.templates=(function(){var h=b.wrapTemplate(["<div>",'<div class="controls"></div>','<div class="list-items"></div>','<div class="empty-message infomessagesmall"></div>',"</div>"]);var g=b.wrapTemplate(['<div class="controls">','<div class="title">','<div class="name"><%= view.title %></div>',"</div>",'<div class="subtitle"><%= view.subtitle %></div>','<div class="actions"></div>','<div class="messages"></div>','<div class="search">','<div class="search-input"></div>',"</div>",'<div class="list-actions">','<div class="btn-group">','<button class="select-all btn btn-default"','data-mode="select">',c("All"),"</button>",'<button class="deselect-all btn btn-default"','data-mode="select">',c("None"),"</button>","</div>","</div>","</div>"]);return{el:h,controls:g}}());var a=e.extend({modelCollectionKey:"contents",initialize:function(g){e.prototype.initialize.call(this,g);this.selecting=(g.selecting!==undefined)?g.selecting:false;this.setModel(this.model,g)},setModel:function(h,g){g=g||{};this.debug(this+".setModel:",h,g);this.freeModel();this.freeViews();if(h){var i=this.model?this.model.get("id"):null;this.model=h;if(this.logger){this.model.logger=this.logger}this._setUpModelListeners();this.collection.off();this.collection=(this.model[this.modelCollectionKey])?this.model[this.modelCollectionKey]:(g.collection||(new this.collectionClass([])));this._setUpCollectionListeners();if(i&&h.get("id")!==i){this.trigger("new-model",this)}}return this},freeModel:function(){if(this.model){this.stopListening(this.model)}return this},_setUpModelListeners:function(){this.log(this+"._setUpModelListeners",this.model);this.model.on("error",function(){var g=Array.prototype.slice.call(arguments,0);g.unshift("error");this.trigger.apply(this,g)},this);return this},_renderControls:function(h){this.debug(this+"(ListPanel)._renderControls");var i=this.model?this.model.toJSON():{},g=$(this.templates.controls(i,this));h.find(".controls").replaceWith(g);return g},toString:function(){return"ModelListPanel("+this.model+")"}});a.prototype.templates=(function(){var g=b.wrapTemplate(['<div class="controls">','<div class="title">','<div class="name"><%= model.name %></div>',"</div>",'<div class="subtitle"><%= view.subtitle %></div>','<div class="actions"></div>','<div class="messages"></div>','<div class="search">','<div class="search-input"></div>',"</div>",'<div class="list-actions">','<div class="btn-group">','<button class="select-all btn btn-default"','data-mode="select">',c("All"),"</button>",'<button class="deselect-all btn btn-default"','data-mode="select">',c("None"),"</button>","</div>","</div>","</div>"]);return _.extend(_.clone(e.prototype.templates),{controls:g})}());return{ListPanel:e,ModelListPanel:a}}); \ No newline at end of file diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef static/scripts/packed/mvc/upload/upload-row.js --- a/static/scripts/packed/mvc/upload/upload-row.js +++ b/static/scripts/packed/mvc/upload/upload-row.js @@ -1,1 +1,1 @@ -define(["utils/utils","mvc/upload/upload-model","mvc/upload/upload-settings","mvc/ui/ui-popover","mvc/ui/ui-select"],function(d,b,a,c,e){return Backbone.View.extend({options:{padding:8},status_classes:{init:"upload-icon-button fa fa-trash-o",queued:"upload-icon fa fa-spinner fa-spin",running:"upload-icon fa fa-spinner fa-spin",success:"upload-icon-button fa fa-check",error:"upload-icon-button fa fa-exclamation-triangle"},settings:null,select_genome:null,select_extension:null,initialize:function(k,h){this.app=k;var f=this;this.model=new b.Model(h);this.setElement(this._template(h));var j=this.$el;this.settings=new c.View({title:"Upload configuration",container:j.find("#settings"),placement:"bottom"});var i=this.app.select_genome.value();this.select_genome=new e.View({css:"genome",onchange:function(l){f.model.set("genome",l);f.app.updateGenome(l)},data:f.app.list_genomes,container:j.find("#genome"),value:i});this.model.set("genome",i);var g=this.app.select_extension.value();this.select_extension=new e.View({css:"extension",onchange:function(l){f.model.set("extension",l);f.app.updateExtension(l)},data:f.app.list_extensions,container:j.find("#extension"),value:g});this.model.set("extension",g);j.find("#symbol").on("click",function(){f._removeRow()});j.find("#extension-info").on("click",function(l){f.app.showExtensionInfo({$el:$(l.target),title:f.select_extension.text(),extension:f.select_extension.value()})}).on("mousedown",function(l){l.preventDefault()});j.find("#settings").on("click",function(l){f._showSettings()}).on("mousedown",function(l){l.preventDefault()});j.find("#text-content").on("keyup",function(l){f.model.set("url_paste",$(l.target).val());f.model.set("file_size",$(l.target).val().length)});j.find("#space_to_tabs").on("change",function(l){f.model.set("space_to_tabs",$(l.target).prop("checked"))});this.model.on("change:percentage",function(){f._refreshPercentage()});this.model.on("change:status",function(){f._refreshStatus()});this.model.on("change:info",function(){f._refreshInfo()});this.model.on("change:genome",function(){f._refreshGenome()});this.model.on("change:extension",function(){f._refreshExtension()});this.model.on("change:file_size",function(){f._refreshFileSize()});this.model.on("remove",function(){f.remove()});this.app.collection.on("reset",function(){f.remove()})},render:function(){var m=this.model.get("file_name");var g=this.model.get("file_size");var j=this.model.get("file_mode");var i=this.$el;i.find("#title").html(m);i.find("#size").html(d.bytesToString(g));i.find("#mode").removeClass().addClass("mode");if(j=="new"){var l=i.find("#text");var k=this.options.padding;var h=i.width()-2*k;var f=i.height()-k;l.css("width",h+"px");l.css("top",f+"px");i.height(f+l.height()+2*k);l.show();i.find("#mode").addClass("fa fa-pencil")}if(j=="local"){i.find("#mode").addClass("fa fa-laptop")}if(j=="ftp"){i.find("#mode").addClass("fa fa-code-fork")}},remove:function(){this.select_genome.remove();this.select_extension.remove();Backbone.View.prototype.remove.apply(this)},_refreshExtension:function(){this.select_extension.value(this.model.get("extension"))},_refreshGenome:function(){this.select_genome.value(this.model.get("genome"))},_refreshInfo:function(){var f=this.model.get("info");if(f){this.$el.find("#info").html("<strong>Failed: </strong>"+f).show()}else{this.$el.find("#info").hide()}},_refreshPercentage:function(){var f=parseInt(this.model.get("percentage"));this.$el.find(".progress-bar").css({width:f+"%"});if(f!=100){this.$el.find("#percentage").html(f+"%")}else{this.$el.find("#percentage").html("Adding to history...")}},_refreshStatus:function(){var g=this.$el;var f=this.model.get("status");var i=this.status_classes[f];var h=this.$el.find("#symbol");h.removeClass();h.addClass(i);if(f=="init"){this.select_genome.enable();this.select_extension.enable();g.find("#text-content").attr("disabled",false);g.find("#space_to_tabs").attr("disabled",false)}else{this.select_genome.disable();this.select_extension.disable();g.find("#text-content").attr("disabled",true);g.find("#space_to_tabs").attr("disabled",true)}if(f=="success"){g.addClass("success");g.find("#percentage").html("100%")}if(f=="error"){g.addClass("danger");g.find(".progress").remove()}},_refreshFileSize:function(){var f=this.model.get("file_size");this.$el.find("#size").html(d.bytesToString(f))},_removeRow:function(){var f=this.model.get("status");if(f=="init"||f=="success"||f=="error"){this.app.collection.remove(this.model)}},_showSettings:function(){if(!this.settings.visible){this.settings.empty();this.settings.append((new a(this)).$el);this.settings.show()}else{this.settings.hide()}},_template:function(f){return'<tr id="upload-item-'+f.id+'" class="upload-item"><td><div class="name-column"><div id="mode"></div><div id="title" class="title"></div><div id="text" class="text"><div class="text-info">You can tell Galaxy to download data from web by entering URL in this box (one per line). You can also directly paste the contents of a file.</div><textarea id="text-content" class="text-content form-control"></textarea></div></div></td><td><div id="size" class="size"></div></td><td><div id="extension" class="extension" style="float: left;"/>  <div id="extension-info" class="upload-icon-button fa fa-search"/></td><td><div id="genome" class="genome" /></td><td><div id="settings" class="upload-icon-button fa fa-gear"></div><td><div id="info" class="info"><div class="progress"><div class="progress-bar progress-bar-success"></div><div id="percentage" class="percentage">0%</div></div></div></td><td><div id="symbol" class="'+this.status_classes.init+'"></div></td></tr>'}})}); \ No newline at end of file +define(["utils/utils","mvc/upload/upload-model","mvc/upload/upload-settings","mvc/ui/ui-popover","mvc/ui/ui-select"],function(d,b,a,c,e){return Backbone.View.extend({options:{padding:8},status_classes:{init:"upload-icon-button fa fa-trash-o",queued:"upload-icon fa fa-spinner fa-spin",running:"upload-icon fa fa-spinner fa-spin",success:"upload-icon-button fa fa-check",error:"upload-icon-button fa fa-exclamation-triangle"},settings:null,select_genome:null,select_extension:null,initialize:function(k,h){this.app=k;var f=this;this.model=new b.Model(h);this.setElement(this._template(h));var j=this.$el;this.settings=new c.View({title:"Upload configuration",container:j.find("#settings"),placement:"bottom"});var i=this.app.select_genome.value();this.select_genome=new e.View({css:"genome",onchange:function(l){f.model.set("genome",l);f.app.updateGenome(l,true)},data:f.app.list_genomes,container:j.find("#genome"),value:i});this.model.set("genome",i);var g=this.app.select_extension.value();this.select_extension=new e.View({css:"extension",onchange:function(l){f.model.set("extension",l);f.app.updateExtension(l,true)},data:f.app.list_extensions,container:j.find("#extension"),value:g});this.model.set("extension",g);j.find("#symbol").on("click",function(){f._removeRow()});j.find("#extension-info").on("click",function(l){f.app.showExtensionInfo({$el:$(l.target),title:f.select_extension.text(),extension:f.select_extension.value()})}).on("mousedown",function(l){l.preventDefault()});j.find("#settings").on("click",function(l){f._showSettings()}).on("mousedown",function(l){l.preventDefault()});j.find("#text-content").on("keyup",function(l){f.model.set("url_paste",$(l.target).val());f.model.set("file_size",$(l.target).val().length)});j.find("#space_to_tabs").on("change",function(l){f.model.set("space_to_tabs",$(l.target).prop("checked"))});this.model.on("change:percentage",function(){f._refreshPercentage()});this.model.on("change:status",function(){f._refreshStatus()});this.model.on("change:info",function(){f._refreshInfo()});this.model.on("change:genome",function(){f._refreshGenome()});this.model.on("change:extension",function(){f._refreshExtension()});this.model.on("change:file_size",function(){f._refreshFileSize()});this.model.on("remove",function(){f.remove()});this.app.collection.on("reset",function(){f.remove()})},render:function(){var m=this.model.get("file_name");var g=this.model.get("file_size");var j=this.model.get("file_mode");var i=this.$el;i.find("#title").html(m);i.find("#size").html(d.bytesToString(g));i.find("#mode").removeClass().addClass("mode");if(j=="new"){var l=i.find("#text");var k=this.options.padding;var h=i.width()-2*k;var f=i.height()-k;l.css("width",h+"px");l.css("top",f+"px");i.height(f+l.height()+2*k);l.show();i.find("#mode").addClass("fa fa-pencil")}if(j=="local"){i.find("#mode").addClass("fa fa-laptop")}if(j=="ftp"){i.find("#mode").addClass("fa fa-code-fork")}},remove:function(){this.select_genome.remove();this.select_extension.remove();Backbone.View.prototype.remove.apply(this)},_refreshExtension:function(){this.select_extension.value(this.model.get("extension"))},_refreshGenome:function(){this.select_genome.value(this.model.get("genome"))},_refreshInfo:function(){var f=this.model.get("info");if(f){this.$el.find("#info").html("<strong>Failed: </strong>"+f).show()}else{this.$el.find("#info").hide()}},_refreshPercentage:function(){var f=parseInt(this.model.get("percentage"));this.$el.find(".progress-bar").css({width:f+"%"});if(f!=100){this.$el.find("#percentage").html(f+"%")}else{this.$el.find("#percentage").html("Adding to history...")}},_refreshStatus:function(){var g=this.$el;var f=this.model.get("status");var i=this.status_classes[f];var h=this.$el.find("#symbol");h.removeClass();h.addClass(i);if(f=="init"){this.select_genome.enable();this.select_extension.enable();g.find("#text-content").attr("disabled",false);g.find("#space_to_tabs").attr("disabled",false)}else{this.select_genome.disable();this.select_extension.disable();g.find("#text-content").attr("disabled",true);g.find("#space_to_tabs").attr("disabled",true)}if(f=="success"){g.addClass("success");g.find("#percentage").html("100%")}if(f=="error"){g.addClass("danger");g.find(".progress").remove()}},_refreshFileSize:function(){var f=this.model.get("file_size");this.$el.find("#size").html(d.bytesToString(f))},_removeRow:function(){var f=this.model.get("status");if(f=="init"||f=="success"||f=="error"){this.app.collection.remove(this.model)}},_showSettings:function(){if(!this.settings.visible){this.settings.empty();this.settings.append((new a(this)).$el);this.settings.show()}else{this.settings.hide()}},_template:function(f){return'<tr id="upload-item-'+f.id+'" class="upload-item"><td><div class="name-column"><div id="mode"></div><div id="title" class="title"></div><div id="text" class="text"><div class="text-info">You can tell Galaxy to download data from web by entering URL in this box (one per line). You can also directly paste the contents of a file.</div><textarea id="text-content" class="text-content form-control"></textarea></div></div></td><td><div id="size" class="size"></div></td><td><div id="extension" class="extension" style="float: left;"/>  <div id="extension-info" class="upload-icon-button fa fa-search"/></td><td><div id="genome" class="genome" /></td><td><div id="settings" class="upload-icon-button fa fa-gear"></div><td><div id="info" class="info"><div class="progress"><div class="progress-bar progress-bar-success"></div><div id="percentage" class="percentage">0%</div></div></div></td><td><div id="symbol" class="'+this.status_classes.init+'"></div></td></tr>'}})}); \ No newline at end of file diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef static/scripts/packed/mvc/upload/upload-view.js --- a/static/scripts/packed/mvc/upload/upload-view.js +++ b/static/scripts/packed/mvc/upload/upload-view.js @@ -1,1 +1,1 @@ -define(["utils/utils","mvc/upload/upload-button","mvc/upload/upload-model","mvc/upload/upload-row","mvc/upload/upload-ftp","mvc/ui/ui-popover","mvc/ui/ui-modal","mvc/ui/ui-select","utils/uploadbox"],function(f,e,c,b,g,d,a,h){return Backbone.View.extend({options:{nginx_upload_path:""},modal:null,ui_button:null,uploadbox:null,current_history:null,select_extension:null,select_genome:null,upload_size:0,list_extensions:[],list_genomes:[],auto:{id:"auto",text:"Auto-detect",description:"This system will try to detect the file type automatically. If your file is not detected properly as one of the known formats, it most likely means that it has some format problems (e.g., different number of columns on different rows). You can still coerce the system to set your data to the format you think it should be. You can also upload compressed files, which will automatically be decompressed."},collection:new c.Collection(),ftp:null,counter:{announce:0,success:0,error:0,running:0,reset:function(){this.announce=this.success=this.error=this.running=0}},initialize:function(j){var i=this;if(j){this.options=_.defaults(j,this.options)}this.ui_button=new e.Model({icon:"fa-upload",tooltip:"Download from URL or upload files from disk",label:"Load Data",onclick:function(k){if(k){k.preventDefault();i.show()}},onunload:function(){if(i.counter.running>0){return"Several uploads are still processing."}}});$(".with-upload-button").append((new e.View(this.ui_button)).$el);var i=this;f.get({url:galaxy_config.root+"api/datatypes?extension_only=False",success:function(k){for(key in k){i.list_extensions.push({id:k[key].extension,text:k[key].extension,description:k[key].description,description_url:k[key].description_url})}i.list_extensions.sort(function(m,l){return m.id>l.id?1:m.id<l.id?-1:0});if(!i.options.datatypes_disable_auto){i.list_extensions.unshift(i.auto)}i.default_extension=i.list_extensions[0]&&i.list_extensions[0].id}});f.get({url:galaxy_config.root+"api/genomes",success:function(k){for(key in k){i.list_genomes.push({id:k[key][1],text:k[key][0]})}i.list_genomes.sort(function(m,l){return m.id>l.id?1:m.id<l.id?-1:0});i.default_genome=i.list_genomes[0]&&i.list_genomes[0].id}});this.collection.on("remove",function(k){i._eventRemove(k)})},show:function(){var i=this;if(!Galaxy.currHistoryPanel||!Galaxy.currHistoryPanel.model){window.setTimeout(function(){i.show()},500);return}if(!this.modal){var i=this;this.modal=new a.View({title:"Download data directly from web or upload files from your disk",body:this._template("upload-box","upload-info"),buttons:{"Choose local file":function(){i.uploadbox.select()},"Choose FTP file":function(){i._eventFtp()},"Paste/Fetch data":function(){i._eventCreate()},Start:function(){i._eventStart()},Pause:function(){i._eventStop()},Reset:function(){i._eventReset()},Close:function(){i.modal.hide()},},height:"400",width:"900",closing_events:true});this.setElement("#upload-box");var i=this;this.uploadbox=this.$el.uploadbox({announce:function(k,l,m){i._eventAnnounce(k,l,m)},initialize:function(k,l,m){return i._eventInitialize(k,l,m)},progress:function(k,l,m){i._eventProgress(k,l,m)},success:function(k,l,m){i._eventSuccess(k,l,m)},error:function(k,l,m){i._eventError(k,l,m)},complete:function(){i._eventComplete()}});var j=this.modal.getButton("Choose FTP file");this.ftp=new d.View({title:"FTP files",container:j});this.select_extension=new h.View({css:"header-selection",data:this.list_extensions,container:this.$el.parent().find("#header-extension"),value:this.default_extension,onchange:function(k){i.updateExtension(k)}});i.$el.parent().find("#header-extension-info").on("click",function(k){i.showExtensionInfo({$el:$(k.target),title:i.select_extension.text(),extension:i.select_extension.value(),placement:"top"})}).on("mousedown",function(k){k.preventDefault()});this.select_genome=new h.View({css:"header-selection",data:this.list_genomes,container:this.$el.parent().find("#header-genome"),value:this.default_genome,onchange:function(k){i.updateGenome(k)}})}this.modal.show();this._updateUser();this._updateScreen()},showExtensionInfo:function(j){var i=this;var k=j.$el;var n=j.extension;var m=j.title;var l=_.findWhere(i.list_extensions,{id:n});this.extension_popup&&this.extension_popup.remove();this.extension_popup=new d.View({placement:j.placement||"bottom",container:k,destroy:true});this.extension_popup.title(m);this.extension_popup.empty();this.extension_popup.append(this._templateDescription(l));this.extension_popup.show()},_eventRemove:function(j){var i=j.get("status");if(i=="success"){this.counter.success--}else{if(i=="error"){this.counter.error--}else{this.counter.announce--}}this._updateScreen();this.uploadbox.remove(j.id)},_eventAnnounce:function(i,j,l){this.counter.announce++;this._updateScreen();var k=new b(this,{id:i,file_name:j.name,file_size:j.size,file_mode:j.mode,file_path:j.path});this.collection.add(k.model);$(this.el).find("tbody:first").append(k.$el);k.render()},_eventInitialize:function(n,k,t){var l=this.collection.get(n);l.set("status","running");var p=l.get("file_name");var o=l.get("file_path");var i=l.get("file_mode");var q=l.get("extension");var s=l.get("genome");var r=l.get("url_paste");var m=l.get("space_to_tabs");var j=l.get("to_posix_lines");if(!r&&!(k.size>0)){return null}this.uploadbox.configure({url:this.options.nginx_upload_path});if(i=="local"){this.uploadbox.configure({paramname:"files_0|file_data"})}else{this.uploadbox.configure({paramname:null})}tool_input={};if(i=="new"){tool_input["files_0|url_paste"]=r}if(i=="ftp"){tool_input["files_0|ftp_files"]=o}tool_input.dbkey=s;tool_input.file_type=q;tool_input["files_0|type"]="upload_dataset";tool_input["files_0|space_to_tab"]=m&&"Yes"||null;tool_input["files_0|to_posix_lines"]=j&&"Yes"||null;data={};data.history_id=this.current_history;data.tool_id="upload1";data.inputs=JSON.stringify(tool_input);return data},_eventProgress:function(j,k,i){var l=this.collection.get(j);l.set("percentage",i);this.ui_button.set("percentage",this._uploadPercentage(i,k.size))},_eventSuccess:function(j,k,m){var l=this.collection.get(j);l.set("percentage",100);l.set("status","success");var i=l.get("file_size");this.ui_button.set("percentage",this._uploadPercentage(100,i));this.upload_completed+=i*100;this.counter.announce--;this.counter.success++;this._updateScreen();Galaxy.currHistoryPanel.refreshContents()},_eventError:function(i,j,l){var k=this.collection.get(i);k.set("percentage",100);k.set("status","error");k.set("info",l);this.ui_button.set("percentage",this._uploadPercentage(100,j.size));this.ui_button.set("status","danger");this.upload_completed+=j.size*100;this.counter.announce--;this.counter.error++;this._updateScreen()},_eventComplete:function(){this.collection.each(function(i){if(i.get("status")=="queued"){i.set("status","init")}});this.counter.running=0;this._updateScreen()},_eventFtp:function(){if(!this.ftp.visible){this.ftp.empty();this.ftp.append((new g(this)).$el);this.ftp.show()}else{this.ftp.hide()}},_eventCreate:function(){this.uploadbox.add([{name:"New File",size:0,mode:"new"}])},_eventStart:function(){if(this.counter.announce==0||this.counter.running>0){return}var i=this;this.upload_size=0;this.upload_completed=0;this.collection.each(function(j){if(j.get("status")=="init"){j.set("status","queued");i.upload_size+=j.get("file_size")}});this.ui_button.set("percentage",0);this.ui_button.set("status","success");this.counter.running=this.counter.announce;this._updateScreen();this.uploadbox.start()},_eventStop:function(){if(this.counter.running==0){return}this.ui_button.set("status","info");this.uploadbox.stop();$("#upload-info").html("Queue will pause after completing the current file...")},_eventReset:function(){if(this.counter.running==0){this.collection.reset();this.counter.reset();this._updateScreen();this.uploadbox.reset();this.select_extension.value(this.default_extension);this.select_genome.value(this.default_genome);this.ui_button.set("percentage",0)}},updateExtension:function(j){var i=this;this.collection.each(function(k){if(k.get("status")=="init"&&k.get("extension")==i.default_extension){k.set("extension",j)}})},updateGenome:function(i){var j=this;this.collection.each(function(k){if(k.get("status")=="init"&&k.get("genome")==j.default_genome){k.set("genome",i)}})},_updateUser:function(){this.current_user=Galaxy.currUser.get("id");this.current_history=null;if(this.current_user){this.current_history=Galaxy.currHistoryPanel.model.get("id")}},_updateScreen:function(){if(this.counter.announce==0){if(this.uploadbox.compatible()){message="You can Drag & Drop files into this box."}else{message="Unfortunately, your browser does not support multiple file uploads or drag&drop.<br>Some supported browsers are: Firefox 4+, Chrome 7+, IE 10+, Opera 12+ or Safari 6+."}}else{if(this.counter.running==0){message="You added "+this.counter.announce+" file(s) to the queue. Add more files or click 'Start' to proceed."}else{message="Please wait..."+this.counter.announce+" out of "+this.counter.running+" remaining."}}$("#upload-info").html(message);if(this.counter.running==0&&this.counter.announce+this.counter.success+this.counter.error>0){this.modal.enableButton("Reset")}else{this.modal.disableButton("Reset")}if(this.counter.running==0&&this.counter.announce>0){this.modal.enableButton("Start")}else{this.modal.disableButton("Start")}if(this.counter.running>0){this.modal.enableButton("Pause")}else{this.modal.disableButton("Pause")}if(this.counter.running==0){this.modal.enableButton("Choose local file");this.modal.enableButton("Choose FTP file");this.modal.enableButton("Paste/Fetch data")}else{this.modal.disableButton("Choose local file");this.modal.disableButton("Choose FTP file");this.modal.disableButton("Paste/Fetch data")}if(this.current_user&&this.options.ftp_upload_dir&&this.options.ftp_upload_site){this.modal.showButton("Choose FTP file")}else{this.modal.hideButton("Choose FTP file")}if(this.counter.announce+this.counter.success+this.counter.error>0){$(this.el).find("#upload-table").show()}else{$(this.el).find("#upload-table").hide()}},_uploadPercentage:function(i,j){return(this.upload_completed+(i*j))/this.upload_size},_templateDescription:function(j){if(j.description){var i=j.description;if(j.description_url){i+=' (<a href="'+j.description_url+'" target="_blank">read more</a>)'}return i}else{return"There is no description available for this file extension."}},_template:function(j,i){return'<div class="upload-top"><h6 id="'+i+'" class="upload-info"></h6></div><div id="'+j+'" class="upload-box"><table id="upload-table" class="table table-striped" style="display: none;"><thead><tr><th>Name</th><th>Size</th><th>Type</th><th>Genome</th><th>Settings</th><th>Status</th><th></th></tr></thead><tbody></tbody></table></div><div id="upload-header" class="upload-header"><span class="header-title">Type (default):</span><span id="header-extension"/><span id="header-extension-info" class="upload-icon-button fa fa-search"/><span class="header-title">Genome (default):</span><span id="header-genome"/></div>'}})}); \ No newline at end of file +define(["utils/utils","mvc/upload/upload-button","mvc/upload/upload-model","mvc/upload/upload-row","mvc/upload/upload-ftp","mvc/ui/ui-popover","mvc/ui/ui-modal","mvc/ui/ui-select","utils/uploadbox"],function(f,e,c,b,g,d,a,h){return Backbone.View.extend({options:{nginx_upload_path:""},default_extension:"auto",default_genome:"?",modal:null,ui_button:null,uploadbox:null,current_history:null,select_extension:null,select_genome:null,upload_size:0,list_extensions:[],list_genomes:[],auto:{id:"auto",text:"Auto-detect",description:"This system will try to detect the file type automatically. If your file is not detected properly as one of the known formats, it most likely means that it has some format problems (e.g., different number of columns on different rows). You can still coerce the system to set your data to the format you think it should be. You can also upload compressed files, which will automatically be decompressed."},collection:new c.Collection(),ftp:null,counter:{announce:0,success:0,error:0,running:0,reset:function(){this.announce=this.success=this.error=this.running=0}},initialize:function(j){var i=this;if(j){this.options=_.defaults(j,this.options)}this.ui_button=new e.Model({icon:"fa-upload",tooltip:"Download from URL or upload files from disk",label:"Load Data",onclick:function(k){if(k){k.preventDefault();i.show()}},onunload:function(){if(i.counter.running>0){return"Several uploads are still processing."}}});$(".with-upload-button").append((new e.View(this.ui_button)).$el);var i=this;f.get({url:galaxy_config.root+"api/datatypes?extension_only=False",success:function(k){for(key in k){i.list_extensions.push({id:k[key].extension,text:k[key].extension,description:k[key].description,description_url:k[key].description_url})}i.list_extensions.sort(function(m,l){return m.text>l.text?1:m.text<l.text?-1:0});if(!i.options.datatypes_disable_auto){i.list_extensions.unshift(i.auto)}}});f.get({url:galaxy_config.root+"api/genomes",success:function(k){for(key in k){i.list_genomes.push({id:k[key][1],text:k[key][0]})}i.list_genomes.sort(function(m,l){if(m.id==i.default_genome){return -1}if(l.id==i.default_genome){return 1}return m.text>l.text?1:m.text<l.text?-1:0})}});this.collection.on("remove",function(k){i._eventRemove(k)})},show:function(){var i=this;if(!Galaxy.currHistoryPanel||!Galaxy.currHistoryPanel.model){window.setTimeout(function(){i.show()},500);return}if(!this.modal){var i=this;this.modal=new a.View({title:"Download data directly from web or upload files from your disk",body:this._template("upload-box","upload-info"),buttons:{"Choose local file":function(){i.uploadbox.select()},"Choose FTP file":function(){i._eventFtp()},"Paste/Fetch data":function(){i._eventCreate()},Start:function(){i._eventStart()},Pause:function(){i._eventStop()},Reset:function(){i._eventReset()},Close:function(){i.modal.hide()},},height:"400",width:"900",closing_events:true});this.setElement("#upload-box");var i=this;this.uploadbox=this.$el.uploadbox({announce:function(k,l,m){i._eventAnnounce(k,l,m)},initialize:function(k,l,m){return i._eventInitialize(k,l,m)},progress:function(k,l,m){i._eventProgress(k,l,m)},success:function(k,l,m){i._eventSuccess(k,l,m)},error:function(k,l,m){i._eventError(k,l,m)},complete:function(){i._eventComplete()}});var j=this.modal.getButton("Choose FTP file");this.ftp=new d.View({title:"FTP files",container:j});this.select_extension=new h.View({css:"header-selection",data:this.list_extensions,container:this.$el.parent().find("#header-extension"),value:this.default_extension,onchange:function(k){i.updateExtension(k)}});i.$el.parent().find("#header-extension-info").on("click",function(k){i.showExtensionInfo({$el:$(k.target),title:i.select_extension.text(),extension:i.select_extension.value(),placement:"top"})}).on("mousedown",function(k){k.preventDefault()});this.select_genome=new h.View({css:"header-selection",data:this.list_genomes,container:this.$el.parent().find("#header-genome"),value:this.default_genome,onchange:function(k){i.updateGenome(k)}})}this.modal.show();this._updateUser();this._updateScreen()},showExtensionInfo:function(j){var i=this;var k=j.$el;var n=j.extension;var m=j.title;var l=_.findWhere(i.list_extensions,{id:n});this.extension_popup&&this.extension_popup.remove();this.extension_popup=new d.View({placement:j.placement||"bottom",container:k,destroy:true});this.extension_popup.title(m);this.extension_popup.empty();this.extension_popup.append(this._templateDescription(l));this.extension_popup.show()},_eventRemove:function(j){var i=j.get("status");if(i=="success"){this.counter.success--}else{if(i=="error"){this.counter.error--}else{this.counter.announce--}}this._updateScreen();this.uploadbox.remove(j.id)},_eventAnnounce:function(i,j,l){this.counter.announce++;this._updateScreen();var k=new b(this,{id:i,file_name:j.name,file_size:j.size,file_mode:j.mode,file_path:j.path});this.collection.add(k.model);$(this.el).find("tbody:first").append(k.$el);k.render()},_eventInitialize:function(n,k,t){var l=this.collection.get(n);l.set("status","running");var p=l.get("file_name");var o=l.get("file_path");var i=l.get("file_mode");var q=l.get("extension");var s=l.get("genome");var r=l.get("url_paste");var m=l.get("space_to_tabs");var j=l.get("to_posix_lines");if(!r&&!(k.size>0)){return null}this.uploadbox.configure({url:this.options.nginx_upload_path});if(i=="local"){this.uploadbox.configure({paramname:"files_0|file_data"})}else{this.uploadbox.configure({paramname:null})}tool_input={};if(i=="new"){tool_input["files_0|url_paste"]=r}if(i=="ftp"){tool_input["files_0|ftp_files"]=o}tool_input.dbkey=s;tool_input.file_type=q;tool_input["files_0|type"]="upload_dataset";tool_input["files_0|space_to_tab"]=m&&"Yes"||null;tool_input["files_0|to_posix_lines"]=j&&"Yes"||null;data={};data.history_id=this.current_history;data.tool_id="upload1";data.inputs=JSON.stringify(tool_input);return data},_eventProgress:function(j,k,i){var l=this.collection.get(j);l.set("percentage",i);this.ui_button.set("percentage",this._uploadPercentage(i,k.size))},_eventSuccess:function(j,k,m){var l=this.collection.get(j);l.set("percentage",100);l.set("status","success");var i=l.get("file_size");this.ui_button.set("percentage",this._uploadPercentage(100,i));this.upload_completed+=i*100;this.counter.announce--;this.counter.success++;this._updateScreen();Galaxy.currHistoryPanel.refreshContents()},_eventError:function(i,j,l){var k=this.collection.get(i);k.set("percentage",100);k.set("status","error");k.set("info",l);this.ui_button.set("percentage",this._uploadPercentage(100,j.size));this.ui_button.set("status","danger");this.upload_completed+=j.size*100;this.counter.announce--;this.counter.error++;this._updateScreen()},_eventComplete:function(){this.collection.each(function(i){if(i.get("status")=="queued"){i.set("status","init")}});this.counter.running=0;this._updateScreen()},_eventFtp:function(){if(!this.ftp.visible){this.ftp.empty();this.ftp.append((new g(this)).$el);this.ftp.show()}else{this.ftp.hide()}},_eventCreate:function(){this.uploadbox.add([{name:"New File",size:0,mode:"new"}])},_eventStart:function(){if(this.counter.announce==0||this.counter.running>0){return}var i=this;this.upload_size=0;this.upload_completed=0;this.collection.each(function(j){if(j.get("status")=="init"){j.set("status","queued");i.upload_size+=j.get("file_size")}});this.ui_button.set("percentage",0);this.ui_button.set("status","success");this.counter.running=this.counter.announce;this._updateScreen();this.uploadbox.start()},_eventStop:function(){if(this.counter.running==0){return}this.ui_button.set("status","info");this.uploadbox.stop();$("#upload-info").html("Queue will pause after completing the current file...")},_eventReset:function(){if(this.counter.running==0){this.collection.reset();this.counter.reset();this._updateScreen();this.uploadbox.reset();this.select_extension.value(this.default_extension);this.select_genome.value(this.default_genome);this.ui_button.set("percentage",0)}},updateExtension:function(k,j){var i=this;this.collection.each(function(l){if(l.get("status")=="init"&&(l.get("extension")==i.default_extension||!j)){l.set("extension",k)}})},updateGenome:function(i,k){var j=this;this.collection.each(function(l){if(l.get("status")=="init"&&(l.get("genome")==j.default_genome||!k)){l.set("genome",i)}})},_updateUser:function(){this.current_user=Galaxy.currUser.get("id");this.current_history=null;if(this.current_user){this.current_history=Galaxy.currHistoryPanel.model.get("id")}},_updateScreen:function(){if(this.counter.announce==0){if(this.uploadbox.compatible()){message="You can Drag & Drop files into this box."}else{message="Unfortunately, your browser does not support multiple file uploads or drag&drop.<br>Some supported browsers are: Firefox 4+, Chrome 7+, IE 10+, Opera 12+ or Safari 6+."}}else{if(this.counter.running==0){message="You added "+this.counter.announce+" file(s) to the queue. Add more files or click 'Start' to proceed."}else{message="Please wait..."+this.counter.announce+" out of "+this.counter.running+" remaining."}}$("#upload-info").html(message);if(this.counter.running==0&&this.counter.announce+this.counter.success+this.counter.error>0){this.modal.enableButton("Reset")}else{this.modal.disableButton("Reset")}if(this.counter.running==0&&this.counter.announce>0){this.modal.enableButton("Start")}else{this.modal.disableButton("Start")}if(this.counter.running>0){this.modal.enableButton("Pause")}else{this.modal.disableButton("Pause")}if(this.counter.running==0){this.modal.enableButton("Choose local file");this.modal.enableButton("Choose FTP file");this.modal.enableButton("Paste/Fetch data")}else{this.modal.disableButton("Choose local file");this.modal.disableButton("Choose FTP file");this.modal.disableButton("Paste/Fetch data")}if(this.current_user&&this.options.ftp_upload_dir&&this.options.ftp_upload_site){this.modal.showButton("Choose FTP file")}else{this.modal.hideButton("Choose FTP file")}if(this.counter.announce+this.counter.success+this.counter.error>0){$(this.el).find("#upload-table").show()}else{$(this.el).find("#upload-table").hide()}},_uploadPercentage:function(i,j){return(this.upload_completed+(i*j))/this.upload_size},_templateDescription:function(j){if(j.description){var i=j.description;if(j.description_url){i+=' (<a href="'+j.description_url+'" target="_blank">read more</a>)'}return i}else{return"There is no description available for this file extension."}},_template:function(j,i){return'<div class="upload-top"><h6 id="'+i+'" class="upload-info"></h6></div><div id="'+j+'" class="upload-box"><table id="upload-table" class="table table-striped" style="display: none;"><thead><tr><th>Name</th><th>Size</th><th>Type</th><th>Genome</th><th>Settings</th><th>Status</th><th></th></tr></thead><tbody></tbody></table></div><div id="upload-header" class="upload-header"><span class="header-title">Type (set all):</span><span id="header-extension"/><span id="header-extension-info" class="upload-icon-button fa fa-search"/><span class="header-title">Genome (set all):</span><span id="header-genome"/></div>'}})}); \ No newline at end of file diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef templates/webapps/galaxy/history/view_multiple.mako --- a/templates/webapps/galaxy/history/view_multiple.mako +++ b/templates/webapps/galaxy/history/view_multiple.mako @@ -33,6 +33,11 @@ display: -ms-flexbox; display: flex; + /* force ff to squish beyond content: + https://developer.mozilla.org/en-US/Firefox/Releases/34/Site_Compatibility#C... */ + min-width: 0px; + min-height: 0px; + -webkit-align-items: stretch; -ms-align-items: stretch; align-items: stretch; diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef test/base/asserts/xml.py --- a/test/base/asserts/xml.py +++ b/test/base/asserts/xml.py @@ -1,10 +1,10 @@ -import elementtree.ElementTree +import xml.etree import re # Helper functions used to work with XML output. def to_xml(output): - return elementtree.ElementTree.fromstring(output) + return xml.etree.fromstring(output) def xml_find_text(output, path): @@ -31,8 +31,7 @@ def assert_has_element_with_path(output, path): """ Asserts the specified output has at least one XML element with a path matching the specified path argument. Valid paths are the - simplified subsets of XPath implemented by elementtree (currently - Galaxy makes use of elementtree 1.2). See + simplified subsets of XPath implemented by xml.etree; http://effbot.org/zone/element-xpath.htm for more information.""" if xml_find(output, path) is None: errmsg = "Expected to find XML element matching expression %s, not such match was found." % path @@ -74,13 +73,13 @@ errmsg = "Expected attribute '%s' on element with path '%s' to match '%s', instead attribute value was '%s'." % (attribute, path, expression, attribute_value) raise AssertionError(errmsg) - + def assert_attribute_is(output, path, attribute, text): """ Asserts the specified attribute of the first element matching the specified path matches exactly the specified text.""" assert_attribute_matches(output, path, attribute, re.escape(text)) - + def assert_element_text(output, path, verify_assertions_function, children): """ Recursively checks the specified assertions against the text of the first element matching the specified path.""" diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef test/base/twilltestcase.py --- a/test/base/twilltestcase.py +++ b/test/base/twilltestcase.py @@ -22,10 +22,9 @@ from urlparse import urlparse from galaxy import eggs -eggs.require( "elementtree" ) eggs.require( 'twill' ) -from elementtree import ElementTree +from xml.etree import ElementTree import twill import twill.commands as tc diff -r f6fb30a976ab8a83d536c5ab883c4d5be921e70c -r e7560410c0f235df210d29545a51327a1af5f7ef test/casperjs/history-share-tests.js --- /dev/null +++ b/test/casperjs/history-share-tests.js @@ -0,0 +1,227 @@ +var require = patchRequire( require ), + spaceghost = require( 'spaceghost' ).fromCasper( casper ), + xpath = require( 'casper' ).selectXPath, + utils = require( 'utils' ), + format = utils.format; + +spaceghost.test.begin( 'Testing the user share form for histories', 0, function suite( test ){ + spaceghost.start(); + + // =================================================================== globals and helpers + var email = spaceghost.user.getRandomEmail(), + password = '123456'; + if( spaceghost.fixtureData.testUser ){ + email = spaceghost.fixtureData.testUser.email; + password = spaceghost.fixtureData.testUser.password; + spaceghost.info( 'Will use fixtureData.testUser: ' + email ); + } + var email2 = spaceghost.user.getRandomEmail(), + password2 = '123456'; + if( spaceghost.fixtureData.testUser2 ){ + email2 = spaceghost.fixtureData.testUser2.email; + password2 = spaceghost.fixtureData.testUser2.password; + } + + var shareLink = 'a[href^="/history/share?"]', + shareSubmit = 'input[name="share_button"]', + firstUserShareButton = '#user-0-popup', + shareHistoryId = null, + shareUserId = null; + + function fromUserSharePage( fn ){ + spaceghost.then( function(){ + this.openHomePage( function(){ + this.historyoptions.clickOption( 'Share or Publish' ); + }); + this.waitForNavigation( 'history/sharing', function(){ + this.jumpToMain( function(){ + this.click( shareLink ); + }); + }); + this.waitForNavigation( 'history/share', function(){ + this.jumpToMain( function(){ + fn.call( this ); + }); + }); + }); + } + + function thenSwitchUser( email, password ){ + spaceghost.then( function(){ + spaceghost.user.logout(); + spaceghost.user.loginOrRegisterUser( email, password ); + }); + return spaceghost; + } + + function thenShareWithUser( comment, emailOrId, thenFn ){ + spaceghost.then( function(){ + fromUserSharePage( function(){ + this.test.comment( comment ); + this.fill( 'form#share', { + email : emailOrId + }); + // strangely, casper's submit=true flag doesn't work well here - need to manually push the button + this.click( shareSubmit ); + }); + spaceghost.then( function(){ + this.jumpToMain( function(){ + thenFn.call( this ); + }); + }); + }); + return spaceghost; + } + + // =================================================================== TESTS + // create user 1 and the test/target history + spaceghost.user.loginOrRegisterUser( email, password ).openHomePage( function(){ + shareHistoryId = this.api.histories.index()[0].id; + this.info( 'shareHistoryId: ' + shareHistoryId ); + }); + spaceghost.then( function(){ + // can't share an empty history (for some reason) + this.api.tools.thenUpload( shareHistoryId, { filepath: '../../test-data/1.bed' }); + }); + + // create user 2 and make sure they can't access the history right now + thenSwitchUser( email2, password2 ).openHomePage( function(){ + shareUserId = this.api.users.index()[0].id; + this.info( 'shareUserId: ' + shareUserId ); + + this.test.comment( 'user2 should not have access to test history' ); + this.api.assertRaises( function(){ + this.api.histories.show( shareHistoryId ); + }, 403, 'History is not accessible by user', 'show failed with error' ); + }); + + thenSwitchUser( email, password ); + thenShareWithUser( "should NOT work: share using non-existant user email", 'chunkylover53@aol.com', function(){ + this.test.assertExists( '.errormessage', 'found error message' ); + this.test.assertSelectorHasText( '.errormessage', 'is not a valid Galaxy user', 'wording is good' ); + }); + thenShareWithUser( "should NOT work: share using current user email", email, function(){ + this.test.assertExists( '.errormessage', 'found error message' ); + this.test.assertSelectorHasText( '.errormessage', 'You cannot send histories to yourself', 'wording is good' ); + }); + thenShareWithUser( "should work: share using email", email2, function(){ + this.test.assertExists( firstUserShareButton, 'found user share button' ); + this.test.assertSelectorHasText( firstUserShareButton, email2, 'share button text is email2' ); + }); + + // user 2 can now access the history + thenSwitchUser( email2, password2 ).openHomePage( function(){ + this.test.comment( 'user 2 can now access the history' ); + this.test.assert( !!this.api.histories.show( shareHistoryId ).id ); + }); + + + // remove share + thenSwitchUser( email, password ).thenOpen( spaceghost.baseUrl + '/history/sharing', function(){ + this.jumpToMain( function(){ + this.click( firstUserShareButton ); + this.wait( 100, function(){ + this.click( 'a[href^="/history/sharing?unshare_user"]' ); + }); + }); + }); + spaceghost.then( function(){ + this.test.assertDoesntExist( firstUserShareButton, 'no user share button seen' ); + }); + + thenSwitchUser( email2, password2 ).openHomePage( function(){ + this.test.comment( 'user2 should not have access to test history (again)' ); + this.api.assertRaises( function(){ + this.api.histories.show( shareHistoryId ); + }, 403, 'History is not accessible by user', 'show failed with error' ); + }); + + + // should NOT work: share using malformed id + thenSwitchUser( email, password ); + thenShareWithUser( "should NOT work: share using malformed id", '1234xyz', function(){ + this.test.assertExists( '.errormessage', 'found error message' ); + this.test.assertSelectorHasText( '.errormessage', 'is not a valid Galaxy user', 'wording is good' ); + }); + //spaceghost.then( function(){ + // // test user share using email + // fromUserSharePage( function(){ + // this.test.comment( 'should NOT work: share using malformed id' ); + // this.fill( '#share', { + // email : '1234xyz' + // }); + // this.click( shareSubmit ); + // }); + // spaceghost.then( function(){ + // this.jumpToMain( function(){ + // this.test.assertExists( '.errormessage', 'found error message' ); + // this.test.assertSelectorHasText( '.errormessage', 'is not a valid Galaxy user', 'wording is good' ); + // }); + // }); + //}); + + // should NOT work: share using current user id + spaceghost.then( function(){ + var currUserId = spaceghost.api.users.index()[0].id; + thenShareWithUser( "should NOT work: share using current user id", currUserId, function(){ + this.test.assertExists( '.errormessage', 'found error message' ); + this.test.assertSelectorHasText( '.errormessage', + 'You cannot send histories to yourself', 'wording is good' ); + }); + }); + //// should NOT work: share using current user id + //spaceghost.then( function(){ + // var currUserId = spaceghost.api.users.index()[0].id; + // // test user share using email + // fromUserSharePage( function(){ + // this.test.comment( 'should NOT work: share using current user id' ); + // this.debug( 'currUserId: ' + currUserId ); + // this.fill( 'form#share', { + // email : currUserId + // }); + // this.click( shareSubmit ); + // }); + // spaceghost.then( function(){ + // this.jumpToMain( function(){ + // this.test.assertExists( '.errormessage', 'found error message' ); + // this.test.assertSelectorHasText( '.errormessage', + // 'You cannot send histories to yourself', 'wording is good' ); + // }); + // }); + //}); + + spaceghost.then( function(){ + thenShareWithUser( "should work: share using id", shareUserId, function(){ + this.test.assertExists( firstUserShareButton, 'found user share button' ); + this.test.assertSelectorHasText( firstUserShareButton, email2, 'share button text is email2' ); + }); + }); + //// should work: share using id + //spaceghost.then( function(){ + // // test user share using email + // fromUserSharePage( function(){ + // this.test.comment( 'should work: share using id' ); + // this.fill( '#share', { + // email : shareUserId + // }); + // this.click( shareSubmit ); + // }); + // spaceghost.then( function(){ + // this.jumpToMain( function(){ + // this.test.assertExists( firstUserShareButton, 'found user share button' ); + // this.test.assertSelectorHasText( firstUserShareButton, email2, 'share button text is email2' ); + // }); + // }); + //}); + + // user 2 can now access the history + thenSwitchUser( email2, password2 ).openHomePage( function(){ + this.test.comment( 'user 2 can now access the history' ); + this.test.assert( !!this.api.histories.show( shareHistoryId ).id ); + }); + + /* + */ + // =================================================================== + spaceghost.run( function(){ test.done(); }); +}); This diff is so big that we needed to truncate the remainder. https://bitbucket.org/galaxy/galaxy-central/commits/a1a8011ec8d1/ Changeset: a1a8011ec8d1 User: carlfeberhard Date: 2015-02-02 14:17:51+00:00 Summary: Merge Affected #: 3 files diff -r e7560410c0f235df210d29545a51327a1af5f7ef -r a1a8011ec8d14bdb28df805e0493ac3a226698b3 lib/galaxy/config.py --- a/lib/galaxy/config.py +++ b/lib/galaxy/config.py @@ -318,6 +318,9 @@ # Crummy, but PasteScript does not give you a way to determine this if arg.lower().startswith('--server-name='): self.server_name = arg.split('=', 1)[-1] + # Allow explicit override of server name in confg params + if "server_name" in kwargs: + self.server_name = kwargs.get("server_name") # Store all configured server names self.server_names = [] for section in global_conf_parser.sections(): diff -r e7560410c0f235df210d29545a51327a1af5f7ef -r a1a8011ec8d14bdb28df805e0493ac3a226698b3 lib/galaxy/main.py --- /dev/null +++ b/lib/galaxy/main.py @@ -0,0 +1,245 @@ +""" Entry point for starting Galaxy without starting as part of a web server. + +Example Usage: Start a job/workflow handler without a web server and with +a given name using. + +python lib/galaxy/main.py --server-name handler0 + +Start as a daemon with (requires daemonized - install with 'pip install daemonize'): + +python lib/galaxy/main.py -d --daemon-log-file=handler0-daemon.log --pid-file handler0.pid --server-name handler0 + +In daemon mode logging of Galaxy (as opposed to this script) is configured via +a loggers section in Galaxy's ini file - this can be overridden with sensible +defaults logging to a single file with the following: + +python lib/galaxy/main.py -d --server-name handler0 --daemon-log-file=handler0-daemon.log --pid-file handler0.pid --log-file handler0.log + +""" +import logging +from logging.config import fileConfig + +import functools +import os +import time +import sys + +try: + import ConfigParser as configparser +except ImportError: + import configparser + +try: + from daemonize import Daemonize +except ImportError: + Daemonize = None + +# Vaguely Python 2.6 compatibile ArgumentParser import +try: + from argparser import ArgumentParser +except ImportError: + from optparse import OptionParser + + class ArgumentParser(OptionParser): + + def __init__(self, **kwargs): + self.delegate = OptionParser(**kwargs) + + def add_argument(self, *args, **kwargs): + if "required" in kwargs: + del kwargs["required"] + return self.delegate.add_option(*args, **kwargs) + + def parse_args(self, args=None): + (options, args) = self.delegate.parse_args(args) + return options + +REQUIRES_DAEMONIZE_MESSAGE = "Attempted to use Galaxy in daemon mode, but daemonize is unavailable." + +log = logging.getLogger(__name__) + +GALAXY_ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) +GALAXY_LIB_DIR = os.path.join(GALAXY_ROOT_DIR, "lib") +DEFAULT_INI_APP = "main" +DEFAULT_INIS = ["config/galaxy.ini", "universe_wsgi.ini", "config/galaxy.ini.sample"] + +DEFAULT_PID = "galaxy.pid" +DEFAULT_VERBOSE = True +DESCRIPTION = "Daemonized entry point for Galaxy." + + +def load_galaxy_app( + config_builder, + config_env=False, + log=None, + **kwds +): + # Allow specification of log so daemon can reuse properly configured one. + if log is None: + log = logging.getLogger(__name__) + + # If called in daemon mode, set the ROOT directory and ensure Galaxy is on + # sys.path. + if config_env: + try: + os.chdir(GALAXY_ROOT_DIR) + except Exception: + log.exception("Failed to chdir") + raise + try: + sys.path.append(GALAXY_LIB_DIR) + except Exception: + log.exception("Failed to add Galaxy to sys.path") + raise + + config_builder.setup_logging() + from galaxy.util.properties import load_app_properties + kwds = config_builder.app_kwds() + kwds = load_app_properties(**kwds) + from galaxy.app import UniverseApplication + app = UniverseApplication( + global_conf={"__file__": config_builder.ini_path}, + **kwds + ) + return app + + +def app_loop(args, log): + try: + config_builder = GalaxyConfigBuilder(args) + galaxy_app = load_galaxy_app( + config_builder, + config_env=True, + log=log, + ) + except BaseException: + log.exception("Failed to initialize Galaxy application") + raise + sleep = True + while sleep: + try: + time.sleep(5) + except KeyboardInterrupt: + sleep = False + except SystemExit: + sleep = False + except Exception: + pass + try: + galaxy_app.shutdown() + except Exception: + log.exception("Failed to shutdown Galaxy application") + raise + + +def absolute_config_path(path, galaxy_root): + if path and not os.path.isabs(path): + path = os.path.join(galaxy_root, path) + return path + + +def find_ini(supplied_ini, galaxy_root): + if supplied_ini: + return supplied_ini + + # If not explicitly supplied an ini, check server.ini and then + # just restort to sample if that has not been configured. + for guess in DEFAULT_INIS: + ini_path = os.path.join(galaxy_root, guess) + if os.path.exists(ini_path): + return ini_path + + return guess + + +class GalaxyConfigBuilder(object): + """ Generate paste-like configuration from supplied command-line arguments. + """ + + def __init__(self, args=None, **kwds): + ini_path = kwds.get("ini_path", None) or (args and args.ini_path) + # If given app_conf_path - use that - else we need to ensure we have an + # ini path. + if not ini_path: + galaxy_root = kwds.get("galaxy_root", GALAXY_ROOT_DIR) + ini_path = find_ini(ini_path, galaxy_root) + ini_path = absolute_config_path(ini_path, galaxy_root=galaxy_root) + self.ini_path = ini_path + self.app_name = kwds.get("app") or (args and args.app) or DEFAULT_INI_APP + self.log_file = (args and args.log_file) + + @classmethod + def populate_options(cls, arg_parser): + arg_parser.add_argument("-c", "--ini-path", default=None, help="Galaxy ini config file (defaults to config/galaxy.ini)") + arg_parser.add_argument("--app", default=DEFAULT_INI_APP, help="app section in ini file (defaults to main)") + arg_parser.add_argument("-d", "--daemonize", default=False, help="Daemonzie process", action="store_true") + arg_parser.add_argument("--daemon-log-file", default=None, help="log file for daemon script ") + arg_parser.add_argument("--log-file", default=None, help="Galaxy log file (overrides log configuration in ini_path if set)") + arg_parser.add_argument("--pid-file", default=DEFAULT_PID, help="pid file (default is %s)" % DEFAULT_PID) + arg_parser.add_argument("--server-name", default=None, help="set a galaxy server name") + + def app_kwds(self): + config = dict( + ini_file=self.ini_path, + ini_section="app:%s" % self.app_name, + ) + return config + + def setup_logging(self): + # Galaxy will attempt to setup logging if loggers is not present in + # ini config file - this handles that loggers block however if present + # (the way paste normally would) + if not self.ini_path: + return + raw_config = configparser.ConfigParser() + raw_config.read([self.ini_path]) + if raw_config.has_section('loggers'): + config_file = os.path.abspath(self.ini_path) + fileConfig( + config_file, + dict(__file__=config_file, here=os.path.dirname(config_file)) + ) + + +def main(): + if Daemonize is None: + raise ImportError(REQUIRES_DAEMONIZE_MESSAGE) + + arg_parser = ArgumentParser(description=DESCRIPTION) + GalaxyConfigBuilder.populate_options(arg_parser) + args = arg_parser.parse_args() + if args.log_file: + os.environ["GALAXY_CONFIG_LOG_DESTINATION"] = os.path.abspath(args.log_file) + if args.server_name: + os.environ["GALAXY_CONFIG_SERVER_NAME"] = args.server_name + pid_file = args.pid_file + + log.setLevel(logging.DEBUG) + log.propagate = False + if args.daemonize: + keep_fds = [] + if args.daemon_log_file: + fh = logging.FileHandler(args.daemon_log_file, "w") + fh.setLevel(logging.DEBUG) + log.addHandler(fh) + keep_fds.append(fh.stream.fileno()) + else: + fh = logging.StreamHandler(sys.stderr) + fh.setLevel(logging.DEBUG) + log.addHandler(fh) + + daemon = Daemonize( + app="galaxy", + pid=pid_file, + action=functools.partial(app_loop, args, log), + verbose=DEFAULT_VERBOSE, + logger=log, + keep_fds=keep_fds, + ) + daemon.start() + else: + app_loop(args, log) + + +if __name__ == "__main__": + main() diff -r e7560410c0f235df210d29545a51327a1af5f7ef -r a1a8011ec8d14bdb28df805e0493ac3a226698b3 rolling_restart.sh --- a/rolling_restart.sh +++ b/rolling_restart.sh @@ -1,21 +1,7 @@ #!/bin/sh + cd `dirname $0` -check_if_not_started(){ - # Search for all pids in the logs and tail for the last one - latest_pid=`egrep '^Starting server in PID [0-9]+\.$' $1 -o | sed 's/Starting server in PID //g;s/\.$//g' | tail -n 1` - # Grab the current pid from the file we were given - current_pid_in_file=$(cat $2); - # If they're equivalent, then the current pid file agrees with our logs - # and we've succesfully started - if [ $latest_pid -eq $current_pid_in_file ]; - then - echo 0; - else - echo 1; - fi -} - # If there is a .venv/ directory, assume it contains a virtualenv that we # should run this instance in. if [ -d .venv ]; @@ -23,6 +9,15 @@ . .venv/bin/activate fi +python ./scripts/check_python.py +[ $? -ne 0 ] && exit 1 + +./scripts/common_startup.sh + +if [ -n "$GALAXY_UNIVERSE_CONFIG_DIR" ]; then + python ./scripts/build_universe_config.py "$GALAXY_UNIVERSE_CONFIG_DIR" +fi + if [ -z "$GALAXY_CONFIG_FILE" ]; then if [ -f universe_wsgi.ini ]; then GALAXY_CONFIG_FILE=universe_wsgi.ini @@ -35,32 +30,32 @@ fi servers=`sed -n 's/^\[server:\(.*\)\]/\1/ p' $GALAXY_CONFIG_FILE | xargs echo` -for server in $servers; -do - # If there's a pid - if [ -e $server.pid ] - then - # Then kill it - echo "Killing $server" - pid=`cat $server.pid` - kill $pid - else - # Otherwise just continue - echo "$server not running" - fi - # Start the server (and background) (should this be nohup'd?) - python ./scripts/paster.py serve $GALAXY_CONFIG_FILE --server-name=$server --pid-file=$server.pid --log-file=$server.log --daemon $@ - # Wait for the server to start - sleep 1 - # Grab the new pid - pid=`cat $server.pid` - result=1 - # Wait for the latest pid in the file to be the pid we've grabbed - while [ $result -eq 1 ] - do - result=$(check_if_not_started $server.log $server.pid) - printf "." - sleep 1 - done - echo +for server in $servers; do + # If there's a pid + if [ -e $server.pid ]; then + # Then kill it + echo "Killing $server" + pid=`cat $server.pid` + kill $pid + else + # Otherwise just continue + echo "$server not running" + fi + # Start the server (and background) (should this be nohup'd?) + python ./scripts/paster.py serve $GALAXY_CONFIG_FILE --server-name=$server --pid-file=$server.pid --log-file=$server.log --daemon $@ + while true; do + sleep 1 + printf "." + # Grab the current pid from the pid file + if ! current_pid_in_file=$(cat $server.pid); then + echo "A Galaxy process died, interrupting" >&2 + exit 1 + fi + # Search for all pids in the logs and tail for the last one + latest_pid=`egrep '^Starting server in PID [0-9]+\.$' $server.log -o | sed 's/Starting server in PID //g;s/\.$//g' | tail -n 1` + # If they're equivalent, then the current pid file agrees with our logs + # and we've succesfully started + [ -n "$latest_pid" ] && [ $latest_pid -eq $current_pid_in_file ] && break + done + echo done https://bitbucket.org/galaxy/galaxy-central/commits/0d92ab68b8b3/ Changeset: 0d92ab68b8b3 User: carlfeberhard Date: 2015-02-02 14:19:13+00:00 Summary: HDAs, Histories: remove set_from_dict; minor fixes Affected #: 3 files diff -r a1a8011ec8d14bdb28df805e0493ac3a226698b3 -r 0d92ab68b8b3764e98d338c7d594b8e77aaf5b99 lib/galaxy/managers/datasets.py --- a/lib/galaxy/managers/datasets.py +++ b/lib/galaxy/managers/datasets.py @@ -232,6 +232,7 @@ deletable.PurgableDeserializerMixin.add_deserializers( self ) +# ============================================================================= AKA DatasetInstanceManager class DatasetAssociationManager( base.ModelManager, secured.AccessibleManagerMixin, deletable.PurgableManagerMixin ): """ DatasetAssociation/DatasetInstances are intended to be working diff -r a1a8011ec8d14bdb28df805e0493ac3a226698b3 -r 0d92ab68b8b3764e98d338c7d594b8e77aaf5b99 lib/galaxy/managers/histories.py --- a/lib/galaxy/managers/histories.py +++ b/lib/galaxy/managers/histories.py @@ -49,8 +49,7 @@ """ # handle default and/or anonymous user (which still may not have a history yet) if self.user_manager.is_anonymous( user ): - #TODO: trans - current_history = trans.get_history() + current_history = self.get_current( trans ) return [ current_history ] if current_history else [] return super( HistoryManager, self ).by_user( trans, user, **kwargs ) @@ -60,8 +59,7 @@ True if the current user is the owner of the given history. """ # anon users are only allowed to view their current history - #TODO: trans - if self.user_manager.is_anonymous( user ) and history == trans.get_history(): + if self.user_manager.is_anonymous( user ) and history == self.get_current( trans ): return True return super( HistoryManager, self ).is_owner( trans, history, user ) @@ -69,10 +67,14 @@ def most_recent( self, trans, user, filters=None, **kwargs ): """ Return the most recently update history for the user. + + If user is anonymous, return the current history. If the user is anonymous + and the current history is deleted, return None. """ #TODO: trans if not user: - return None if trans.history.deleted else trans.history + current_history = self.get_current( trans ) + return None if ( not current_history or current_history.deleted ) else current_history desc_update_time = self.model_class.table.c.update_time filters = self._munge_filters( filters, self.model_class.user_id == user.id ) #TODO: normalize this return value diff -r a1a8011ec8d14bdb28df805e0493ac3a226698b3 -r 0d92ab68b8b3764e98d338c7d594b8e77aaf5b99 lib/galaxy/model/__init__.py --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -1074,28 +1074,6 @@ return rval - def set_from_dict( self, new_data ): - #AKA: set_api_value - """ - Set object attributes to the values in dictionary new_data limiting - to only those keys in dict_element_visible_keys. - - Returns a dictionary of the keys, values that have been changed. - """ - # precondition: keys are proper, values are parsed and validated - changed = {} - # unknown keys are ignored here - for key in [ k for k in new_data.keys() if k in self.dict_element_visible_keys ]: - new_val = new_data[ key ] - old_val = self.__getattribute__( key ) - if new_val == old_val: - continue - - self.__setattr__( key, new_val ) - changed[ key ] = new_val - - return changed - @property def latest_export( self ): exports = self.exports @@ -2120,36 +2098,6 @@ rval['metadata_' + name] = val return rval - def set_from_dict( self, new_data ): - #AKA: set_api_value - """ - Set object attributes to the values in dictionary new_data limiting - to only the following keys: name, deleted, visible, genome_build, - info, and blurb. - - Returns a dictionary of the keys, values that have been changed. - """ - # precondition: keys are proper, values are parsed and validated - #NOTE!: does not handle metadata - editable_keys = ( 'name', 'deleted', 'visible', 'dbkey', 'info', 'blurb' ) - - changed = {} - # unknown keys are ignored here - for key in [ k for k in new_data.keys() if k in editable_keys ]: - new_val = new_data[ key ] - old_val = self.__getattribute__( key ) - if new_val == old_val: - continue - - # special cases here - if key == 'deleted' and new_val is False and self.purged: - raise Exception( 'Cannot undelete a purged dataset' ) - - self.__setattr__( key, new_val ) - changed[ key ] = new_val - - return changed - @property def history_content_type( self ): return "dataset" 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.
participants (1)
-
commits-noreply@bitbucket.org