merging from central

This commit is contained in:
Greg Von Kuster
2008-10-28 11:28:15 -04:00
103 changed files with 6480 additions and 757 deletions
+6 -1
View File
@@ -17,4 +17,9 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Some icons found in Galaxy are from the Silk Icons set, available under
the Creative Commons Attribution 2.5 License, from:
http://www.famfamfam.com/lab/icons/silk/
+2 -2
View File
@@ -40,7 +40,7 @@ PasteDeploy = 1.3.1
PasteScript = 1.3.6
Routes = 1.6.3
simplejson = 1.5
SQLAlchemy = 0.3.11
SQLAlchemy = 0.4.7p1
Tempita = 0.1
twill = 0.9
WebError = 0.8a
@@ -86,7 +86,7 @@ PasteDeploy = http://cheeseshop.python.org/packages/source/P/PasteDeploy/PasteDe
PasteScript = http://cheeseshop.python.org/packages/source/P/PasteScript/PasteScript-1.3.6.tar.gz
Routes = http://pypi.python.org/packages/source/R/Routes/Routes-1.6.3.tar.gz
simplejson = http://cheeseshop.python.org/packages/source/s/simplejson/simplejson-1.5.tar.gz
SQLAlchemy = http://pypi.python.org/packages/source/S/SQLAlchemy/SQLAlchemy-0.3.11.tar.gz
SQLAlchemy = http://pypi.python.org/packages/source/S/SQLAlchemy/SQLAlchemy-0.4.7p1.tar.gz
Tempita = http://pypi.python.org/packages/source/T/Tempita/Tempita-0.1.tar.gz
twill = http://darcs.idyll.org/~t/projects/twill-0.9.tar.gz
WebError = http://pypi.python.org/packages/source/W/WebError/WebError-0.8a.tar.gz
+4 -1
View File
@@ -4,6 +4,7 @@ from galaxy import config, jobs, util, tools, web
import galaxy.model
import galaxy.model.mapping
import galaxy.datatypes.registry
import galaxy.security
class UniverseApplication( object ):
"""Encapsulates the state of a Universe application"""
@@ -20,7 +21,7 @@ class UniverseApplication( object ):
if self.config.database_connection:
db_url = self.config.database_connection
else:
db_url = "sqlite://%s?isolation_level=IMMEDIATE" % self.config.database
db_url = "sqlite:///%s?isolation_level=IMMEDIATE" % self.config.database
# Setup the database engine and ORM
self.model = galaxy.model.mapping.init( self.config.file_path,
db_url,
@@ -30,6 +31,8 @@ class UniverseApplication( object ):
self.toolbox = tools.ToolBox( self.config.tool_config, self.config.tool_path, self )
#Load datatype converters
self.datatypes_registry.load_datatype_converters( self.toolbox )
#Load security policy
self.security_agent = self.model.security_agent
# Start the job queue
job_dispatcher = jobs.DefaultJobDispatcher( self )
self.job_queue = jobs.JobQueue( self, job_dispatcher )
+4 -1
View File
@@ -44,7 +44,7 @@ class Configuration( object ):
self.job_scheduler_policy = kwargs.get("job_scheduler_policy", "FIFO")
self.job_queue_cleanup_interval = int( kwargs.get("job_queue_cleanup_interval", "5") )
self.job_working_directory = resolve_path( kwargs.get( "job_working_directory", "database/job_working_directory" ), self.root )
self.admin_pass = kwargs.get('admin_pass',"galaxy")
self.admin_users = kwargs.get( "admin_users", "" )
self.sendmail_path = kwargs.get('sendmail_path',"/usr/sbin/sendmail")
self.mailing_join_addr = kwargs.get('mailing_join_addr',"galaxy-user-join@bx.psu.edu")
self.error_email_to = kwargs.get( 'error_email_to', None )
@@ -64,6 +64,9 @@ class Configuration( object ):
self.bugs_email = kwargs.get( 'bugs_email', None )
self.blog_url = kwargs.get( 'blog_url', None )
self.screencasts_url = kwargs.get( 'screencasts_url', None )
self.library_import_dir = kwargs.get( 'library_import_dir', None )
if self.library_import_dir is not None and not os.path.exists( self.library_import_dir ):
raise ConfigurationError( "library_import_dir specified in config (%s) does not exist" % self.library_import_dir )
# Parse global_conf and save the parser
global_conf = kwargs.get( 'global_conf', None )
global_conf_parser = ConfigParser.ConfigParser()
+14 -6
View File
@@ -45,6 +45,9 @@ class Data( object ):
"""Stores the set of display applications, and viewing methods, supported by this datatype """
supported_display_apps = {}
"""If False, the peek is regenerated whenever a dataset of this type is copied"""
copy_safe_peek = True
def __init__(self, **kwd):
"""Initialize the datatype"""
object.__init__(self, **kwd)
@@ -180,12 +183,17 @@ class Data( object ):
return "This display type (%s) is not implemented for this datatype (%s)." % ( type, dataset.ext)
def get_display_links(self, dataset, type, app, base_url, **kwd):
"""Returns a list of tuples of (name, link) for a particular display type """
try:
if type in self.get_display_types():
return getattr (self, self.supported_display_apps[type]['links_function']) (dataset, type, app, base_url, **kwd)
except:
log.exception('Function %s is referred to in datatype %s for generating links for type %s, but is not accessible' % (self.supported_display_apps[type]['links_function'], self.__class__.__name__, type) )
"""
Returns a list of tuples of (name, link) for a particular display type
as long as the dataset is not associated with a role restricting its access.
We determine this by sending None as the user to the allow_action method.
"""
if app.security_agent.allow_action( None, dataset.permitted_actions.DATASET_ACCESS, dataset=dataset ):
try:
if type in self.get_display_types():
return getattr (self, self.supported_display_apps[type]['links_function']) (dataset, type, app, base_url, **kwd)
except:
log.exception('Function %s is referred to in datatype %s for generating links for type %s, but is not accessible' % (self.supported_display_apps[type]['links_function'], self.__class__.__name__, type) )
return []
def get_converter_types(self, original_dataset, datatypes_registry):
+30 -22
View File
@@ -5,7 +5,7 @@ Image classes
import data
import logging
from galaxy.datatypes.sniff import *
from urllib import urlencode
from urllib import urlencode, quote_plus
import zipfile
log = logging.getLogger(__name__)
@@ -103,18 +103,22 @@ def create_applet_tag_peek( class_name, archive, params ):
class Gmaj( data.Data ):
"""Class describing a GMAJ Applet"""
file_ext = "gmaj.zip"
copy_safe_peek = False
def set_peek( self, dataset ):
params = {
"bundle":"display?id=%s&tofile=yes&toext=.zip" % dataset.id,
"buttonlabel": "Launch GMAJ",
"nobutton": "false",
"urlpause" :"100",
"debug": "false",
"posturl": "history_add_to?%s" % urlencode( { 'history_id': dataset.history_id, 'ext': 'maf', 'name': 'GMAJ Output on data %s' % dataset.hid, 'info': 'Added by GMAJ', 'dbkey': dataset.dbkey } )
}
class_name = "edu.psu.bx.gmaj.MajApplet.class"
archive = "/static/gmaj/gmaj.jar"
dataset.peek = create_applet_tag_peek( class_name, archive, params )
if hasattr( dataset, 'history_id' ):
params = {
"bundle":"display?id=%s&tofile=yes&toext=.zip" % dataset.id,
"buttonlabel": "Launch GMAJ",
"nobutton": "false",
"urlpause" :"100",
"debug": "false",
"posturl": quote_plus( "history_add_to?%s" % "&".join( [ "%s=%s" % ( key, value ) for key, value in { 'history_id': dataset.history_id, 'ext': 'maf', 'name': 'GMAJ Output on data %s' % dataset.hid, 'info': 'Added by GMAJ', 'dbkey': dataset.dbkey, 'copy_access_from': dataset.id }.items() ] ) )
}
class_name = "edu.psu.bx.gmaj.MajApplet.class"
archive = "/static/gmaj/gmaj.jar"
dataset.peek = create_applet_tag_peek( class_name, archive, params )
else:
dataset.peek = "After you add this item to your history, you will be able to launch the GMAJ applet."
dataset.blurb = 'GMAJ Multiple Alignment Viewer'
def display_peek(self, dataset):
try:
@@ -175,17 +179,21 @@ class Html( data.Text ):
class Laj( data.Text ):
"""Class describing a LAJ Applet"""
file_ext = "laj"
copy_safe_peek = False
def set_peek( self, dataset ):
params = {
"alignfile1": "display?id=%s" % dataset.id,
"buttonlabel": "Launch LAJ",
"title": "LAJ in Galaxy",
"posturl": "history_add_to?%s" % urlencode( { 'history_id': dataset.history_id, 'ext': 'lav', 'name': 'LAJ Output', 'info': 'Added by LAJ', 'dbkey': dataset.dbkey } ),
"noseq": "true"
}
class_name = "edu.psu.cse.bio.laj.LajApplet.class"
archive = "/static/laj/laj.jar"
dataset.peek = create_applet_tag_peek( class_name, archive, params )
if hasattr( dataset, 'history_id' ):
params = {
"alignfile1": "display?id=%s" % dataset.id,
"buttonlabel": "Launch LAJ",
"title": "LAJ in Galaxy",
"posturl": quote_plus( "history_add_to?%s" % "&".join( [ "%s=%s" % ( key, value ) for key, value in { 'history_id': dataset.history_id, 'ext': 'lav', 'name': 'LAJ Output', 'info': 'Added by LAJ', 'dbkey': dataset.dbkey, 'copy_access_from': dataset.id }.items() ] ) ),
"noseq": "true"
}
class_name = "edu.psu.cse.bio.laj.LajApplet.class"
archive = "/static/laj/laj.jar"
dataset.peek = create_applet_tag_peek( class_name, archive, params )
else:
dataset.peek = "After you add this item to your history, you will be able to launch the LAJ applet."
dataset.blurb = 'LAJ Multiple Alignment Viewer'
def display_peek(self, dataset):
try:
+3 -4
View File
@@ -97,11 +97,10 @@ class JobQueue( object ):
model = self.app.model
# Jobs in the NEW state won't be requeued unless we're tracking in the database
if not self.track_jobs_in_database:
for job in model.Job.select( model.Job.c.state == model.Job.states.NEW ):
for job in model.Job.filter( model.Job.c.state==model.Job.states.NEW ).all():
log.debug( "no runner: %s is still in new state, adding to the jobs queue" %job.id )
self.queue.put( ( job.id, job.tool_id ) )
for job in model.Job.select( (model.Job.c.state == model.Job.states.RUNNING)
| (model.Job.c.state == model.Job.states.QUEUED) ):
for job in model.Job.filter( (model.Job.c.state == model.Job.states.RUNNING) | (model.Job.c.state == model.Job.states.QUEUED) ).all():
if job.job_runner_name is not None:
# why are we passing the queue to the wrapper?
job_wrapper = JobWrapper( job.id, self.app.toolbox.tools_by_id[ job.tool_id ], self )
@@ -136,7 +135,7 @@ class JobQueue( object ):
new_jobs = []
if self.track_jobs_in_database:
model = self.app.model
for j in model.Job.select( model.Job.c.state == model.Job.states.NEW ):
for j in model.Job.filter( model.Job.c.state==model.Job.states.NEW ).all():
job = JobWrapper( j.id, self.app.toolbox.tools_by_id[ j.tool_id ], self )
new_jobs.append( job )
else:
+471 -193
View File
@@ -13,6 +13,7 @@ from galaxy import util
import tempfile
import galaxy.datatypes.registry
from galaxy.datatypes.metadata import MetadataCollection
from galaxy.security import RBACAgent
import logging
log = logging.getLogger( __name__ )
@@ -33,13 +34,21 @@ class User( object ):
self.external = False
# Relationships
self.histories = []
def set_password_cleartext( self, cleartext ):
"""Set 'self.password' to the digest of 'cleartext'."""
self.password = sha.new( cleartext ).hexdigest()
def check_password( self, cleartext ):
"""Check if 'cleartext' matches 'self.password' when hashed."""
return self.password == sha.new( cleartext ).hexdigest()
def all_roles( self ):
roles = [ ura.role for ura in self.roles ]
for group in [ uga.group for uga in self.groups ]:
for role in [ gra.role for gra in group.roles ]:
if role not in roles:
roles.append( role )
return roles
class Job( object ):
"""
A job represents a request to run a tool given input datasets, tool
@@ -101,194 +110,15 @@ class JobToOutputDatasetAssociation( object ):
self.name = name
self.dataset = dataset
class HistoryDatasetAssociation( object ):
def __init__( self, id=None, hid=None, name=None, info=None, blurb=None, peek=None, extension=None,
dbkey=None, metadata=None, history=None, dataset=None, deleted=False, designation=None,
parent_id=None, copied_from_history_dataset_association = None, validation_errors=None, visible=True, create_dataset = False ):
self.name = name or "Unnamed dataset"
self.id = id
self.hid = hid
self.info = info
self.blurb = blurb
self.peek = peek
self.extension = extension
self.designation = designation
self.metadata = metadata or dict()
if dbkey: #dbkey is stored in metadata, only set if non-zero, or else we could clobber one supplied by input 'metadata'
self.dbkey = dbkey
self.deleted = deleted
self.visible = visible
# Relationships
self.history = history
if not dataset and create_dataset:
dataset = Dataset()
dataset.flush()
self.dataset = dataset
self.parent_id = parent_id
self.validation_errors = validation_errors
self.copied_from_history_dataset_association = copied_from_history_dataset_association
@property
def ext( self ):
return self.extension
@property
def states( self ):
return self.dataset.states
def get_dataset_state( self ):
return self.dataset.state
def set_dataset_state ( self, state ):
self.dataset.state = state
self.dataset.flush() #flush here, because hda.flush() won't flush the Dataset object
state = property( get_dataset_state, set_dataset_state )
def get_file_name( self ):
return self.dataset.get_file_name()
def set_file_name (self, filename):
return self.dataset.set_file_name( filename )
file_name = property( get_file_name, set_file_name )
@property
def extra_files_path( self ):
return self.dataset.extra_files_path
@property
def datatype( self ):
return datatypes_registry.get_datatype_by_extension( self.extension )
class Group( object ):
permitted_actions = galaxy.security.get_permitted_actions( 'GROUP' )
def __init__( self, name = None ):
self.name = name
def get_metadata( self ):
if not hasattr( self, '_metadata_collection' ):
self._metadata_collection = MetadataCollection( self )
return self._metadata_collection
def set_metadata( self, bunch ):
# Needs to accept a MetadataCollection, a bunch, or a dict
self._metadata = self.metadata.make_dict_copy( bunch )
metadata = property( get_metadata, set_metadata )
"""
This provide backwards compatibility with using the old dbkey
field in the database. That field now maps to "old_dbkey" (see mapping.py).
"""
def get_dbkey( self ):
dbkey = self.metadata.dbkey
if not isinstance(dbkey, list): dbkey = [dbkey]
#if dbkey in [["?"], [None], []]: dbkey = [self.old_dbkey]
if dbkey in [[None], []]: return "?"
return dbkey[0]
def set_dbkey( self, value ):
if "dbkey" in self.datatype.metadata_spec:
if not isinstance(value, list):
self.metadata.dbkey = [value]
else:
self.metadata.dbkey = value
#if isinstance(value, list):
# self.old_dbkey = value[0]
#else:
# self.old_dbkey = value
dbkey = property( get_dbkey, set_dbkey )
def change_datatype( self, new_ext ):
self.clear_associated_files()
datatypes_registry.change_datatype( self, new_ext )
def get_size( self ):
"""Returns the size of the data on disk"""
return self.dataset.get_size()
def set_size( self ):
"""Returns the size of the data on disk"""
return self.dataset.set_size()
def has_data( self ):
"""Detects whether there is any data"""
return self.dataset.has_data()
def get_raw_data( self ):
"""Returns the full data. To stream it open the file_name and read/write as needed"""
return self.datatype.get_raw_data( self )
def write_from_stream( self, stream ):
"""Writes data from a stream"""
self.datatype.write_from_stream(self, stream)
def set_raw_data( self, data ):
"""Saves the data on the disc"""
self.datatype.set_raw_data(self, data)
def get_mime( self ):
"""Returns the mime type of the data"""
return datatypes_registry.get_mimetype_by_extension( self.extension.lower() )
def set_peek( self ):
return self.datatype.set_peek( self )
def init_meta( self, copy_from=None ):
return self.datatype.init_meta( self, copy_from=copy_from )
def set_meta( self, **kwd ):
self.clear_associated_files( metadata_safe = True )
return self.datatype.set_meta( self, **kwd )
def set_readonly_meta( self, **kwd ):
return self.datatype.set_readonly_meta( self, **kwd )
def missing_meta( self, **kwd ):
return self.datatype.missing_meta( self, **kwd )
def as_display_type( self, type, **kwd ):
return self.datatype.as_display_type( self, type, **kwd )
def display_peek( self ):
return self.datatype.display_peek( self )
def display_name( self ):
return self.datatype.display_name( self )
def display_info( self ):
return self.datatype.display_info( self )
def get_converted_files_by_type( self, file_type ):
valid = []
for assoc in self.implicitly_converted_datasets:
if not assoc.deleted and assoc.type == file_type:
valid.append( assoc.dataset )
return valid
def clear_associated_files( self, metadata_safe = False, purge = False ):
#metadata_safe = True means to only clear when assoc.metadata_safe == False
for assoc in self.implicitly_converted_datasets:
if not metadata_safe or not assoc.metadata_safe:
assoc.clear( purge = purge )
def get_child_by_designation(self, designation):
for child in self.children:
if child.designation == designation:
return child
return None
def get_converter_types(self):
return self.datatype.get_converter_types( self, datatypes_registry)
def find_conversion_destination( self, accepted_formats, **kwd ):
"""Returns ( target_ext, exisiting converted dataset )"""
return self.datatype.find_conversion_destination( self, accepted_formats, datatypes_registry, **kwd )
def copy( self, copy_children = False, parent_id = None ):
des = HistoryDatasetAssociation( hid=self.hid, name=self.name, info=self.info, blurb=self.blurb, peek=self.peek, extension=self.extension, dbkey=self.dbkey, dataset = self.dataset, visible=self.visible, deleted=self.deleted, parent_id=parent_id, copied_from_history_dataset_association = self )
des.flush()
des.metadata = self.metadata #need to set after flushed, as MetadataFiles require dataset.id
if copy_children:
for child in self.children:
child_copy = child.copy( copy_children = copy_children, parent_id = des.id )
des.set_peek() #in some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs
des.flush()
return des
def add_validation_error( self, validation_error ):
self.validation_errors.append( validation_error )
def extend_validation_errors( self, validation_errors ):
self.validation_errors.extend(validation_errors)
def mark_deleted( self, include_children=True ):
self.deleted = True
if include_children:
for child in self.children:
child.mark_deleted()
def mark_undeleted( self, include_children=True ):
self.deleted = False
if include_children:
for child in self.children:
child.mark_undeleted()
def undeletable( self ):
if self.purged:
return False
return True
class UserGroupAssociation( object ):
def __init__( self, user, group ):
self.user = user
self.group = group
class History( object ):
def __init__( self, id=None, name=None, user=None ):
@@ -362,6 +192,49 @@ class History( object ):
# self.history = history
# self.datasets = []
class UserRoleAssociation( object ):
def __init__( self, user, role ):
self.user = user
self.role = role
class GroupRoleAssociation( object ):
def __init__( self, group, role ):
self.group = group
self.role = role
class Role( object ):
private_id = None
types = Bunch(
PRIVATE = 'private',
SYSTEM = 'system',
USER = 'user',
ADMIN = 'admin',
SHARING = 'sharing'
)
def __init__( self, name="", description="", type="system", deleted=False ):
self.name = name
self.description = description
self.type = type
self.deleted = deleted
class ActionDatasetRoleAssociation( object ):
def __init__( self, action, dataset, role ):
self.action = action
self.dataset = dataset
self.role = role
class DefaultUserPermissions( object ):
def __init__( self, user, action, role ):
self.user = user
self.action = action
self.role = role
class DefaultHistoryPermissions( object ):
def __init__( self, history, action, role ):
self.history = history
self.action = action
self.role = role
class Dataset( object ):
states = Bunch( NEW = 'new',
QUEUED = 'queued',
@@ -370,6 +243,7 @@ class Dataset( object ):
EMPTY = 'empty',
ERROR = 'error',
DISCARDED = 'discarded' )
permitted_actions = galaxy.security.get_permitted_actions( 'DATASET' )
file_path = "/tmp/"
engine = None
def __init__( self, id=None, state=None, external_filename=None, extra_files_path=None, file_size=None, purgable=True ):
@@ -454,8 +328,400 @@ class Dataset( object ):
except OSError, e:
log.critical('%s delete error %s' % (self.__class__.__name__, e))
class Old_Dataset( Dataset ):
pass
class DatasetInstance( object ):
"""A base class for all 'dataset instances', HDAs, LDAs, etc"""
states = Dataset.states
permitted_actions = Dataset.permitted_actions
def __init__( self, id=None, hid=None, name=None, info=None, blurb=None, peek=None, extension=None,
dbkey=None, metadata=None, history=None, dataset=None, deleted=False, designation=None,
parent_id=None, validation_errors=None, visible=True, create_dataset = False ):
self.name = name or "Unnamed dataset"
self.id = id
self.info = info
self.blurb = blurb
self.peek = peek
self.extension = extension
self.dbkey = dbkey
self.designation = designation
self.metadata = metadata or dict()
self.deleted = deleted
self.visible = visible
# Relationships
if not dataset and create_dataset:
dataset = Dataset()
dataset.flush()
self.dataset = dataset
self.parent_id = parent_id
self.validation_errors = validation_errors
@property
def ext( self ):
return self.extension
def get_dataset_state( self ):
return self.dataset.state
def set_dataset_state ( self, state ):
self.dataset.state = state
self.dataset.flush() #flush here, because hda.flush() won't flush the Dataset object
state = property( get_dataset_state, set_dataset_state )
def get_file_name( self ):
return self.dataset.get_file_name()
def set_file_name (self, filename):
return self.dataset.set_file_name( filename )
file_name = property( get_file_name, set_file_name )
@property
def extra_files_path( self ):
return self.dataset.extra_files_path
@property
def datatype( self ):
return datatypes_registry.get_datatype_by_extension( self.extension )
def get_metadata( self ):
if not hasattr( self, '_metadata_collection' ):
self._metadata_collection = MetadataCollection( self )
return self._metadata_collection
def set_metadata( self, bunch ):
# Needs to accept a MetadataCollection, a bunch, or a dict
self._metadata = self.metadata.make_dict_copy( bunch )
metadata = property( get_metadata, set_metadata )
# This provide backwards compatibility with using the old dbkey
# field in the database. That field now maps to "old_dbkey" (see mapping.py).
def get_dbkey( self ):
dbkey = self.metadata.dbkey
if not isinstance(dbkey, list): dbkey = [dbkey]
#if dbkey in [["?"], [None], []]: dbkey = [self.old_dbkey]
if dbkey in [[None], []]: return "?"
return dbkey[0]
def set_dbkey( self, value ):
if "dbkey" in self.datatype.metadata_spec:
if not isinstance(value, list):
self.metadata.dbkey = [value]
else:
self.metadata.dbkey = value
#if isinstance(value, list):
# self.old_dbkey = value[0]
#else:
# self.old_dbkey = value
dbkey = property( get_dbkey, set_dbkey )
def change_datatype( self, new_ext ):
self.clear_associated_files()
datatypes_registry.change_datatype( self, new_ext )
def get_size( self ):
"""Returns the size of the data on disk"""
return self.dataset.get_size()
def set_size( self ):
"""Returns the size of the data on disk"""
return self.dataset.set_size()
def has_data( self ):
"""Detects whether there is any data"""
return self.dataset.has_data()
def get_raw_data( self ):
"""Returns the full data. To stream it open the file_name and read/write as needed"""
return self.datatype.get_raw_data( self )
def write_from_stream( self, stream ):
"""Writes data from a stream"""
self.datatype.write_from_stream(self, stream)
def set_raw_data( self, data ):
"""Saves the data on the disc"""
self.datatype.set_raw_data(self, data)
def get_mime( self ):
"""Returns the mime type of the data"""
return datatypes_registry.get_mimetype_by_extension( self.extension.lower() )
def set_peek( self ):
return self.datatype.set_peek( self )
def init_meta( self, copy_from=None ):
return self.datatype.init_meta( self, copy_from=copy_from )
def set_meta( self, **kwd ):
self.clear_associated_files( metadata_safe = True )
return self.datatype.set_meta( self, **kwd )
def set_readonly_meta( self, **kwd ):
return self.datatype.set_readonly_meta( self, **kwd )
def missing_meta( self, **kwd ):
return self.datatype.missing_meta( self, **kwd )
def as_display_type( self, type, **kwd ):
return self.datatype.as_display_type( self, type, **kwd )
def display_peek( self ):
return self.datatype.display_peek( self )
def display_name( self ):
return self.datatype.display_name( self )
def display_info( self ):
return self.datatype.display_info( self )
def get_converted_files_by_type( self, file_type ):
valid = []
for assoc in self.implicitly_converted_datasets:
if not assoc.deleted and assoc.type == file_type:
valid.append( assoc.dataset )
return valid
def clear_associated_files( self, metadata_safe = False, purge = False ):
raise 'Unimplemented'
def get_child_by_designation(self, designation):
for child in self.children:
if child.designation == designation:
return child
return None
def get_converter_types(self):
return self.datatype.get_converter_types( self, datatypes_registry)
def find_conversion_destination( self, accepted_formats, **kwd ):
"""Returns ( target_ext, exisiting converted dataset )"""
return self.datatype.find_conversion_destination( self, accepted_formats, datatypes_registry, **kwd )
def add_validation_error( self, validation_error ):
self.validation_errors.append( validation_error )
def extend_validation_errors( self, validation_errors ):
self.validation_errors.extend(validation_errors)
def mark_deleted( self, include_children=True ):
self.deleted = True
if include_children:
for child in self.children:
child.mark_deleted()
def mark_undeleted( self, include_children=True ):
self.deleted = False
if include_children:
for child in self.children:
child.mark_undeleted()
def undeletable( self ):
if self.purged:
return False
return True
class HistoryDatasetAssociation( DatasetInstance ):
def __init__( self,
hid = None,
history = None,
copied_from_history_dataset_association = None,
copied_from_library_folder_dataset_association = None,
**kwd ):
DatasetInstance.__init__( self, **kwd )
self.hid = hid
# Relationships
self.history = history
self.copied_from_history_dataset_association = copied_from_history_dataset_association
self.copied_from_library_folder_dataset_association = copied_from_library_folder_dataset_association
def copy( self, copy_children = False, parent_id = None, target_history = None ):
des = HistoryDatasetAssociation( hid=self.hid,
name=self.name,
info=self.info,
blurb=self.blurb,
peek=self.peek,
extension=self.extension,
dbkey=self.dbkey,
dataset = self.dataset,
visible=self.visible,
deleted=self.deleted,
parent_id=parent_id,
copied_from_history_dataset_association=self,
history = target_history )
des.flush()
des.metadata = self.metadata #need to set after flushed, as MetadataFiles require dataset.id
if copy_children:
for child in self.children:
child_copy = child.copy( copy_children = copy_children, parent_id = des.id )
if not self.datatype.copy_safe_peek:
des.set_peek() #in some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs
des.flush()
return des
def to_library_dataset_folder_association( self, parent_id = None, target_folder = None ):
des = LibraryFolderDatasetAssociation( name=self.name,
info=self.info,
blurb=self.blurb,
peek=self.peek,
extension=self.extension,
dbkey=self.dbkey,
dataset = self.dataset,
visible=self.visible,
deleted=self.deleted,
parent_id=parent_id,
copied_from_history_dataset_association = self,
folder = target_folder )
des.flush()
des.metadata = self.metadata #need to set after flushed, as MetadataFiles require dataset.id
if target_folder:
target_folder.add_dataset( des )
for child in self.children:
child_copy = child.to_library_dataset_folder_association( parent_id = des.id )
if not self.datatype.copy_safe_peek:
des.set_peek() #in some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs
des.flush()
return des
def clear_associated_files( self, metadata_safe = False, purge = False ):
#metadata_safe = True means to only clear when assoc.metadata_safe == False
for assoc in self.implicitly_converted_datasets:
if not metadata_safe or not assoc.metadata_safe:
assoc.clear( purge = purge )
class History( object ):
def __init__( self, id=None, name=None, user=None ):
self.id = id
self.name = name or "Unnamed history"
self.deleted = False
self.purged = False
self.genome_build = None
# Relationships
self.user = user
self.datasets = []
self.galaxy_sessions = []
def _next_hid( self ):
# TODO: override this with something in the database that ensures
# better integrity
if len( self.datasets ) == 0:
return 1
else:
last_hid = 0
for dataset in self.datasets:
if dataset.hid > last_hid:
last_hid = dataset.hid
return last_hid + 1
def add_galaxy_session( self, galaxy_session, association=None ):
if association is None:
self.galaxy_sessions.append( GalaxySessionToHistoryAssociation( galaxy_session, self ) )
else:
self.galaxy_sessions.append( association )
def add_dataset( self, dataset, parent_id=None, genome_build=None, set_hid = True ):
if isinstance( dataset, Dataset ):
dataset = HistoryDatasetAssociation( dataset = dataset )
dataset.flush()
elif not isinstance( dataset, HistoryDatasetAssociation ):
raise TypeError, "You can only add Dataset and HistoryDatasetAssociation instances to a history."
if parent_id:
for data in self.datasets:
if data.id == parent_id:
dataset.hid = data.hid
break
else:
if set_hid: dataset.hid = self._next_hid()
else:
if set_hid: dataset.hid = self._next_hid()
dataset.history = self
if genome_build not in [None, '?']:
self.genome_build = genome_build
self.datasets.append( dataset )
def copy( self, target_user = None ):
if not target_user:
target_user = self.user
des = History( user = target_user )
des.flush()
des.name = self.name
for data in self.datasets:
new_data = data.copy( copy_children = True, target_history = des )
des.add_dataset( new_data, set_hid = False )
new_data.flush()
des.hid_counter = self.hid_counter
des.flush()
return des
class Library( object ):
def __init__( self, name = None, description = None, root_folder = None ):
self.name = name or "Unnamed library"
self.description = description
self.root_folder = root_folder
class LibraryFolder( object ):
def __init__( self, name = None, description = None, item_count = 0, order_id = None ):
self.name = name or "Unnamed folder"
self.description = description
self.item_count = item_count
self.order_id = order_id
self.genome_build = None
def add_dataset( self, dataset, genome_build=None ):
dataset.folder_id = self.id
dataset.order_id = self.item_count
self.item_count += 1
if genome_build not in [None, '?']:
self.genome_build = genome_build
def add_folder( self, folder ):
folder.parent_id = self.id
folder.order_id = self.item_count
self.item_count += 1
@property
def active_components( self ):
return list( self.active_folders ) + list( self.active_datasets )
class LibraryFolderDatasetAssociation( DatasetInstance ):
def __init__( self,
folder = None,
order_id = None,
copied_from_history_dataset_association = None,
copied_from_library_folder_dataset_association = None,
**kwd ):
DatasetInstance.__init__( self, **kwd )
self.folder = folder
self.order_id = order_id
self.copied_from_history_dataset_association = copied_from_history_dataset_association
self.copied_from_library_folder_dataset_association = copied_from_library_folder_dataset_association
def to_history_dataset_association( self, parent_id = None, target_history = None ):
if target_history:
hid = target_history._next_hid()
else:
hid = None
des = HistoryDatasetAssociation( name=self.name,
info=self.info,
blurb=self.blurb,
peek=self.peek,
extension=self.extension,
dbkey=self.dbkey,
dataset = self.dataset,
visible=self.visible,
deleted=self.deleted,
parent_id=parent_id,
copied_from_library_folder_dataset_association = self,
history = target_history,
hid = hid )
des.flush()
des.metadata = self.metadata #need to set after flushed, as MetadataFiles require dataset.id
for child in self.children:
child_copy = child.to_history_dataset_association( parent_id = des.id )
if not self.datatype.copy_safe_peek:
des.set_peek() #in some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs
des.flush()
return des
def copy( self, copy_children = False, parent_id = None, target_folder = None ):
des = LibraryFolderDatasetAssociation( name=self.name,
info=self.info,
blurb=self.blurb,
peek=self.peek,
extension=self.extension,
dbkey=self.dbkey,
dataset = self.dataset,
visible=self.visible,
deleted=self.deleted,
parent_id=parent_id,
copied_from_library_folder_dataset_association = self,
folder = target_folder )
des.flush()
des.metadata = self.metadata #need to set after flushed, as MetadataFiles require dataset.id
if copy_children:
for child in self.children:
child_copy = child.copy( copy_children = copy_children, parent_id = des.id )
if not self.datatype.copy_safe_peek:
des.set_peek() #in some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs
des.flush()
return des
def clear_associated_files( self, metadata_safe = False, purge = False ):
return
class LibraryTag( object ):
def __init__( self, tag ):
self.tag = tag
class LibraryTagFolderAssociation( object ):
def __init__( self, tag, folder ):
self.tag = tag
self.folder = folder
class LibraryTagDatasetAssociation( object ):
def __init__( self, tag, dataset ):
self.tag = tag
self.dataset = dataset
# class Query( object ):
# def __init__( self, name=None, state=None, tool_parameters=None, history=None ):
# self.name = name or "Unnamed query"
# self.state = state
# self.tool_parameters = tool_parameters
# # Relationships
# self.history = history
# self.datasets = []
class ValidationError( object ):
def __init__( self, message=None, err_type=None, attributes=None ):
@@ -497,7 +763,16 @@ class Event( object ):
self.message = message
class GalaxySession( object ):
def __init__( self, id=None, user=None, remote_host=None, remote_addr=None, referer=None, current_history_id=None, session_key=None, is_valid=False, prev_session_id=None ):
def __init__( self,
id=None,
user=None,
remote_host=None,
remote_addr=None,
referer=None,
current_history_id=None,
session_key=None,
is_valid=False,
prev_session_id=None ):
self.id = id
self.user = user
self.remote_host = remote_host
@@ -544,7 +819,7 @@ class WorkflowStep( object ):
self.tool_inputs = None
self.tool_errors = None
self.position = None
self.input_connections = None
self.input_connections = []
self.config = None
class WorkflowStepConnection( object ):
@@ -567,7 +842,10 @@ class StoredWorkflowMenuEntry( object ):
class MetadataFile( object ):
def __init__( self, dataset = None, name = None ):
self.dataset = dataset
if isinstance( dataset, HistoryDatasetAssociation ):
self.history_dataset = dataset
elif isinstance( dataset, LibraryFolderDatasetAssociation ):
self.library_dataset = dataset
self.name = name
@property
def file_name( self ):
+8 -9
View File
@@ -18,16 +18,15 @@ class JSONType( TypeDecorator ):
self.mutable = mutable
super( JSONType, self).__init__()
def convert_result_value( self, value, dialect ):
def process_bind_param( self, value, dialect ):
if value is None:
return None
buf = self.impl.convert_result_value( value, dialect )
return self.jsonifyer.loads( str(buf) )
def convert_bind_param( self, value, dialect ):
return self.jsonifyer.dumps( value )
def process_result_value( self, value, dialect ):
if value is None:
return None
return self.impl.convert_bind_param( self.jsonifyer.dumps(value), dialect )
return self.jsonifyer.loads( str( value ) )
def copy_value( self, value ):
if self.mutable:
@@ -60,10 +59,10 @@ class MetadataType( JSONType ):
self.mutable = mutable
super( MetadataType, self).__init__()
def convert_result_value( self, value, dialect ):
def process_result_value( self, value, dialect ):
if value is None:
return None
buf = self.impl.convert_result_value( value, dialect )
buf = value
ret = None
try:
ret = self.pickler.loads( str(buf) )
@@ -77,7 +76,7 @@ class MetadataType( JSONType ):
class TrimmedString( TypeDecorator ):
impl = String
def convert_bind_param( self, value, dialect ):
def process_bind_param( self, value, dialect ):
"""Automatically truncate string values"""
if self.impl.length and value is not None:
value = value[0:self.impl.length]
+281 -38
View File
@@ -5,23 +5,21 @@ are encapsulated here.
import logging
log = logging.getLogger( __name__ )
import pkg_resources
pkg_resources.require( "sqlalchemy>=0.3" )
import sys
import datetime
from sqlalchemy.ext.sessioncontext import SessionContext
from sqlalchemy.ext.assignmapper import assign_mapper
from sqlalchemy.ext.orderinglist import ordering_list
from sqlalchemy import *
from galaxy.model import *
from galaxy.model.orm import *
from galaxy.model.orm.ext.assignmapper import *
from galaxy.model.custom_types import *
from galaxy.util.bunch import Bunch
from galaxy.security import GalaxyRBACAgent
metadata = DynamicMetaData( threadlocal=False )
context = SessionContext( create_session )
metadata = MetaData()
context = Session = scoped_session( sessionmaker( autoflush=False, transactional=False ) )
# For backward compatibility with "context.current"
context.current = Session
dialect_to_egg = {
"sqlite" : "pysqlite>=2",
@@ -65,7 +63,6 @@ History.table = Table( "history", metadata,
# Column( "state", String( 64 ) ),
# Column( "tool_parameters", Pickle() ) )
HistoryDatasetAssociation.table = Table( "history_dataset_association", metadata,
Column( "id", Integer, primary_key=True ),
Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ),
@@ -73,6 +70,7 @@ HistoryDatasetAssociation.table = Table( "history_dataset_association", metadata
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "copied_from_history_dataset_association_id", Integer, ForeignKey( "history_dataset_association.id" ), nullable=True ),
Column( "copied_from_library_folder_dataset_association_id", Integer, ForeignKey( "library_folder_dataset_association.id" ), nullable=True ),
Column( "hid", Integer ),
Column( "name", TrimmedString( 255 ) ),
Column( "info", TrimmedString( 255 ) ),
@@ -114,21 +112,139 @@ ValidationError.table = Table( "validation_error", metadata,
Column( "err_type", TrimmedString( 64 ) ),
Column( "attributes", TEXT ) )
Group.table = Table( "galaxy_group", metadata,
Column( "id", Integer, primary_key=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "name", TEXT, index=True, unique=True ),
Column( "deleted", Boolean, index=True, default=False ) )
UserGroupAssociation.table = Table( "user_group_association", metadata,
Column( "id", Integer, primary_key=True ),
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ),
Column( "group_id", Integer, ForeignKey( "galaxy_group.id" ), index=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ) )
UserRoleAssociation.table = Table( "user_role_association", metadata,
Column( "id", Integer, primary_key=True ),
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ),
Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ) )
GroupRoleAssociation.table = Table( "group_role_association", metadata,
Column( "id", Integer, primary_key=True ),
Column( "group_id", Integer, ForeignKey( "galaxy_group.id" ), index=True ),
Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ) )
Role.table = Table( "role", metadata,
Column( "id", Integer, primary_key=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "name", TEXT, index=True, unique=True ),
Column( "description", TEXT ),
Column( "type", TEXT, index=True ),
Column( "deleted", Boolean, index=True, default=False ) )
ActionDatasetRoleAssociation.table = Table( "action_dataset_role_association", metadata,
Column( "id", Integer, primary_key=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "action", TEXT ),
Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ),
Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) )
DefaultUserPermissions.table = Table( "default_user_permissions", metadata,
Column( "id", Integer, primary_key=True ),
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ),
Column( "action", TEXT ),
Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) )
DefaultHistoryPermissions.table = Table( "default_history_permissions", metadata,
Column( "id", Integer, primary_key=True ),
Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ),
Column( "action", TEXT ),
Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) )
LibraryFolderDatasetAssociation.table = Table( "library_folder_dataset_association", metadata,
Column( "id", Integer, primary_key=True ),
Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ),
Column( "folder_id", Integer, ForeignKey( "library_folder.id" ), index=True ),
Column( "order_id", Integer ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "copied_from_history_dataset_association_id", Integer, ForeignKey( "history_dataset_association.id", use_alter=True, name='history_dataset_association_dataset_id_fkey' ), nullable=True ),
Column( "copied_from_library_folder_dataset_association_id", Integer, ForeignKey( "library_folder_dataset_association.id", use_alter=True, name='library_folder_dataset_association_id_fkey' ), nullable=True ),
Column( "name", TrimmedString( 255 ) ),
Column( "info", TrimmedString( 255 ) ),
Column( "blurb", TrimmedString( 255 ) ),
Column( "peek" , TEXT ),
Column( "extension", TrimmedString( 64 ) ),
Column( "metadata", MetadataType(), key="_metadata" ),
Column( "parent_id", Integer, ForeignKey( "library_folder_dataset_association.id" ), nullable=True ),
Column( "designation", TrimmedString( 255 ) ),
Column( "deleted", Boolean, index=True, default=False ),
Column( "visible", Boolean ) )
Library.table = Table( "library", metadata,
Column( "id", Integer, primary_key=True ),
Column( "root_folder_id", Integer, ForeignKey( "library_folder.id" ), index=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "name", TEXT, index=True ),
Column( "deleted", Boolean, index=True, default=False ),
Column( "description", TEXT ) )
LibraryFolder.table = Table( "library_folder", metadata,
Column( "id", Integer, primary_key=True ),
Column( "parent_id", Integer, ForeignKey( "library_folder.id" ), nullable = True, index=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "name", TEXT ),
Column( "description", TEXT ),
Column( "order_id", Integer ),
Column( "item_count", Integer ),
Column( "deleted", Boolean, index=True, default=False ),
Column( "genome_build", TrimmedString( 40 ) ) )
LibraryTag.table = Table( "library_tag", metadata,
Column( "id", Integer, primary_key=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "text", TEXT ) )
LibraryTagFolderAssociation.table = Table( "library_tag_folder_association", metadata,
Column( "id", Integer, primary_key=True ),
Column( "folder_id", Integer, ForeignKey( "library_folder.id" ), index=True ),
Column( "tag_id", Integer, ForeignKey( "library_tag.id" ), index=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ) )
LibraryTagDatasetAssociation.table = Table( "library_tag_dataset_association", metadata,
Column( "id", Integer, primary_key=True ),
Column( "dataset_id", Integer, ForeignKey( "library_folder_dataset_association.id" ), index=True ),
Column( "tag_id", Integer, ForeignKey( "library_tag.id" ), index=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ) )
Job.table = Table( "job", metadata,
Column( "id", Integer, primary_key=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ),
Column( "tool_id", String( 255 ) ),
Column( "tool_version", String, default="1.0.0" ),
Column( "tool_version", TEXT, default="1.0.0" ),
Column( "state", String( 64 ) ),
Column( "info", TrimmedString( 255 ) ),
Column( "command_line", String() ),
Column( "command_line", TEXT ),
Column( "param_filename", String( 1024 ) ),
Column( "runner_name", String( 255 ) ),
Column( "stdout", String() ),
Column( "stderr", String() ),
Column( "traceback", String() ),
Column( "stdout", TEXT ),
Column( "stderr", TEXT ),
Column( "traceback", TEXT ),
Column( "session_id", Integer, ForeignKey( "galaxy_session.id" ), index=True, nullable=True ),
Column( "job_runner_name", String( 255 ) ),
Column( "job_runner_external_id", String( 255 ) ) )
@@ -188,7 +304,7 @@ StoredWorkflow.table = Table( "stored_workflow", metadata,
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ),
Column( "latest_workflow_id", Integer,
ForeignKey( "workflow.id", use_alter=True, name='stored_workflow_latest_workflow_id_fk' ), index=True ),
Column( "name", String ),
Column( "name", TEXT ),
Column( "deleted", Boolean, default=False ),
)
@@ -197,7 +313,7 @@ Workflow.table = Table( "workflow", metadata,
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "stored_workflow_id", Integer, ForeignKey( "stored_workflow.id" ), index=True, nullable=False ),
Column( "name", String ),
Column( "name", TEXT ),
Column( "has_cycles", Boolean ),
Column( "has_errors", Boolean )
)
@@ -208,8 +324,8 @@ WorkflowStep.table = Table( "workflow_step", metadata,
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "workflow_id", Integer, ForeignKey( "workflow.id" ), index=True, nullable=False ),
Column( "type", String(64) ),
Column( "tool_id", String ),
Column( "tool_version", String ), # Reserved for future
Column( "tool_id", TEXT ),
Column( "tool_version", TEXT ), # Reserved for future
Column( "tool_inputs", JSONType ),
Column( "tool_errors", JSONType ),
Column( "position", JSONType ),
@@ -222,8 +338,8 @@ WorkflowStepConnection.table = Table( "workflow_step_connection", metadata,
Column( "id", Integer, primary_key=True ),
Column( "output_step_id", Integer, ForeignKey( "workflow_step.id" ), index=True ),
Column( "input_step_id", Integer, ForeignKey( "workflow_step.id" ), index=True ),
Column( "output_name", String ),
Column( "input_name", String)
Column( "output_name", TEXT ),
Column( "input_name", TEXT)
)
StoredWorkflowUserShareAssociation.table = Table( "stored_workflow_user_share_connection", metadata,
@@ -242,6 +358,7 @@ MetadataFile.table = Table( "metadata_file", metadata,
Column( "id", Integer, primary_key=True ),
Column( "name", String ),
Column( "hda_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True, nullable=True ),
Column( "lda_id", Integer, ForeignKey( "library_folder_dataset_association.id" ), index=True, nullable=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, index=True, default=now, onupdate=now ),
Column( "deleted", Boolean, index=True, default=False ),
@@ -257,27 +374,32 @@ assign_mapper( context, HistoryDatasetAssociation, HistoryDatasetAssociation.tab
dataset=relation(
Dataset,
primaryjoin=( Dataset.table.c.id == HistoryDatasetAssociation.table.c.dataset_id ), lazy=False ),
history=relation(
History,
primaryjoin=( History.table.c.id == HistoryDatasetAssociation.table.c.history_id ) ),
# .history defined in History mapper
copied_to_history_dataset_associations=relation(
HistoryDatasetAssociation,
primaryjoin=( HistoryDatasetAssociation.table.c.copied_from_history_dataset_association_id == HistoryDatasetAssociation.table.c.id ),
backref=backref( "copied_from_history_dataset_association", primaryjoin=( HistoryDatasetAssociation.table.c.copied_from_history_dataset_association_id == HistoryDatasetAssociation.table.c.id ), remote_side=[HistoryDatasetAssociation.table.c.id] ) ),
backref=backref( "copied_from_history_dataset_association", primaryjoin=( HistoryDatasetAssociation.table.c.copied_from_history_dataset_association_id == HistoryDatasetAssociation.table.c.id ), remote_side=[HistoryDatasetAssociation.table.c.id], uselist=False ) ),
copied_to_library_folder_dataset_associations=relation(
LibraryFolderDatasetAssociation,
primaryjoin=( HistoryDatasetAssociation.table.c.copied_from_library_folder_dataset_association_id == LibraryFolderDatasetAssociation.table.c.id ),
backref=backref( "copied_from_history_dataset_association", primaryjoin=( HistoryDatasetAssociation.table.c.copied_from_library_folder_dataset_association_id == LibraryFolderDatasetAssociation.table.c.id ), remote_side=[LibraryFolderDatasetAssociation.table.c.id], uselist=False ) ),
implicitly_converted_datasets=relation(
ImplicitlyConvertedDatasetAssociation,
primaryjoin=( ImplicitlyConvertedDatasetAssociation.table.c.hda_parent_id == HistoryDatasetAssociation.table.c.id ) ),
children=relation(
HistoryDatasetAssociation,
primaryjoin=( HistoryDatasetAssociation.table.c.parent_id == HistoryDatasetAssociation.table.c.id ),
backref=backref( "parent", primaryjoin=( HistoryDatasetAssociation.table.c.parent_id == HistoryDatasetAssociation.table.c.id ), remote_side=[HistoryDatasetAssociation.table.c.id] ) )
backref=backref( "parent", primaryjoin=( HistoryDatasetAssociation.table.c.parent_id == HistoryDatasetAssociation.table.c.id ), remote_side=[HistoryDatasetAssociation.table.c.id], uselist=False ) )
) )
assign_mapper( context, Dataset, Dataset.table,
properties=dict(
history_associations=relation(
HistoryDatasetAssociation,
primaryjoin=( Dataset.table.c.id == HistoryDatasetAssociation.table.c.dataset_id ) )
primaryjoin=( Dataset.table.c.id == HistoryDatasetAssociation.table.c.dataset_id ) ),
library_associations=relation(
LibraryFolderDatasetAssociation,
primaryjoin=( Dataset.table.c.id == LibraryFolderDatasetAssociation.table.c.dataset_id ) )
) )
@@ -302,13 +424,118 @@ assign_mapper( context, History, History.table,
) )
assign_mapper( context, User, User.table,
properties=dict( histories=relation( History, backref="user",
properties=dict( histories=relation( History, backref="user",
order_by=desc(History.table.c.update_time) ),
active_histories=relation( History, primaryjoin=( ( History.table.c.user_id == User.table.c.id ) & ( not_( History.table.c.deleted ) ) ), order_by=desc( History.table.c.update_time ) ),
stored_workflow_menu_entries=relation( StoredWorkflowMenuEntry, backref="user",
cascade="all, delete-orphan",
collection_class=ordering_list( 'order_index' ) )
) )
assign_mapper( context, Group, Group.table,
properties=dict( users=relation( UserGroupAssociation ) ) )
assign_mapper( context, UserGroupAssociation, UserGroupAssociation.table,
properties=dict( user=relation( User, backref = "groups" ),
group=relation( Group, backref = "members" ) ) )
assign_mapper( context, DefaultUserPermissions, DefaultUserPermissions.table,
properties=dict( user=relation( User, backref = "default_permissions" ),
role=relation( Role ) ) )
assign_mapper( context, DefaultHistoryPermissions, DefaultHistoryPermissions.table,
properties=dict( history=relation( History, backref = "default_permissions" ),
role=relation( Role ) ) )
assign_mapper( context, Role, Role.table,
properties=dict(
users=relation( UserRoleAssociation ),
groups=relation( GroupRoleAssociation )
)
)
assign_mapper( context, UserRoleAssociation, UserRoleAssociation.table,
properties=dict(
user=relation( User, backref="roles" ),
non_private_roles=relation( User,
backref="non_private_roles",
primaryjoin=( ( User.table.c.id == UserRoleAssociation.table.c.user_id ) & ( UserRoleAssociation.table.c.role_id == Role.table.c.id ) & not_( Role.table.c.type == 'private' ) ) ),
role=relation( Role )
)
)
assign_mapper( context, GroupRoleAssociation, GroupRoleAssociation.table,
properties=dict(
group=relation( Group, backref="roles" ),
role=relation( Role )
)
)
assign_mapper( context, ActionDatasetRoleAssociation, ActionDatasetRoleAssociation.table,
properties=dict(
dataset=relation( Dataset, backref="actions" ),
role=relation( Role, backref="actions" )
)
)
assign_mapper( context, Library, Library.table,
properties=dict(
root_folder=relation( LibraryFolder,
backref=backref( "library_root" ) )
) )
assign_mapper( context, LibraryFolder, LibraryFolder.table,
properties=dict(
folders=relation(
LibraryFolder,
primaryjoin=( LibraryFolder.table.c.parent_id == LibraryFolder.table.c.id ),
backref=backref( "parent", primaryjoin=( LibraryFolder.table.c.parent_id == LibraryFolder.table.c.id ), remote_side=[LibraryFolder.table.c.id] ) ),
active_folders=relation( LibraryFolder,
primaryjoin=( ( LibraryFolder.table.c.parent_id == LibraryFolder.table.c.id ) & ( not_( LibraryFolder.table.c.deleted ) ) ),
order_by=asc( LibraryFolder.table.c.order_id ),
lazy=True, #"""sqlalchemy.exceptions.ArgumentError: Error creating eager relationship 'active_folders' on parent class '<class 'galaxy.model.LibraryFolder'>' to child class '<class 'galaxy.model.LibraryFolder'>': Cant use eager loading on a self referential relationship."""
viewonly=True ),
active_datasets=relation( LibraryFolderDatasetAssociation,
primaryjoin=( ( LibraryFolderDatasetAssociation.table.c.folder_id == LibraryFolder.table.c.id ) & ( not_( LibraryFolderDatasetAssociation.table.c.deleted ) ) ),
order_by=asc( LibraryFolderDatasetAssociation.table.c.order_id ),
lazy=False,
viewonly=True ),
tags=relation(
LibraryTagFolderAssociation,
primaryjoin=( LibraryFolder.table.c.id == LibraryTagFolderAssociation.table.c.folder_id ),
backref=backref( "folders" ) )
) )
assign_mapper( context, LibraryFolderDatasetAssociation, LibraryFolderDatasetAssociation.table,
properties=dict(
dataset=relation( Dataset ),
folder=relation(
LibraryFolder,
backref=backref( "datasets" ) ),
copied_to_library_folder_dataset_associations=relation(
LibraryFolderDatasetAssociation,
primaryjoin=( LibraryFolderDatasetAssociation.table.c.copied_from_library_folder_dataset_association_id == LibraryFolderDatasetAssociation.table.c.id ),
backref=backref( "copied_from_library_folder_dataset_association", primaryjoin=( LibraryFolderDatasetAssociation.table.c.copied_from_library_folder_dataset_association_id == LibraryFolderDatasetAssociation.table.c.id ), remote_side=[LibraryFolderDatasetAssociation.table.c.id] ) ),
children=relation(
LibraryFolderDatasetAssociation,
primaryjoin=( LibraryFolderDatasetAssociation.table.c.parent_id == LibraryFolderDatasetAssociation.table.c.id ),
backref=backref( "parent", primaryjoin=( LibraryFolderDatasetAssociation.table.c.parent_id == LibraryFolderDatasetAssociation.table.c.id ), remote_side=[LibraryFolderDatasetAssociation.table.c.id] ) ),
tags=relation(
LibraryTagDatasetAssociation,
primaryjoin=( LibraryFolderDatasetAssociation.table.c.id == LibraryTagDatasetAssociation.table.c.dataset_id ),
backref=backref( "datasets" ) )
) )
assign_mapper( context, LibraryTag, LibraryTag.table )
assign_mapper( context, LibraryTagFolderAssociation, LibraryTagFolderAssociation.table,
properties=dict( tag=relation( LibraryTag ),
folder=relation( LibraryFolder ) ) )
assign_mapper( context, LibraryTagDatasetAssociation, LibraryTagDatasetAssociation.table,
properties=dict( tag=relation( LibraryTag ),
dataset=relation( LibraryFolderDatasetAssociation ) ) )
assign_mapper( context, JobToInputDatasetAssociation, JobToInputDatasetAssociation.table,
properties=dict( job=relation( Job ), dataset=relation( HistoryDatasetAssociation ) ) )
@@ -373,18 +600,19 @@ assign_mapper( context, StoredWorkflowMenuEntry, StoredWorkflowMenuEntry.table,
properties=dict( stored_workflow=relation( StoredWorkflow ) ) )
assign_mapper( context, MetadataFile, MetadataFile.table,
properties=dict( dataset=relation( HistoryDatasetAssociation ) ) )
properties=dict( history_dataset=relation( HistoryDatasetAssociation ), library_dataset=relation( LibraryFolderDatasetAssociation ) ) )
def db_next_hid( self ):
"""
Override __next_hid to generate from the database in a concurrency
safe way.
"""
conn = self.table.engine.contextual_connect()
conn = object_session( self ).connection()
table = self.table
trans = conn.begin()
try:
next_hid = select( [self.c.hid_counter], self.c.id == self.id, for_update=True ).scalar()
self.table.update( self.c.id == self.id ).execute( hid_counter = ( next_hid + 1 ) )
next_hid = select( [table.c.hid_counter], table.c.id == self.id, for_update=True ).scalar()
table.update( table.c.id == self.id ).execute( hid_counter = ( next_hid + 1 ) )
trans.commit()
return next_hid
except:
@@ -413,18 +641,33 @@ def init( file_path, url, engine_options={}, create_tables=False ):
# Create the database engine
engine = create_engine( url, **engine_options )
# Connect the metadata to the database.
metadata.connect( engine )
## metadata.engine.echo = True
metadata.bind = engine
# Clear any existing contextual sessions and reconfigure
Session.remove()
Session.configure( bind=engine )
# Create tables if needed
if create_tables:
metadata.create_all()
# metadata.engine.commit()
# Pack everything into a bunch
result = Bunch( **globals() )
result.engine = metadata.engine
result.flush = lambda *args, **kwargs: context.current.flush( *args, **kwargs )
result.context = context
result.engine = engine
result.flush = lambda *args, **kwargs: Session.flush( *args, **kwargs )
result.session = Session
# For backward compatibility with "model.context.current"
result.context = Session
result.create_tables = create_tables
#load local galaxy security policy
result.security_agent = GalaxyRBACAgent( result )
# Create private roles if necessary.
if not result.Role.query().all():
for user in result.User.query().all():
role = Role( name = user.email, description = 'Private Role for ' + user.email, type = 'private' )
role.flush()
ura = UserRoleAssociation( user = user, role = role )
ura.flush()
dup = DefaultUserPermissions( user = user, action = result.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS.action, role = role )
dup.flush()
return result
def get_suite():
+3 -3
View File
@@ -22,13 +22,13 @@ class MappingTests( unittest.TestCase ):
model.context.current.flush()
model.context.current.clear()
# Check
users = model.User.select()
users = model.User.query().all()
assert len( users ) == 1
assert users[0].email == "james@foo.bar.baz"
assert users[0].password == "password"
assert len( users[0].histories ) == 1
assert users[0].histories[0].name == "History 1"
hists = model.History.select()
hists = model.History.query().all()
assert hists[0].name == "History 1"
assert hists[1].name == ( "H" * 255 )
assert hists[0].user == users[0]
@@ -40,7 +40,7 @@ class MappingTests( unittest.TestCase ):
hists[1].name = "History 2b"
model.context.current.flush()
model.context.current.clear()
hists = model.History.select()
hists = model.History.query().all()
assert hists[0].name == "History 1"
assert hists[1].name == "History 2b"
# gvk TODO need to ad test for GalaxySessions, but not yet sure what they should look like.
+7
View File
@@ -0,0 +1,7 @@
import pkg_resources
pkg_resources.require( "SQLAlchemy >= 0.4" )
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.orderinglist import ordering_list
+3
View File
@@ -0,0 +1,3 @@
"""
Galaxy specific SQLAlchemy extensions.
"""
+62
View File
@@ -0,0 +1,62 @@
"""
This is similar to the assignmapper extensions in SQLAclhemy 0.3 and 0.4 but
with some compatibility fixes. It assumes that the session is a ScopedSession,
and thus has the "mapper" method to attach contextual mappers to a class. It
adds additional query and session methods to the class to support the
SQLAlchemy 0.3 style of access. The following methods which would normally be
accessed through "Object.query().method()" are available directly through the
object:
'get', 'filter', 'filter_by', 'select', 'select_by',
'selectfirst', 'selectfirst_by', 'selectone', 'selectone_by',
'get_by', 'join_to', 'join_via', 'count', 'count_by',
'options', 'instances'
Additionally, the following Session methods, which normally accept an instance
or list of instances, are available directly through the objects, e.g.
"Session.flush( [instance] )" can be performed as "instance.flush()":
'refresh', 'expire', 'delete', 'expunge', 'update'
"""
__all__ = [ 'assign_mapper' ]
from sqlalchemy import util, exceptions
import types
from sqlalchemy.orm import mapper, Query
def _monkeypatch_query_method( name, session, class_ ):
def do(self, *args, **kwargs):
## util.warn_deprecated('Query methods on the class are deprecated; use %s.query.%s instead' % (class_.__name__, name))
return getattr( class_.query, name)(*args, **kwargs)
try:
do.__name__ = name
except:
pass
if not hasattr(class_, name):
setattr(class_, name, classmethod(do))
def _monkeypatch_session_method(name, session, class_, make_list=False):
def do(self, *args, **kwargs):
if make_list:
self = [ self ]
return getattr(session, name)( self, *args, **kwargs )
try:
do.__name__ = name
except:
pass
if not hasattr(class_, name):
setattr(class_, name, do)
def assign_mapper( session, class_, *args, **kwargs ):
m = class_.mapper = session.mapper( class_, *args, **kwargs )
for name in ('get', 'filter', 'filter_by', 'select', 'select_by',
'selectfirst', 'selectfirst_by', 'selectone', 'selectone_by',
'get_by', 'join_to', 'join_via', 'count', 'count_by',
'options', 'instances'):
_monkeypatch_query_method(name, session, class_)
for name in ('refresh', 'expire', 'delete', 'expunge', 'update'):
_monkeypatch_session_method(name, session, class_)
for name in ( 'flush', ):
_monkeypatch_session_method( name, session, class_, make_list=True )
return m
+340
View File
@@ -0,0 +1,340 @@
"""
Galaxy Security
"""
import logging
from galaxy.util.bunch import Bunch
from galaxy.model.orm import *
log = logging.getLogger(__name__)
class Action( object ):
def __init__( self, action, description, model ):
self.action = action
self.description = description
self.model = model
class RBACAgent:
"""Class that handles galaxy security"""
permitted_actions = Bunch(
DATASET_EDIT_METADATA = Action(
"edit metadata", "Role members can edit this dataset's metadata in the library", "grant" ),
DATASET_MANAGE_PERMISSIONS = Action(
"manage permissions", "Role members can manage the roles associated with this dataset", "grant" ),
DATASET_ACCESS = Action(
"access", "Role members can import this dataset into their history for analysis", "restrict" )
)
def get_action( self, name, default=None ):
"""
Get a permitted action by its dict key or action name
"""
for k, v in self.permitted_actions.items():
if k == name or v.action == name:
return v
return default
def get_actions( self ):
"""
Get all permitted actions as a list
"""
return self.permitted_actions.__dict__.values()
def allow_action( self, user, action, **kwd ):
raise 'No valid method of checking action (%s) on %s for user %s.' % ( action, kwd, user )
def guess_derived_permissions_for_datasets( self, datasets = [] ):
raise "Unimplemented Method"
def associate_components( self, **kwd ):
raise 'No valid method of associating provided components: %s' % kwd
def create_private_user_role( self, user ):
raise "Unimplemented Method"
def get_private_user_role( self, user ):
raise "Unimplemented Method"
def user_set_default_permissions( self, user, permissions = None, history = False, dataset = False ):
raise "Unimplemented Method"
def setup_new_user( self, user ):
self.create_private_user_role( user )
self.user_set_default_permissions( user, history = True, dataset = True )
#self.associate_components( user=user, group=self.get_public_group() )
def history_set_default_permissions( self, history, permissions=None, dataset=False, bypass_manage_permission=False ):
raise "Unimplemented Method"
def set_dataset_permissions( self, dataset, permissions ):
raise "Unimplemented Method"
def get_component_associations( self, **kwd ):
raise "Unimplemented Method"
def components_are_associated( self, **kwd ):
return bool( self.get_component_associations( **kwd ) )
def convert_permitted_action_strings( self, permitted_action_strings ):
"""
When getting permitted actions from an untrusted source like a
form, ensure that they match our actual permitted actions.
"""
return filter( lambda x: x is not None, [ self.permitted_actions.get( action_string ) for action_string in permitted_action_strings ] )
class GalaxyRBACAgent( RBACAgent ):
def __init__( self, model, permitted_actions=None ):
self.model = model
if permitted_actions:
self.permitted_actions = permitted_actions
def allow_action( self, user, action, **kwd ):
if 'dataset' in kwd:
return self.allow_dataset_action( user, action, kwd['dataset'] )
raise 'No valid method of checking action (%s) on %s for user %s.' % ( action, kwd, user )
def allow_dataset_action( self, user, action, dataset ):
"""Returns true when user has permission to perform an action"""
if not isinstance( dataset, self.model.Dataset ):
dataset = dataset.dataset
if not user:
if action == self.permitted_actions.DATASET_ACCESS and action.action not in [ adra.action for adra in dataset.actions ]:
return True # anons only get access, and only if there are no roles required for the access action
# other actions (or if the dataset has roles defined for the access action) fall through to the false below
elif action.action not in [ adra.action for adra in dataset.actions ]:
if action.model == 'restrict':
return True # implicit access to restrict-style actions if the dataset does not have the action
# grant-style actions fall through to the false below
else:
user_role_ids = sorted( [ r.id for r in user.all_roles() ] )
perms = self.get_dataset_permissions( dataset )
if action in perms.keys():
# the filter() returns a list of the dataset's role ids
# of which the user is not a member. so an empty list
# means the user has all of the required roles.
if not filter( lambda x: x not in user_role_ids, [ r.id for r in perms[ action ] ] ):
return True # user has all of the roles required to perform the action
# fall through to the false. user is missing at least one required role
return False # default is to reject
def guess_derived_permissions_for_datasets( self, datasets=[] ):
"""Returns a dict of { action : [ role, role, ... ] } for the output dataset based upon provided datasets"""
perms = {}
for dataset in datasets:
if not isinstance( dataset, self.model.Dataset ):
dataset = dataset.dataset
these_perms = {}
# initialize blank perms
for action in self.get_actions():
these_perms[ action ] = []
# collect this dataset's perms
these_perms = self.get_dataset_permissions( dataset )
# join or intersect this dataset's permissions with others
for action, roles in these_perms.items():
if action not in perms.keys():
perms[ action ] = roles
else:
if action.model == 'grant':
# intersect existing roles with new roles
perms[ action ] = filter( lambda x: x in perms[ action ], roles )
elif action.model == 'restrict':
# join existing roles with new roles
perms[ action ].extend( filter( lambda x: x not in perms[ action ], roles ) )
return perms
def associate_components( self, **kwd ):
if 'user' in kwd:
if 'group' in kwd:
return self.associate_user_group( kwd['user'], kwd['group'] )
elif 'role' in kwd:
return self.associate_user_role( kwd['user'], kwd['role'] )
elif 'role' in kwd:
if 'group' in kwd:
return self.associate_group_role( kwd['group'], kwd['role'] )
if 'action' in kwd:
if 'dataset' in kwd and 'role' in kwd:
return self.associate_action_dataset_role( kwd['action'], kwd['dataset'], kwd['role'] )
raise 'No valid method of associating provided components: %s' % kwd
def associate_user_group( self, user, group ):
assoc = self.model.UserGroupAssociation( user, group )
assoc.flush()
return assoc
def associate_user_role( self, user, role ):
assoc = self.model.UserRoleAssociation( user, role )
assoc.flush()
return assoc
def associate_group_role( self, group, role ):
assoc = self.model.GroupRoleAssociation( group, role )
assoc.flush()
return assoc
def associate_action_dataset_role( self, action, dataset, role ):
assoc = self.model.ActionDatasetRoleAssociation( action, dataset, role )
assoc.flush()
return assoc
def create_private_user_role( self, user ):
# Create private role
role = self.model.Role( name=user.email, description='Private Role for ' + user.email, type=self.model.Role.types.PRIVATE )
role.flush()
# Add user to role
self.associate_components( role=role, user=user )
return role
def get_private_user_role( self, user, auto_create=False ):
role = self.model.Role.filter( and_( self.model.Role.table.c.name == user.email,
self.model.Role.table.c.type == self.model.Role.types.PRIVATE ) ).first()
if not role:
if auto_create:
return self.create_private_user_role( user )
else:
return None
return role
def user_set_default_permissions( self, user, permissions = {}, history = False, dataset = False ):
if user is None:
return None
if not permissions:
permissions = { self.permitted_actions.DATASET_MANAGE_PERMISSIONS : [ self.get_private_user_role( user, auto_create=True ) ] }
# Delete all of the previous defaults
for dup in user.default_permissions:
dup.delete()
dup.flush()
# Add the new defaults (if any)
for action, roles in permissions.items():
if isinstance( action, Action ):
action = action.action
for role in roles:
dup = self.model.DefaultUserPermissions( user, action, role )
dup.flush()
if history:
for history in user.active_histories:
self.history_set_default_permissions( history, permissions=permissions, dataset=dataset )
def user_get_default_permissions( self, user ):
perms = {}
for action in self.get_actions():
perms[ action ] = []
for dup in user.default_permissions:
perms[ self.get_action( dup.action ) ].append( dup.role )
return perms
def history_set_default_permissions( self, history, permissions = {}, dataset = False, bypass_manage_permission = False ):
if not history.user:
return None # default permissions on a userless history are none
if not permissions:
permissions = self.user_get_default_permissions( history.user )
for dhp in history.default_permissions:
dhp.delete()
dhp.flush()
for action, roles in permissions.items():
if isinstance( action, Action ):
action = action.action
for role in roles:
dhp = self.model.DefaultHistoryPermissions( history, action, role )
dhp.flush()
if dataset:
for hda_in_history in history.datasets:
if len( hda_in_history.dataset.library_associations ):
continue # dataset has a library association, don't change the permissions
if len( [ hda for hda in hda_in_history.dataset.history_associations if hda.history not in history.user.histories ] ):
continue # dataset has a history association in a history the user doesn't own, don't change the permissions
# bypass is used to change permissions of datasets in a userless history when logging in
if bypass_manage_permission or self.allow_action( history.user, self.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset=hda_in_history.dataset ):
self.set_dataset_permissions( hda_in_history.dataset, permissions )
def history_get_default_permissions( self, history ):
perms = {}
for action in self.get_actions():
perms[ action ] = []
for dhp in history.default_permissions:
perms[ self.get_action( dhp.action ) ].append( dhp.role )
return perms
def set_dataset_permissions( self, dataset, permissions={} ):
# to delete permission on an action, pass in a blank list of
# roles with that action. leaving an action out of the perm
# dict simply leaves those perms untouched (if they exist)
incoming_actions = []
for action in permissions.keys():
if isinstance( action, Action ):
action = action.action
incoming_actions.append( action )
for adra in dataset.actions:
if adra.action in incoming_actions:
adra.delete()
adra.flush()
for action, roles in permissions.items():
if isinstance( action, Action ):
action = action.action
for role in roles:
self.associate_components( action=action, dataset=dataset, role=role )
def get_dataset_permissions( self, dataset ):
if not isinstance( dataset, self.model.Dataset ):
dataset = dataset.dataset
perms = {}
for action in self.model.Dataset.permitted_actions.__dict__.values():
perms[ action ] = []
for adra in dataset.actions:
perms[ self.get_action( adra.action ) ].append( adra.role )
return perms
def copy_dataset_permissions( self, src, dst ):
if not isinstance( src, self.model.Dataset ):
src = src.dataset
if not isinstance( dst, self.model.Dataset ):
dst = dst.dataset
self.set_dataset_permissions( dst, self.get_dataset_permissions( src ) )
def privately_share_dataset( self, dataset, users = [] ):
intersect = None
for user in users:
roles = [ ura.role for ura in user.roles if ura.role.type == self.model.Role.types.SHARING ]
if intersect is None:
intersect = roles
else:
new_intersect = []
for role in roles:
if role in intersect:
new_intersect.append( role )
intersect = new_intersect
sharing_role = None
if intersect:
for role in intersect:
if not filter( lambda x: x not in users, [ ura.user for ura in role.users ] ):
# only use a role if it contains ONLY the users we're sharing with
sharing_role = role
break
if sharing_role is None:
sharing_role = self.model.Role( name = "Sharing role for: " + ", ".join( [ u.email for u in users ] ),
type = self.model.Role.types.SHARING )
sharing_role.flush()
for user in users:
self.associate_components( user=user, role=sharing_role )
self.set_dataset_permissions( dataset, { self.permitted_actions.DATASET_ACCESS : [ sharing_role ] } )
def set_entity_role_associations( self, roles=[], users=[], groups=[], delete_existing_assocs=True ):
for role in roles:
if delete_existing_assocs:
for a in role.users + role.groups:
a.delete()
a.flush()
for user in users:
self.associate_components( user=user, role=role )
for group in groups:
self.associate_components( group=group, role=role )
def get_component_associations( self, **kwd ):
assert len( kwd ) == 2, 'You must specify exactly 2 Galaxy security components to check for associations.'
if 'dataset' in kwd:
if 'action' in kwd:
return self.model.ActionDatasetRoleAssociation.filter_by( action = kwd['action'].action, dataset_id = kwd['dataset'].id ).first()
elif 'user' in kwd:
if 'group' in kwd:
return self.model.UserGroupAssociation.filter_by( group_id = kwd['group'].id, user_id = kwd['user'].id ).first()
elif 'role' in kwd:
return self.model.UserRoleAssociation.filter_by( role_id = kwd['role'].id, user_id = kwd['user'].id ).first()
elif 'group' in kwd:
if 'role' in kwd:
return self.model.GroupRoleAssociation.filter_by( role_id = kwd['role'].id, group_id = kwd['group'].id ).first()
raise 'No valid method of associating provided components: %s' % kwd
def check_folder_contents( self, user, entry ):
"""
Return true if there are any datasets under 'folder' that the
user has access permission on. We do this a lot and it's a
pretty inefficient method, optimizations are welcomed.
"""
if isinstance( entry, self.model.Library ):
return self.check_folder_contents( user, entry.root_folder )
elif isinstance( entry, self.model.LibraryFolderDatasetAssociation ):
return self.allow_action( user, self.permitted_actions.DATASET_ACCESS, dataset=entry )
elif isinstance( entry, self.model.LibraryFolder ):
for dataset in entry.active_datasets:
if self.allow_action( user, self.permitted_actions.DATASET_ACCESS, dataset=dataset ):
return True
for folder in entry.active_folders:
if self.check_folder_contents( user, folder ):
return True
return False
else:
raise 'Passed an illegal object to check_folder_contents: %s' % type( entry )
def get_permitted_actions( self, filter=None ):
'''Utility method to return a subset of RBACAgent's permitted actions'''
if filter is None:
return RBACAgent.permitted_actions
if not filter.endswith('_'):
filter += '_'
tmp_bunch = Bunch()
[tmp_bunch.__dict__.__setitem__(k, v) for k, v in RBACAgent.permitted_actions.items() if k.startswith(filter)]
return tmp_bunch
+3 -4
View File
@@ -1085,7 +1085,7 @@ class Tool:
redirect_url_params = redirect_url_params.replace( "\n", " " ).replace( "\r", " " )
return redirect_url_params
def parse_redirect_url( self, inp_data, param_dict ):
def parse_redirect_url( self, data, param_dict ):
"""Parse the REDIRECT_URL tool param"""
# Tools that send data to an external application via a redirect must include the following 3 tool params:
# REDIRECT_URL - the url to which the data is being sent
@@ -1104,9 +1104,6 @@ class Tool:
rup_dict[ p_name ] = p_val
DATA_URL = param_dict.get( 'DATA_URL', None )
assert DATA_URL is not None, "DATA_URL parameter missing in tool config."
# Get the dataset - there should only be 1
for name in inp_data.keys():
data = inp_data[ name ]
DATA_URL += "/%s/display" % str( data.id )
redirect_url += "?DATA_URL=%s" % DATA_URL
# Add the redirect_url_params to redirect_url
@@ -1215,6 +1212,7 @@ class Tool:
else: visible = False
ext = fields.pop(0).lower()
child_dataset = self.app.model.HistoryDatasetAssociation( extension=ext, parent_id=outdata.id, designation=designation, visible=visible, dbkey=outdata.dbkey, create_dataset=True )
self.app.security_agent.copy_dataset_permissions( outdata.dataset, child_dataset.dataset )
# Move data from temp location to dataset location
shutil.move( filename, child_dataset.file_name )
child_dataset.flush()
@@ -1251,6 +1249,7 @@ class Tool:
ext = fields.pop(0).lower()
# Create new primary dataset
primary_data = self.app.model.HistoryDatasetAssociation( extension=ext, designation=designation, visible=visible, dbkey=outdata.dbkey, create_dataset=True )
self.app.security_agent.copy_dataset_permissions( outdata.dataset, primary_data.dataset )
primary_data.flush()
# Move data from temp location to dataset location
shutil.move( filename, primary_data.file_name )
+24 -1
View File
@@ -44,6 +44,9 @@ class DefaultToolAction( object ):
assoc.dataset = new_data
assoc.flush()
data = new_data
# TODO, Nate: Make sure the permitted actions here are appropriate.
if data and not trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_ACCESS, dataset=data ):
raise "User does not have permission to use a dataset (%s) provided for input." % data.id
return data
if isinstance( input, DataToolParameter ):
if isinstance( value, list ):
@@ -80,6 +83,14 @@ class DefaultToolAction( object ):
data = NoneDataset( datatypes_registry = trans.app.datatypes_registry )
if data.dbkey not in [None, '?']:
input_dbkey = data.dbkey
# Determine output dataset permission/roles list
existing_datasets = [ inp for inp in inp_data.values() if inp ]
if existing_datasets:
output_permissions = trans.app.security_agent.guess_derived_permissions_for_datasets( existing_datasets )
else:
# No valid inputs, we will use history defaults
output_permissions = trans.app.security_agent.history_get_default_permissions( trans.history )
# Build name for output datasets based on tool name and input names
if len( input_names ) == 1:
on_text = input_names[0]
@@ -133,6 +144,7 @@ class DefaultToolAction( object ):
data = trans.app.model.HistoryDatasetAssociation( extension=ext, create_dataset=True )
# Commit the dataset immediately so it gets database assigned unique id
data.flush()
trans.app.security_agent.set_dataset_permissions( data.dataset, output_permissions )
# Create an empty file immediately
open( data.file_name, "w" ).close()
# This may not be neccesary with the new parent/child associations
@@ -192,6 +204,9 @@ class DefaultToolAction( object ):
job.add_parameter( name, value )
for name, dataset in inp_data.iteritems():
if dataset:
# TODO, Nate: Make sure the permitted actions here are appropriate.
if not trans.app.security_agent.allow_action( trans.user, dataset.permitted_actions.DATASET_ACCESS, dataset=dataset ):
raise "User does not have permission to use a dataset (%s) provided for input." % data.id
job.add_input_dataset( name, dataset )
else:
job.add_input_dataset( name, None )
@@ -203,7 +218,15 @@ class DefaultToolAction( object ):
# include something that can be retrieved from the params ( e.g., REDIRECT_URL ) to keep the job
# from being queued.
if 'REDIRECT_URL' in incoming:
redirect_url = tool.parse_redirect_url( inp_data, incoming )
# Get the dataset - there should only be 1
for name in inp_data.keys():
dataset = inp_data[ name ]
redirect_url = tool.parse_redirect_url( dataset, incoming )
# GALAXY_URL should be include in the tool params to enable the external application
# to send back to the current Galaxy instance
GALAXY_URL = incoming.get( 'GALAXY_URL', None )
assert GALAXY_URL is not None, "GALAXY_URL parameter missing in tool config."
redirect_url += "&GALAXY_URL=%s" % GALAXY_URL
# Job should not be queued, so set state to ok
job.state = JOB_OK
job.info = "Redirected to: %s" % redirect_url
+19 -5
View File
@@ -36,12 +36,24 @@ class UploadToolAction( object ):
return self.upload_empty( trans, "Error:", str( e ) )
if url_paste not in [ None, "" ]:
if url_paste.lower().find( 'http://' ) >= 0 or url_paste.lower().find( 'ftp://' ) >= 0:
# If we were sent a DATA_URL from an external application in a post, NAME and INFO
# values should be in the request
if 'NAME' in incoming and incoming[ 'NAME' ] not in [ "None", None ]:
NAME = incoming[ 'NAME' ]
else:
NAME = ''
if 'INFO' in incoming and incoming[ 'INFO' ] not in [ "None", None ]:
INFO = incoming[ 'INFO' ]
else:
INFO = "uploaded url"
url_paste = url_paste.replace( '\r', '' ).split( '\n' )
for line in url_paste:
line = line.rstrip( '\r\n' )
if line:
if not NAME:
NAME = line
try:
data_list.append( self.add_file( trans, urllib.urlopen( line ), line, file_type, dbkey, info="uploaded url", space_to_tab=space_to_tab ) )
data_list.append( self.add_file( trans, urllib.urlopen( line ), NAME, file_type, dbkey, info=INFO, space_to_tab=space_to_tab ) )
except Exception, e:
return self.upload_empty( trans, "Error:", str( e ) )
else:
@@ -65,8 +77,9 @@ class UploadToolAction( object ):
return dict( output=data_list[0] )
def upload_empty(self, trans, err_code, err_msg):
data = trans.app.model.HistoryDatasetAssociation( create_dataset = True )
data.name = err_code
data = trans.app.model.HistoryDatasetAssociation( create_dataset=True )
trans.app.security_agent.set_dataset_permissions( data.dataset, trans.app.security_agent.history_get_default_permissions( trans.history ) )
data.name = err_code
data.extension = "txt"
data.dbkey = "?"
data.info = err_msg
@@ -85,12 +98,12 @@ class UploadToolAction( object ):
if not os.path.getsize( temp_name ) > 0:
raise BadFileException( "you attempted to upload an empty file." )
# See if we have a gzipped file, which, if it passes our restrictions, we'll decompress on the fly.
# See if we have a gzipped file, which, if it passes our restrictions, we'll uncompress on the fly.
is_gzipped, is_valid = self.check_gzip( temp_name )
if is_gzipped and not is_valid:
raise BadFileException( "you attempted to upload an inappropriate file." )
elif is_gzipped and is_valid:
#We need to decompress the temp_name file
# We need to uncompress the temp_name file
CHUNK_SIZE = 2**20 # 1Mb
fd, uncompressed = tempfile.mkstemp()
gzipped_file = gzip.GzipFile( temp_name )
@@ -159,6 +172,7 @@ class UploadToolAction( object ):
info = 'uploaded %s file' %data_type
data = trans.app.model.HistoryDatasetAssociation( history = trans.history, extension = ext, create_dataset = True )
trans.app.security_agent.set_dataset_permissions( data.dataset, trans.app.security_agent.history_get_default_permissions( trans.history ) )
data.name = file_name
data.dbkey = dbkey
data.info = info
+29 -36
View File
@@ -3,16 +3,13 @@ Basic tool parameters.
"""
import logging, string, sys, os
from elementtree.ElementTree import XML, Element
from galaxy import config, datatypes, util
from galaxy.web import form_builder
import validation, dynamic_options
# For BaseURLToolParameter
from galaxy.web import url_for
import galaxy.model
log = logging.getLogger(__name__)
@@ -980,30 +977,16 @@ class DrillDownSelectToolParameter( ToolParameter ):
class DataToolParameter( ToolParameter ):
# TODO, Nate: Make sure the following unit tests appropriately test the dataset security
# components. Add as many additional tests as necessary.
"""
Parameter that takes on one (or many) or a specific set of values.
TODO: There should be an alternate display that allows single selects to be
displayed as radio buttons and multiple selects as a set of checkboxes
>>> # Mock up a history (not connected to database)
>>> from galaxy.model import History, HistoryDatasetAssociation
>>> from galaxy.util.bunch import Bunch
>>> hist = History()
>>> hist.flush()
>>> hist.add_dataset( HistoryDatasetAssociation( id=1, extension='txt', create_dataset=True ) )
>>> hist.add_dataset( HistoryDatasetAssociation( id=2, extension='bed', create_dataset=True ) )
>>> hist.add_dataset( HistoryDatasetAssociation( id=3, extension='fasta', create_dataset=True ) )
>>> hist.add_dataset( HistoryDatasetAssociation( id=4, extension='png', create_dataset=True ) )
>>> hist.add_dataset( HistoryDatasetAssociation( id=5, extension='interval', create_dataset=True ) )
>>> p = DataToolParameter( None, XML( '<param name="blah" type="data" format="interval"/>' ) )
>>> print p.name
blah
>>> print p.get_html( trans=Bunch( history=hist ) )
<select name="blah">
<option value="2">2: Unnamed dataset</option>
<option value="5" selected>5: Unnamed dataset</option>
</select>
TODO: The following must be fixed to test correctly for the new security_check tag in the DataToolParameter ( the last test below is broken )
Nate's next passs at the dataset security stuff will dramatically alter this anyway.
"""
def __init__( self, tool, elem ):
@@ -1050,28 +1033,38 @@ class DataToolParameter( ToolParameter ):
value = [ value ]
field = form_builder.SelectField( self.name, self.multiple, None, self.refresh_on_change )
# CRUCIAL: the dataset_collector function needs to be local to DataToolParameter.get_html_field()
def dataset_collector( datasets, parent_hid ):
for i, data in enumerate( datasets ):
def dataset_collector( hdas, parent_hid ):
for i, hda in enumerate( hdas ):
if parent_hid is not None:
hid = "%s.%d" % ( parent_hid, i + 1 )
else:
hid = str( data.hid )
if not data.deleted and data.state not in [data.states.ERROR, data.states.DISCARDED] and data.visible:
if self.options and data.get_dbkey() != filter_value:
hid = str( hda.hid )
if not hda.dataset.state in [galaxy.model.Dataset.states.ERROR, galaxy.model.Dataset.states.DISCARDED] and \
hda.visible and \
trans.app.security_agent.allow_action( trans.user, hda.permitted_actions.DATASET_ACCESS, dataset=hda ):
# If we are sending data to an external application, then we need to make sure there are no roles
# associated with the dataset that restrict it's access from "public". We determine this by sending
# None as the user to the allow_action method.
if self.tool.tool_type == 'data_destination':
if not trans.app.security_agent.allow_action( None, hda.permitted_actions.DATASET_ACCESS, dataset=hda ):
continue
if self.options and hda.get_dbkey() != filter_value:
continue
if isinstance( data.datatype, self.formats):
selected = ( value and ( data in value ) )
field.add_option( "%s: %s" % ( hid, data.name[:30] ), data.id, selected )
if isinstance( hda.datatype, self.formats):
selected = ( value and ( hda in value ) )
field.add_option( "%s: %s" % ( hid, hda.name[:30] ), hda.id, selected )
else:
target_ext, converted_dataset = data.find_conversion_destination( self.formats, converter_safe = self.converter_safe( other_values, trans ) )
target_ext, converted_dataset = hda.find_conversion_destination( self.formats, converter_safe = self.converter_safe( other_values, trans ) )
if target_ext:
if converted_dataset:
data = converted_dataset
selected = ( value and ( data in value ) )
field.add_option( "%s: (as %s) %s" % ( hid, target_ext, data.name[:30] ), data.id, selected )
hda = converted_dataset
if not trans.app.security_agent.allow_action( trans.user, trans.app.security_agent.permitted_actions.DATASET_ACCESS, dataset=hda.dataset ):
continue
selected = ( value and ( hda in value ) )
field.add_option( "%s: (as %s) %s" % ( hid, target_ext, hda.name[:30] ), hda.id, selected )
# Also collect children via association object
dataset_collector( data.children, hid )
dataset_collector( history.datasets, None )
dataset_collector( hda.children, hid )
dataset_collector( history.active_datasets, None )
some_data = bool( field.options )
if some_data:
if value is None or len( field.options ) == 1:
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -60,7 +60,7 @@ class ASync( BaseController ):
if STATUS == 'OK':
key = hmac.new( trans.app.config.tool_secret, "%d:%d" % ( data.id, data.history_id), sha ).hexdigest()
if key != data_secret:
return "You do not have permision to alter data %s." % data_id
return "You do not have permission to alter data %s." % data_id
# push the job into the queue
data.state = data.blurb = data.states.RUNNING
log.debug('executing tool %s' % tool.id)
@@ -104,6 +104,7 @@ class ASync( BaseController ):
#history.datasets.add_dataset( data )
data = trans.app.model.HistoryDatasetAssociation( create_dataset = True, extension = GALAXY_TYPE )
trans.app.security_agent.set_dataset_permissions( data.dataset, trans.app.security_agent.history_get_default_permissions( trans.history ) )
data.name = GALAXY_NAME
data.dbkey = GALAXY_BUILD
data.info = GALAXY_INFO
+24 -26
View File
@@ -103,31 +103,30 @@ class DatasetInterface( BaseController ):
@web.expose
def display(self, trans, dataset_id=None, filename=None, **kwd):
"""Catches the dataset id and displays file contents as directed"""
if filename is None or filename.lower() == "index":
try:
data = trans.app.model.HistoryDatasetAssociation.get( dataset_id )
if data:
mime = trans.app.datatypes_registry.get_mimetype_by_extension( data.extension.lower() )
trans.response.set_content_type(mime)
trans.log_event( "Display dataset id: %s" % str(dataset_id) )
try:
return open( data.file_name )
except:
return "This item contains no content"
except:
pass
return "Invalid dataset specified"
else:
#display files from directory here
try:
file_path = os.path.join(trans.app.model.HistoryDatasetAssociation.get( dataset_id ).extra_files_path, filename)
mime, encoding = mimetypes.guess_type(file_path)
if mime is None:
mime = trans.app.datatypes_registry.get_mimetype_by_extension(".".split(file_path)[-1])
data = trans.app.model.HistoryDatasetAssociation.get( dataset_id )
if not data:
raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable( "Invalid reference dataset." )
if trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_ACCESS, dataset = data ):
if filename is None or filename.lower() == "index":
mime = trans.app.datatypes_registry.get_mimetype_by_extension( data.extension.lower() )
trans.response.set_content_type(mime)
return open(file_path)
except:
raise paste.httpexceptions.HTTPNotFound( "File Not Found (%s)." % (filename) )
trans.log_event( "Display dataset id: %s" % str( dataset_id ) )
try:
return open( data.file_name )
except:
raise paste.httpexceptions.HTTPNotFound( "File Not Found (%s)." % ( filename ) )
else:
file_path = os.path.join( data.extra_files_path, filename )
mime, encoding = mimetypes.guess_type( file_path )
if mime is None:
mime = trans.app.datatypes_registry.get_mimetype_by_extension( ".".split( file_path )[-1] )
trans.response.set_content_type( mime )
try:
return open( file_path )
except:
raise paste.httpexceptions.HTTPNotFound( "File Not Found (%s)." % ( filename ) )
else:
return trans.show_error_message( "You are not privileged to access this dataset." )
def _undelete( self, trans, id ):
history = trans.get_history()
@@ -141,7 +140,7 @@ class DatasetInterface( BaseController ):
# Mark undeleted
data.mark_undeleted()
self.app.model.flush()
trans.log_event( "Dataset id %s has been undeleted" % str(id) )
trans.log_event( "Dataset id %s has been undeleted" % str(id) )
return True
return False
@@ -155,7 +154,6 @@ class DatasetInterface( BaseController ):
if self._undelete( trans, id ):
return "OK"
raise "Error undeleting"
@web.expose
def copy_datasets( self, trans, source_dataset_ids = "", target_history_ids = "", new_history_name="", do_copy = False ):
+24
View File
@@ -0,0 +1,24 @@
from galaxy.web.base.controller import *
from galaxy.model.orm import *
import logging
log = logging.getLogger( __name__ )
class Library( BaseController ):
@web.expose
def browse( self, trans, **kwd ):
return trans.fill_template( '/library/browser.mako', libraries=trans.app.model.Library.filter_by( deleted=False ).all() )
index = browse
@web.expose
def import_datasets( self, trans, import_ids=[], **kwd ):
if not import_ids:
return trans.show_error_message( "You must select at least one dataset to import" )
if not isinstance( import_ids, list ):
import_ids = [ import_ids ]
history = trans.get_history()
for id in import_ids:
dataset = trans.app.model.LibraryFolderDatasetAssociation.get( id ).to_history_dataset_association()
history.add_dataset( dataset )
dataset.flush()
history.flush()
return trans.show_ok_message( "%i dataset(s) have been imported in to your history" % len( import_ids ), refresh_frames=['history'] )
+252 -103
View File
@@ -1,15 +1,11 @@
"""
Contains the main interface in the Universe class
"""
from galaxy.web.base.controller import *
import logging, os, sets, string, shutil
import re, socket
from galaxy import util, datatypes, jobs, web, util
import logging, os, sets, string, shutil, urllib, re, socket
from cgi import escape, FieldStorage
import urllib
from galaxy import util, datatypes, jobs, web, util
from galaxy.web.base.controller import *
from galaxy.model.orm import *
log = logging.getLogger( __name__ )
@@ -91,7 +87,7 @@ class RootController( BaseController ):
return trans.fill_template("root/history_item.mako", data=data, hid=hid)
else:
return trans.show_error_message( "Must specify a dataset id.")
@web.json
def history_item_updates( self, trans, ids=None, states=None ):
# Avoid caching
@@ -137,22 +133,25 @@ class RootController( BaseController ):
except:
return "Dataset id '%s' is invalid" %str( id )
if data:
mime = trans.app.datatypes_registry.get_mimetype_by_extension( data.extension.lower() )
trans.response.set_content_type(mime)
if tofile:
fStat = os.stat(data.file_name)
trans.response.headers['Content-Length'] = int(fStat.st_size)
if toext[0:1] != ".":
toext = "." + toext
valid_chars = '.,^_-()[]0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
fname = data.name
fname = ''.join(c in valid_chars and c or '_' for c in fname)[0:150]
trans.response.headers["Content-Disposition"] = "attachment; filename=GalaxyHistoryItem-%s-[%s]%s" % (data.hid, fname, toext)
trans.log_event( "Display dataset id: %s" % str(id) )
try:
return open( data.file_name )
except:
return "This dataset contains no content"
if trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_ACCESS, dataset = data ):
mime = trans.app.datatypes_registry.get_mimetype_by_extension( data.extension.lower() )
trans.response.set_content_type(mime)
if tofile:
fStat = os.stat(data.file_name)
trans.response.headers['Content-Length'] = int(fStat.st_size)
if toext[0:1] != ".":
toext = "." + toext
valid_chars = '.,^_-()[]0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
fname = data.name
fname = ''.join(c in valid_chars and c or '_' for c in fname)[0:150]
trans.response.headers["Content-Disposition"] = "attachment; filename=GalaxyHistoryItem-%s-[%s]%s" % (data.hid, fname, toext)
trans.log_event( "Display dataset id: %s" % str(id) )
try:
return open( data.file_name )
except:
return "This dataset contains no content"
else:
return "You are not privileged to view this dataset."
else:
return "No dataset with id '%s'" % str( id )
@@ -164,9 +163,12 @@ class RootController( BaseController ):
try:
data = self.app.model.HistoryDatasetAssociation.get( parent_id )
if data:
child = data.get_child_by_designation(designation)
child = data.get_child_by_designation( designation )
if child:
return self.display(trans, id=child.id, tofile=tofile, toext=toext)
if trans.app.security_agent.allow_action( trans.user, child.permitted_actions.DATASET_ACCESS, dataset = child ):
return self.display( trans, id=child.id, tofile=tofile, toext=toext )
else:
return "You are not privileged to access this dataset."
except Exception:
pass
return "A child named %s could not be found for data %s" % ( designation, parent_id )
@@ -176,9 +178,12 @@ class RootController( BaseController ):
"""Returns a file in a format that can successfully be displayed in display_app"""
data = self.app.model.HistoryDatasetAssociation.get( id )
if data:
trans.response.set_content_type(data.get_mime())
trans.log_event( "Formatted dataset id %s for display at %s" % ( str(id), display_app ) )
return data.as_display_type(display_app, **kwd)
if trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_ACCESS, dataset = data ):
trans.response.set_content_type( data.get_mime() )
trans.log_event( "Formatted dataset id %s for display at %s" % ( str( id ), display_app ) )
return data.as_display_type( display_app, **kwd )
else:
return "You are not privileged to access this dataset."
else:
return "No data with id=%d" % id
@@ -194,77 +199,111 @@ class RootController( BaseController ):
yield "No data with id=%d" % id
@web.expose
def edit(self, trans, id=None, hid=None, **kwd):
def edit(self, trans, id=None, hid=None, lid=None, **kwd):
"""Returns data directly into the browser. Sets the mime-type according to the extension"""
if hid is not None:
history = trans.get_history()
# TODO: hid handling
data = history.datasets[ int( hid ) - 1 ]
elif id is None:
return trans.show_error_message( "Problem loading dataset id %s with history id %s." % ( str( id ), str( hid ) ) )
else:
elif lid is not None:
data = self.app.model.LibraryFolderDatasetAssociation.get( lid )
elif id is not None:
data = self.app.model.HistoryDatasetAssociation.get( id )
else:
trans.log_event( "Problem loading dataset id %s with history id %s and library id %s." % ( str( id ), str( hid ), str( lid ) ) )
return trans.show_error_message( "Problem loading dataset." )
if data is None:
return trans.show_error_message( "Problem retrieving dataset id %s with history id %s." % ( str( id ), str( hid ) ) )
p = util.Params(kwd, safe=False)
if p.change:
# The user clicked the Save button on the 'Change data type' form
trans.app.datatypes_registry.change_datatype( data, p.datatype )
trans.app.model.flush()
elif p.save:
# The user clicked the Save button on the 'Edit Attributes' form
data.name = p.name
data.info = p.info
trans.log_event( "Problem retrieving dataset id %s with history id %s and library id %s." % ( str( id ), str( hid ), str( lid ) ) )
return trans.show_error_message( "Problem retrieving dataset." )
if id is not None and data.history.user is not None and data.history.user != trans.user:
return trans.show_error_message( "This instance of a dataset (%s) in a history does not belong to you." % ( data.id ) )
if trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_ACCESS, dataset = data ):
p = util.Params(kwd, safe=False)
# The following for loop will save all metadata_spec items
for name, spec in data.metadata.spec.items():
if spec.get("readonly"):
continue
optional = p.get("is_"+name, None)
if optional and optional == 'true':
# optional element... == 'true' actually means it is NOT checked (and therefore ommitted)
setattr(data.metadata, name, None)
else:
setattr( data.metadata, name, spec.unwrap( p.get (name, None) ) )
can_edit_metadata = lid is None or trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_EDIT_METADATA, dataset = data )
if p.change:
# The user clicked the Save button on the 'Change data type' form
if not can_edit_metadata:
return trans.show_error_message( "You are not authorized to change this dataset's metadata." )
trans.app.datatypes_registry.change_datatype( data, p.datatype )
trans.app.model.flush()
elif p.save:
# The user clicked the Save button on the 'Edit Attributes' form
if not can_edit_metadata:
return trans.show_error_message( "You are not authorized to change this dataset's metadata." )
data.name = p.name
data.info = p.info
# The following for loop will save all metadata_spec items
for name, spec in data.datatype.metadata_spec.items():
if spec.get("readonly"):
continue
optional = p.get("is_"+name, None)
if optional and optional == 'true':
# optional element... == 'true' actually means it is NOT checked (and therefore ommitted)
setattr(data.metadata, name, None)
else:
setattr( data.metadata, name, spec.unwrap( p.get (name, None) ) )
data.datatype.after_edit( data )
trans.app.model.flush()
return trans.show_ok_message( "Attributes updated", refresh_frames=['history'] )
elif p.detect:
# The user clicked the Auto-detect button on the 'Edit Attributes' form
for name, spec in data.metadata.spec.items():
# We need to be careful about the attributes we are resetting
if name not in [ 'name', 'info', 'dbkey' ]:
if spec.get( 'default' ):
setattr( data.metadata, name, spec.unwrap( spec.get( 'default' ) ) )
data.datatype.set_meta( data )
data.datatype.after_edit( data )
trans.app.model.flush()
return trans.show_ok_message( "Attributes updated", refresh_frames=['history'] )
elif p.convert_data:
"""The user clicked the Convert button on the 'Convert to new format' form"""
target_type = kwd.get("target_type", None)
if target_type:
msg = data.datatype.convert_dataset(trans, data, target_type)
return trans.show_ok_message( msg, refresh_frames=['history'] )
data.datatype.before_edit( data )
if "dbkey" in data.datatype.metadata_spec and not data.metadata.dbkey:
# Copy dbkey into metadata, for backwards compatability
# This looks like it does nothing, but getting the dbkey
# returns the metadata dbkey unless it is None, in which
# case it resorts to the old dbkey. Setting the dbkey
# sets it properly in the metadata
data.metadata.dbkey = data.dbkey
# let's not overwrite the imported datatypes module with the variable datatypes?
### the built-in 'id' is overwritten in lots of places as well
ldatatypes = [x for x in trans.app.datatypes_registry.datatypes_by_extension.iterkeys()]
ldatatypes.sort()
trans.log_event( "Opened edit view on dataset %s" % str(id) )
return trans.fill_template( "/dataset/edit_attributes.mako", data=data,
datatypes=ldatatypes, err=None )
data.datatype.after_edit( data )
trans.app.model.flush()
return trans.show_ok_message( "Attributes updated", refresh_frames=['history'] )
elif p.detect:
# The user clicked the Auto-detect button on the 'Edit Attributes' form
if not can_edit_metadata:
return trans.show_error_message( "You are not authorized to change this dataset's metadata." )
for name, spec in data.metadata.spec.items():
# We need to be careful about the attributes we are resetting
if name not in [ 'name', 'info', 'dbkey' ]:
if spec.get( 'default' ):
setattr( data.metadata, name, spec.unwrap( spec.get( 'default' ) ) )
data.datatype.set_meta( data )
data.datatype.after_edit( data )
trans.app.model.flush()
return trans.show_ok_message( "Attributes updated", refresh_frames=['history'] )
elif p.convert_data:
if lid is not None:
return trans.show_error_message( "Data in the library cannot be converted. Please import it to a history and covert it." )
"""The user clicked the Convert button on the 'Convert to new format' form"""
if not can_edit_metadata:
return trans.show_error_message( "You are not authorized to change this dataset's metadata." )
target_type = kwd.get("target_type", None)
if target_type:
msg = data.datatype.convert_dataset(trans, data, target_type)
return trans.show_ok_message( msg, refresh_frames=['history'] )
elif p.update_roles:
if not trans.user:
return trans.show_error_message( "You must be logged in if you want to change permissions." )
if trans.app.security_agent.allow_action( trans.user, data.dataset.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset = data.dataset ):
permissions = {}
for k, v in trans.app.model.Dataset.permitted_actions.items():
in_roles = p.get( k + '_in', [] )
if not isinstance( in_roles, list ):
in_roles = [ in_roles ]
in_roles = [ trans.app.model.Role.get( x ) for x in in_roles ]
permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles
trans.app.security_agent.set_dataset_permissions( data.dataset, permissions )
data.dataset.refresh()
else:
return trans.show_error_message( "You are not authorized to change this dataset's permissions" )
data.datatype.before_edit( data )
if "dbkey" in data.datatype.metadata_spec and not data.metadata.dbkey:
# Copy dbkey into metadata, for backwards compatability
# This looks like it does nothing, but getting the dbkey
# returns the metadata dbkey unless it is None, in which
# case it resorts to the old dbkey. Setting the dbkey
# sets it properly in the metadata
data.metadata.dbkey = data.dbkey
# let's not overwrite the imported datatypes module with the variable datatypes?
### the built-in 'id' is overwritten in lots of places as well
ldatatypes = [x for x in trans.app.datatypes_registry.datatypes_by_extension.iterkeys()]
ldatatypes.sort()
trans.log_event( "Opened edit view on dataset %s" % str(id) )
return trans.fill_template( "/dataset/edit_attributes.mako", data=data,
datatypes=ldatatypes, err=None )
else:
return trans.show_error_message( "You do not have permission to edit this dataset's (%s) attributes." % id )
@web.expose
def delete( self, trans, id = None, show_deleted_on_refresh = False, **kwd):
@@ -323,6 +362,8 @@ class RootController( BaseController ):
self.app.job_stop_queue.put( data.creating_job_associations[0].job )
except IndexError:
pass # upload tool will cause this since it doesn't have a job
else:
return "Dataset id '%s' is invalid" %str( id )
return "OK"
## ---- History management -----------------------------------------------
@@ -352,8 +393,13 @@ class RootController( BaseController ):
if history:
if history.user_id != None and user:
assert user.id == history.user_id, "History does not belong to current user"
history_names.append(history.name)
# Delete DefaultHistoryPermissions
for dhp in history.default_permissions:
dhp.delete()
dhp.flush()
# Mark history as deleted in db
history.deleted = True
history_names.append(history.name)
# If deleting the current history, make a new current.
if history == trans.get_history():
trans.new_history()
@@ -402,6 +448,42 @@ class RootController( BaseController ):
errors.append( "You must select at least one history to undelete." )
return self.history_available( trans, id=','.join( id ), show_deleted=True, ok_msg = ok_msg, error_msg = " ".join( errors ) )
@web.expose
def history_undelete( self, trans, id=[], **kwd):
"""Undeletes a list of histories, ensures that histories are owned by current user"""
history_names = []
errors = []
ok_msg = ""
if id:
if not isinstance( id, list ):
id = id.split( "," )
user = trans.get_user()
for hid in id:
try:
int( hid )
except:
errors.append( "Invalid history: %s" % str( hid ) )
continue
history = self.app.model.History.get( hid )
if history:
if history.user != user:
errors.append( "History does not belong to current user." )
continue
if history.purged:
errors.append( "History has already been purged and can not be undeleted." )
continue
history_names.append( history.name )
history.deleted = False
else:
errors.append( "Not able to find history %s." % str( hid ) )
trans.log_event( "History id %s marked as undeleted" % str(hid) )
self.app.model.flush()
if history_names:
ok_msg = "Histories (%s) have been undeleted." % ", ".join( history_names )
else:
errors.append( "You must select at least one history to undelete." )
return self.history_available( trans, id=','.join( id ), show_deleted=True, ok_msg = ok_msg, error_msg = " ".join( errors ) )
@web.expose
def clear_history( self, trans ):
"""Clears the history for a user"""
@@ -429,14 +511,45 @@ class RootController( BaseController ):
if not email:
return trans.fill_template("/history/share.mako", histories=histories, email=email, send_to_err=send_to_err)
user = trans.get_user()
send_to_user = trans.app.model.User.get_by( email = email )
send_to_user = trans.app.model.User.filter_by( email=email ).first()
p = util.Params( kwd )
if p.action and p.action == "no_share":
trans.response.send_redirect( url_for( action='history_options' ) )
if not send_to_user:
send_to_err = "No such user"
elif user.email == email:
send_to_err = "You can't send histories to yourself"
else:
# if we're not checking or changing permissions, skip this step
if not p.action or ( p.action and p.action != 'share' ):
# ugly
can_change = {}
cannot_change = {}
for history in histories:
for hda in history.active_datasets:
if not trans.app.security_agent.allow_action( send_to_user, trans.app.security_agent.permitted_actions.DATASET_ACCESS, dataset=hda ):
if trans.app.security_agent.allow_action( user, trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset=hda ) and \
not hda.dataset.library_associations:
# don't change perms on datasets that exist in the library.
if p.action and p.action == "private":
trans.app.security_agent.privately_share_dataset( hda.dataset, users=[ user, send_to_user ] )
elif p.action and p.action == "public":
trans.app.security_agent.set_dataset_permissions( hda.dataset, { trans.app.security_agent.permitted_actions.DATASET_ACCESS : [] } )
elif history not in can_change:
can_change[history] = [ hda ]
else:
can_change[history].append( hda )
else:
if p.action and p.action in [ "private", "public" ]:
pass # don't change stuff that the user doesn't have permission to change
elif history not in cannot_change:
cannot_change[history] = [ hda ]
else:
cannot_change[history].append( hda )
if can_change or cannot_change:
return trans.fill_template("/history/share.mako", histories=histories, email=email, send_to_err=send_to_err, can_change=can_change, cannot_change=cannot_change)
for history in histories:
new_history = history.copy()
new_history = history.copy( target_user=send_to_user )
new_history.name = history.name+" from "+user.email
new_history.user_id = send_to_user.id
trans.log_event( "History share, id: %s, name: '%s': to new id: %s" % (str(history.id), history.name, str(new_history.id)) )
@@ -483,12 +596,12 @@ class RootController( BaseController ):
if user:
if import_history.user_id == user.id:
return trans.show_error_message( "You cannot import your own history.")
new_history = import_history.copy()
new_history = import_history.copy( target_user=trans.user )
new_history.name = "imported: "+new_history.name
new_history.user_id = user.id
galaxy_session = trans.get_galaxy_session()
try:
association = trans.app.model.GalaxySessionToHistoryAssociation.selectone_by( session_id=galaxy_session.id, history_id=new_history.id )
association = trans.app.model.GalaxySessionToHistoryAssociation.filter_by( session_id=galaxy_session.id, history_id=new_history.id ).first()
except:
association = None
new_history.add_galaxy_session( galaxy_session, association=association )
@@ -505,7 +618,7 @@ class RootController( BaseController ):
new_history.user_id = None
galaxy_session = trans.get_galaxy_session()
try:
association = trans.app.model.GalaxySessionToHistoryAssociation.selectone_by( session_id=galaxy_session.id, history_id=new_history.id )
association = trans.app.model.GalaxySessionToHistoryAssociation.filter_by( session_id=galaxy_session.id, history_id=new_history.id ).first()
except:
association = None
new_history.add_galaxy_session( galaxy_session, association=association )
@@ -530,7 +643,7 @@ class RootController( BaseController ):
if new_history:
galaxy_session = trans.get_galaxy_session()
try:
association = trans.app.model.GalaxySessionToHistoryAssociation.selectone_by( session_id=galaxy_session.id, history_id=new_history.id )
association = trans.app.model.GalaxySessionToHistoryAssociation.filter_by( session_id=galaxy_session.id, history_id=new_history.id ).first()
except:
association = None
new_history.add_galaxy_session( galaxy_session, association=association )
@@ -590,11 +703,17 @@ class RootController( BaseController ):
return trans.show_message( "<p>%s" % change_msg, refresh_frames=['history'] )
@web.expose
def history_add_to( self, trans, history_id=None, file_data=None, name="Data Added to History",info=None,ext="txt",dbkey="?",**kwd ):
def history_add_to( self, trans, history_id=None, file_data=None, name="Data Added to History",info=None,ext="txt",dbkey="?",copy_access_from=None,**kwd ):
"""Adds a POSTed file to a History"""
try:
history = trans.app.model.History.get( history_id )
data = trans.app.model.HistoryDatasetAssociation( name = name, info = info, extension = ext, dbkey = dbkey, create_dataset = True )
if copy_access_from:
copy_access_from = trans.app.model.HistoryDatasetAssociation.get( copy_access_from )
trans.app.security_agent.copy_dataset_permissions( copy_access_from.dataset, data.dataset )
else:
permissions = trans.app.security_agent.history_get_default_permissions( history )
trans.app.security_agent.set_dataset_permissions( data.dataset, permissions )
data.flush()
data_file = open( data.file_name, "wb" )
file_data.file.seek( 0 )
@@ -611,9 +730,31 @@ class RootController( BaseController ):
data.flush()
trans.log_event("Added dataset %d to history %d" %(data.id, trans.history.id))
return trans.show_ok_message("Dataset "+str(data.hid)+" added to history "+str(history_id)+".")
except:
except Exception, e:
trans.log_event( "Failed to add dataset to history: %s" % ( e ) )
return trans.show_error_message("Adding File to History has Failed")
@web.expose
def history_set_default_permissions( self, trans, **kwd ):
"""Sets the user's default permissions for the current history"""
if trans.user:
if 'update_roles' in kwd:
history = trans.get_history()
p = util.Params( kwd )
permissions = {}
for k, v in trans.app.model.Dataset.permitted_actions.items():
in_roles = p.get( k + '_in', [] )
if not isinstance( in_roles, list ):
in_roles = [ in_roles ]
in_roles = [ trans.app.model.Role.get( x ) for x in in_roles ]
permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles
trans.app.security_agent.history_set_default_permissions( history, permissions )
return trans.show_ok_message( 'Default history permissions have been changed.' )
return trans.fill_template( 'history/permissions.mako' )
else:
#user not logged in, history group must be only public
return trans.show_error_message( "You must be logged in to change a history's default permissions." )
@web.expose
def dataset_make_primary( self, trans, id=None):
"""Copies a dataset and makes primary"""
@@ -630,7 +771,7 @@ class RootController( BaseController ):
return trans.show_error_message( "<p>Failed to make secondary dataset primary.</p>" )
@web.expose
def masthead( self, trans ):
def masthead( self, trans, active_view=None ):
brand = trans.app.config.get( "brand", "" )
if brand:
brand ="<span class='brand'>/%s</span>" % brand
@@ -638,8 +779,15 @@ class RootController( BaseController ):
bugs_email = trans.app.config.get( "bugs_email", "mailto:galaxy-bugs@bx.psu.edu" )
blog_url = trans.app.config.get( "blog_url", "http://g2.trac.bx.psu.edu/blog" )
screencasts_url = trans.app.config.get( "screencasts_url", "http://g2.trac.bx.psu.edu/wiki/ScreenCasts" )
admin_user = "false"
admin_users = trans.app.config.get( "admin_users", "" ).split( "," )
user = trans.get_user()
if user:
user_email = trans.get_user().email
if user_email in admin_users:
admin_user = "true"
return trans.fill_template( "/root/masthead.mako", brand=brand, wiki_url=wiki_url,
blog_url=blog_url,bugs_email=bugs_email, screencasts_url=screencasts_url )
blog_url=blog_url,bugs_email=bugs_email, screencasts_url=screencasts_url, admin_user=admin_user, active_view=active_view )
@web.expose
def dataset_errors( self, trans, id=None, **kwd ):
@@ -682,3 +830,4 @@ class RootController( BaseController ):
@web.expose
def generate_error( self, trans ):
raise Exception( "Fake error!" )
+27 -6
View File
@@ -1,9 +1,9 @@
"""
Contains the user interface in the Universe class
"""
from galaxy.web.base.controller import *
from galaxy.model.orm import *
from galaxy import util
import logging, os, string
from random import choice
@@ -53,7 +53,7 @@ class User( BaseController ):
email_err = "Please enter a real email address"
elif len( email) > 255:
email_err = "Email address exceeds maximum allowable length"
elif len( trans.app.model.User.select_by( email=email ) ) > 0:
elif trans.app.model.User.filter_by( email=email ).first():
email_err = "User with that email already exists"
elif email != conf_email:
conf_email_err = "Email addresses do not match."
@@ -73,7 +73,7 @@ class User( BaseController ):
email_error = password_error = None
# Attempt login
if email or password:
user = trans.app.model.User.get_by( email = email )
user = trans.app.model.User.filter_by( email=email ).first()
if not user:
email_error = "No such user"
elif user.external:
@@ -108,7 +108,7 @@ class User( BaseController ):
email_error = "Please enter a real email address"
elif len( email) > 255:
email_error = "Email address exceeds maximum allowable length"
elif len( trans.app.model.User.select_by( email=email ) ) > 0:
elif trans.app.model.User.filter_by( email=email ).first():
email_error = "User with that email already exists"
elif len( password ) < 6:
password_error = "Please use a password of at least 6 characters"
@@ -118,6 +118,7 @@ class User( BaseController ):
user = trans.app.model.User( email=email )
user.set_password_cleartext( password )
user.flush()
trans.app.security_agent.setup_new_user( user )
trans.set_user( user )
trans.ensure_valid_galaxy_session()
"""
@@ -143,7 +144,7 @@ class User( BaseController ):
@web.expose
def reset_password(self, trans, email=None, **kwd):
error = ''
reset_user = trans.app.model.User.get_by( email = email )
reset_user = trans.app.model.User.filter_by( email=email ).first()
user = trans.get_user()
if reset_user:
if user and user.id != reset_user.id:
@@ -166,3 +167,23 @@ class User( BaseController ):
return trans.show_form(
web.FormBuilder( web.url_for(), "Reset Password", submit_text="Submit" )
.add_text( "email", "Email", value=email, error=error ) )
@web.expose
def set_default_permissions( self, trans, **kwd ):
"""Sets the user's default permissions for the new histories"""
if trans.user:
if 'update_roles' in kwd:
p = util.Params( kwd )
permissions = {}
for k, v in trans.app.model.Dataset.permitted_actions.items():
in_roles = p.get( k + '_in', [] )
if not isinstance( in_roles, list ):
in_roles = [ in_roles ]
in_roles = [ trans.app.model.Role.get( x ) for x in in_roles ]
permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles
trans.app.security_agent.user_set_default_permissions( trans.user, permissions )
return trans.show_ok_message( 'Default new history permissions have been changed.' )
return trans.fill_template( 'user/permissions.mako' )
else:
# User not logged in, history group must be only public
return trans.show_error_message( "You must be logged in to change your default permitted actions." )
+18 -15
View File
@@ -17,8 +17,12 @@ from galaxy.model.mapping import desc
class WorkflowController( BaseController ):
@web.expose
@web.require_login( "use Galaxy workflows" )
def index( self, trans ):
return trans.fill_template( "workflow/index.mako" )
@web.expose
@web.require_login( "use Galaxy workflows" )
def list( self, trans ):
"""
Render workflow main page (management of existing workflows)
"""
@@ -33,7 +37,7 @@ class WorkflowController( BaseController ):
.filter( model.StoredWorkflow.c.deleted == False ) \
.order_by( desc( model.StoredWorkflow.c.update_time ) ) \
.all()
return trans.fill_template( "workflow/index.mako",
return trans.fill_template( "workflow/list.mako",
workflows = workflows,
shared_by_others = shared_by_others )
@@ -44,7 +48,7 @@ class WorkflowController( BaseController ):
# Load workflow from database
stored = get_stored_workflow( trans, id )
if email:
other = model.User.get_by( email=email )
other = model.User.filter_by( email=email ).first()
if not other:
mtype = "error"
msg = ( "User '%s' does not exist" % email )
@@ -61,10 +65,10 @@ class WorkflowController( BaseController ):
share.stored_workflow = stored
share.user = other
session = trans.sa_session
session.save( share )
session.save_or_update( share )
session.flush()
trans.set_message( "Workflow '%s' shared with user '%s'" % ( stored.name, other.email ) )
return self.index( trans )
return self.list( trans )
return trans.fill_template( "workflow/share.mako",
message = msg,
messagetype = mtype,
@@ -79,7 +83,7 @@ class WorkflowController( BaseController ):
stored.name = new_name
trans.sa_session.flush()
trans.set_message( "Workflow renamed to '%s'." % new_name )
return self.index( trans )
return self.list( trans )
else:
return form( url_for( id=trans.security.encode_id(stored.id) ), "Rename workflow", submit_text="Rename" ) \
.add_text( "new_name", "Workflow Name", value=stored.name )
@@ -104,11 +108,11 @@ class WorkflowController( BaseController ):
new_stored.user = user
# Persist
session = trans.sa_session
session.save( new_stored )
session.save_or_update( new_stored )
session.flush()
# Display the management page
trans.set_message( 'Clone created with name "%s"' % new_stored.name )
return self.index( trans )
return self.list( trans )
@web.expose
@web.require_login( "create workflows" )
@@ -129,11 +133,11 @@ class WorkflowController( BaseController ):
stored_workflow.latest_workflow = workflow
# Persist
session = trans.sa_session
session.save( stored_workflow )
session.save_or_update( stored_workflow )
session.flush()
# Display the management page
trans.set_message( "Workflow '%s' created" % stored_workflow.name )
return self.index( trans )
return self.list( trans )
else:
return form( url_for(), "Create new workflow", submit_text="Create" ) \
.add_text( "workflow_name", "Workflow Name", value="Unnamed workflow" )
@@ -150,7 +154,7 @@ class WorkflowController( BaseController ):
stored.flush()
# Display the management page
trans.set_message( "Workflow '%s' deleted" % stored.name )
return self.index( trans )
return self.list( trans )
@web.expose
@web.require_login( "edit workflows" )
@@ -306,7 +310,7 @@ class WorkflowController( BaseController ):
workflow.stored_workflow = stored
stored.latest_workflow = workflow
# Persist
trans.sa_session.save( stored )
trans.sa_session.save_or_update( stored )
trans.sa_session.flush()
# Return something informative
errors = []
@@ -431,11 +435,10 @@ class WorkflowController( BaseController ):
stored.name = workflow_name
workflow.stored_workflow = stored
stored.latest_workflow = workflow
trans.sa_session.save( stored )
trans.sa_session.save_or_update( stored )
trans.sa_session.flush()
# Index page with message
trans.template_context['message'] = "Workflow '%s' created" % workflow_name
return self.index( trans )
return trans.show_message( "Workflow '%s' created from current history." % workflow_name )
## return trans.show_ok_message( "<p>Workflow '%s' created.</p><p><a target='_top' href='%s'>Click to load in workflow editor</a></p>"
## % ( workflow_name, web.url_for( action='editor', id=trans.security.encode_id(stored.id) ) ) )
+19 -11
View File
@@ -24,7 +24,7 @@ pkg_resources.require( "Mako" )
import mako.template
import mako.lookup
pkg_resources.require( "sqlalchemy>=0.3" )
pkg_resources.require( "SQLAlchemy >= 0.4" )
from sqlalchemy import desc
import logging
@@ -172,7 +172,7 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
if secure_id:
session_key = self.security.decode_session_key( secure_id )
try:
galaxy_session = self.app.model.GalaxySession.selectone_by( session_key=session_key )
galaxy_session = self.app.model.GalaxySession.filter_by( session_key=session_key ).first()
if galaxy_session and galaxy_session.is_valid and galaxy_session.current_history_id:
history = self.app.model.History.get( galaxy_session.current_history_id )
if history and not history.deleted:
@@ -197,9 +197,10 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
return self.new_history()
return self.__history
def new_history( self ):
history = self.app.model.History()
history = self.app.model.History( user = self.user )
# Make sure we have an id
history.flush()
self.app.security_agent.history_set_default_permissions( history )
# Immediately associate the new history with self
self.__history = history
# Make sure we have a valid session to associate with the new history
@@ -216,7 +217,7 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
galaxy_session.user_id = self.user.id
try:
# See if we have already associated the history with the session
association = self.app.model.GalaxySessionToHistoryAssociation.select_by( session_id=galaxy_session.id, history_id=history.id )[0]
association = self.app.model.GalaxySessionToHistoryAssociation.filter_by( session_id=galaxy_session.id, history_id=history.id ).first()
except:
association = None
history.add_galaxy_session( galaxy_session, association=association )
@@ -265,12 +266,14 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
"""Return the user in $HTTP_REMOTE_USER and create if necessary"""
# remote_user middleware ensures HTTP_REMOTE_USER exists
try:
user = self.app.model.User.selectone_by( email=self.environ[ 'HTTP_REMOTE_USER' ] )
user = self.app.model.User.filter_by( email=self.environ[ 'HTTP_REMOTE_USER' ] ).first()
assert user is not None
except:
user = self.app.model.User( email=self.environ[ 'HTTP_REMOTE_USER' ] )
user.set_password_cleartext( 'external' )
user.external = True
user.flush()
self.app.security_agent.setup_new_user( user )
self.log_event( "Automatically created account '%s'" % user.email )
return user
def get_cookie_user( self ):
@@ -281,7 +284,7 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
if secure_id:
session_key = self.security.decode_session_key( secure_id )
try:
galaxy_session = self.app.model.GalaxySession.selectone_by( session_key=session_key )
galaxy_session = self.app.model.GalaxySession.filter_by( session_key=session_key ).first()
if galaxy_session and galaxy_session.is_valid and galaxy_session.user_id:
user = self.app.model.User.get( galaxy_session.user_id )
if user:
@@ -321,7 +324,7 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
session_key = self.security.decode_session_key( secure_id )
try:
# Retrive the galaxy_session id via the unique session_key
galaxy_session = self.app.model.GalaxySession.selectone_by( session_key=session_key )
galaxy_session = self.app.model.GalaxySession.filter_by( session_key=session_key ).first()
if galaxy_session and galaxy_session.is_valid:
self.__galaxy_session = galaxy_session
except:
@@ -382,7 +385,7 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
if self.history is not None:
# See if we have already associated the session with the history
try:
association = self.app.model.GalaxySessionToHistoryAssociation.select_by( session_id=galaxy_session.id, history_id=self.history.id )[0]
association = self.app.model.GalaxySessionToHistoryAssociation.filter_by( session_id=galaxy_session.id, history_id=self.history.id ).first()
except:
association = None
galaxy_session.add_history( self.history, association=association )
@@ -431,6 +434,10 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
galaxy_session.flush()
self.__galaxy_session = galaxy_session
if history is not None and user is not None:
if not history.user:
# This user will now acquire previously non-owned history, so set permitted actions to user's default
history.user = user
self.app.security_agent.history_set_default_permissions( history, dataset=True, bypass_manage_permission=True )
history.user_id = user.id
history.flush()
self.__history = history
@@ -522,8 +529,8 @@ class FormBuilder( object ):
self.action = action
self.submit_text = submit_text
self.inputs = []
def add_input( self, type, name, label, value=None, error=None, help=None ):
self.inputs.append( FormInput( type, label, name, value, error, help ) )
def add_input( self, type, name, label, value=None, error=None, help=None, use_label=True ):
self.inputs.append( FormInput( type, label, name, value, error, help, use_label ) )
return self
def add_text( self, name, label, value=None, error=None, help=None ):
return self.add_input( 'text', label, name, value, error, help )
@@ -534,13 +541,14 @@ class FormInput( object ):
"""
Simple class describing a form input element
"""
def __init__( self, type, name, label, value=None, error=None, help=None ):
def __init__( self, type, name, label, value=None, error=None, help=None, use_label=True ):
self.type = type
self.name = name
self.label = label
self.value = value
self.error = error
self.help = help
self.use_label = use_label
class FormData( object ):
"""
@@ -5,7 +5,7 @@ import calendar
from galaxy.webapps.reports.base.controller import *
import galaxy.model
import pkg_resources
pkg_resources.require( "sqlalchemy>=0.3" )
pkg_resources.require( "SQLAlchemy >= 0.4" )
import sqlalchemy as sa
import logging
log = logging.getLogger( __name__ )
@@ -2,8 +2,9 @@ import operator, os
from datetime import datetime, timedelta
from galaxy.webapps.reports.base.controller import *
import pkg_resources
pkg_resources.require( "sqlalchemy>=0.3" )
from sqlalchemy import eagerload, desc
pkg_resources.require( "SQLAlchemy >= 0.4" )
from sqlalchemy.orm import eagerload
from sqlalchemy import desc
import logging
log = logging.getLogger( __name__ )
@@ -2,8 +2,9 @@ from datetime import *
import calendar
from galaxy.webapps.reports.base.controller import *
import galaxy.model
from galaxy.model.orm import *
import pkg_resources
pkg_resources.require( "sqlalchemy>=0.3" )
pkg_resources.require( "SQLAlchemy >= 0.4" )
import sqlalchemy as sa
import logging
log = logging.getLogger( __name__ )
@@ -13,15 +14,7 @@ class Users( BaseController ):
def registered_users( self, trans, **kwd ):
params = util.Params( kwd )
msg = ''
engine = galaxy.model.mapping.metadata.engine
s = """
SELECT
count(id) AS num_users
FROM
galaxy_user
"""
rows = engine.text( s ).execute().fetchall()
num_users = rows[0].num_users
num_users = galaxy.model.User.query().count()
return trans.fill_template( 'registered_users.mako', num_users=num_users, msg=msg )
@web.expose
def registered_users_per_month( self, trans, **kwd ):
+2 -2
View File
@@ -13,8 +13,8 @@ from galaxy import eggs
import galaxy.model.mapping
import pkg_resources
pkg_resources.require( "sqlalchemy>=0.3" )
from sqlalchemy import eagerload
pkg_resources.require( "SQLAlchemy >= 0.4" )
from sqlalchemy.orm import eagerload
assert sys.version_info[:2] >= ( 2, 4 )
Binary file not shown.

After

Width:  |  Height:  |  Size: 593 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 688 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 446 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 31 KiB

+20 -1
View File
@@ -283,6 +283,11 @@ table.colored tr
background: $table_row_bg;
}
table.colored tr.odd_row
{
background: $odd_row_bg;
}
div.debug
{
margin: 10px;
@@ -350,6 +355,16 @@ ul.toolParameterExpandableCollapsable
list-style: none;
}
ul.manage-table-actions {
float: right;
margin-top: -2.5em;
}
ul.manage-table-actions li {
display: block;
float: left;
margin-left: 0.5em;
}
.action-button {
background: #eeeeee;
color: #333;
@@ -416,4 +431,8 @@ div.popupmenu-item:hover {
.popup-arrow:hover {
color: black;
}
}
div.permissionContainer {
padding-left: 20px;
}
+22 -2
View File
@@ -16,6 +16,7 @@ img
border: 0;
}
a:link, a:visited, a:active
{
color: #303030;
@@ -282,6 +283,11 @@ table.colored tr
background: white;
}
table.colored tr.odd_row
{
background: #DADFEF;
}
div.debug
{
margin: 10px;
@@ -293,7 +299,7 @@ div.debug
div.odd_row
{
background: #FFFF99;
background: #DADFEF;
}
#footer {
@@ -348,6 +354,16 @@ ul.toolParameterExpandableCollapsable
list-style: none;
}
ul.manage-table-actions {
float: right;
margin-top: -2.5em;
}
ul.manage-table-actions li {
display: block;
float: left;
margin-left: 0.5em;
}
.action-button {
background: #eeeeee;
color: #333;
@@ -414,4 +430,8 @@ div.popupmenu-item:hover {
.popup-arrow:hover {
color: black;
}
}
div.permissionContainer {
padding-left: 20px;
}
+8 -1
View File
@@ -80,6 +80,13 @@ div.historyItem-running
*/
}
div.historyItem-noPermission
{
filter: alpha(opacity=60);
-moz-opacity: .60;
opacity: .60;
}
div.historyItem-queued
{
}
@@ -101,4 +108,4 @@ pre.peek th
{
color: white;
background: #023858;
}
}
+65
View File
@@ -0,0 +1,65 @@
.libraryRow {
background-color: #d2c099;
}
.datasetHighlighted {
background-color: #C1C9E5;
}
div.historyItemBody {
padding: 4px 4px 2px 4px;
}
li.folderRow,
li.datasetRow
{
border-top: solid 1px #ddd;
}
li.folderRow:hover,
li.datasetRow:hover
{
background-color: #C1C9E5;
}
img.expanderIcon {
padding-right: 4px;
}
input.datasetCheckbox,
li, ul
{
padding: 0;
margin: 0;
}
.rowTitle
{
padding: 2px;
}
ul {
list-style: none;
}
.libraryTitle th {
text-align: left;
}
pre.peek
{
background: white;
color: black;
width: 100%;
overflow: auto;
}
pre.peek th
{
color: white;
background: #023858;
}
a.expandLink {
text-decoration: none;
}
+36
View File
@@ -7,6 +7,7 @@ body
margin: 3px;
margin-right: 5px;
margin-left: 5px;
overflow: hidden;
}
div.pageTitle
@@ -24,3 +25,38 @@ a:link, a:visited, a:active
{
color: #eeeeee;
}
#tab-bar-bottom
{
z-index: -1;
position:absolute;
top:27px; left: 0;
width: 100%;
height: 100%;
background: #222532;
}
span.link-group
{
margin: 0;
padding: 0;
display: inline;
padding-bottom: 10px;
margin-bottom: -10px;
}
span.link-group span
{
margin: 0;
padding: 0;
display: inline;
}
span.link-group span.active-link
{
background: #222532;
padding-left: 3px; padding-right: 3px;
margin-left: -3px; margin-right: -3px;
padding-bottom: 10px;
margin-bottom: -10px;
}
+47 -7
View File
@@ -45,6 +45,19 @@ body
font-weight: bold;
}
#messagebox
{
position:absolute;
top:33px;
left:0;
width:100%;
height:24px !important;
overflow: hidden;
border-bottom: solid #999 1px;
font-size: 90%;
}
#left, #left-border, #center, #right-border, #right
{
position: absolute;
@@ -146,7 +159,6 @@ table.column-layout
.unified-panel-header {
height: 2em;
overflow: hidden;
z-index: 1000;
background: #cccccc;
background-image: url(panel_header_bg.png);
@@ -160,18 +172,17 @@ table.column-layout
color: #333;
font-weight: bold;
}
.unified-panel-header-inner {
padding-top: 0.45em;
}
.menu-bg {
background: #C1C9E5 url(menu_bg.png) top repeat-x;
}
div.unified-panel-body {
position: absolute;
z-index: 1;
top: 2em;
bottom: 0;
width: 100%;
@@ -197,6 +208,7 @@ div.unified-panel-body {
color: black;
background: #aaaaaa;
}
.panel-header-button:active {
color: white;
background: #aaaaaa;
@@ -240,7 +252,7 @@ div.popupmenu-item:hover {
}
#overlay {
position: absolute;
position: fixed;
top: 0; left: 0; width: 100%; height: 100%;
z-index: 20000;
}
@@ -271,6 +283,34 @@ div.popupmenu-item:hover {
padding: 5px;
}
.dialog-box body {
overflow: scroll;
.panel-error-message, .panel-warning-message, .panel-done-message, .panel-info-message
{
height: 24px;
line-height: 24px;
color: #303030;
padding: 0px;
padding-left: 26px;
background-color: #FFCCCC;
background-image: url(error_small.png);
background-repeat: no-repeat;
background-position: 6px 50%;
}
.panel-warning-message
{
background-image: url(warn_small.png);
background-color: #FFFFCC;
}
.panel-done-message
{
background-image: url(done_small.png);
background-color: #CCFFCC;
}
.panel-info-message
{
background-image: url(info_small.png);
background-color: #CCCCFF;
}
+2 -1
View File
@@ -15,7 +15,7 @@ form_border=#d8b365
#form_body_bg=#FFFFFF
form_body_bg_top=#FFFFFF
form_body_bg_bottom=#FFFFFF
odd_row_bg=#FFFF99
odd_row_bg=#DADFEF
# Messages
error_message_border=#AA6666
error_message_bg=#FFCCCC
@@ -48,6 +48,7 @@ masthead_bg=#2C3143
masthead_text=#eeeeee
masthead_bg_hatch=-
masthead_link=#eeeeee
masthead_active_tab_bg=#222532
# ---- Layout -----------------------------------------------------------------
# Overall background color (including space between panels)
layout_bg=#eee
+8 -1
View File
@@ -80,6 +80,13 @@ div.historyItem-running
*/
}
div.historyItem-noPermission
{
filter: alpha(opacity=60);
-moz-opacity: .60;
opacity: .60;
}
div.historyItem-queued
{
}
@@ -101,4 +108,4 @@ pre.peek th
{
color: white;
background: $peek_table_header;
}
}
+65
View File
@@ -0,0 +1,65 @@
.libraryRow {
background-color: $form_title_bg_bottom;
}
.datasetHighlighted {
background-color: $menu_bg_over;
}
div.historyItemBody {
padding: 4px 4px 2px 4px;
}
li.folderRow,
li.datasetRow
{
border-top: solid 1px #ddd;
}
li.folderRow:hover,
li.datasetRow:hover
{
background-color: $menu_bg_over;
}
img.expanderIcon {
padding-right: 4px;
}
input.datasetCheckbox,
li, ul
{
padding: 0;
margin: 0;
}
.rowTitle
{
padding: 2px;
}
ul {
list-style: none;
}
.libraryTitle th {
text-align: left;
}
pre.peek
{
background: white;
color: black;
width: 100%;
overflow: auto;
}
pre.peek th
{
color: white;
background: $peek_table_header;
}
a.expandLink {
text-decoration: none;
}
+8 -2
View File
@@ -1,5 +1,9 @@
#!/usr/bin/env python
from galaxy import eggs
import pkg_resources
pkg_resources.require("Cheetah")
import sys
from Cheetah.Template import Template
import string
@@ -13,8 +17,8 @@ def run( cmd ):
templates = [ ( "base.css.tmpl", "base.css" ),
( "panel_layout.css.tmpl", "panel_layout.css" ),
( "panel_layout_ie.css.tmpl", "panel_layout_ie.css" ),
( "masthead.css.tmpl", "masthead.css"),
( "library.css.tmpl", "library.css"),
( "history.css.tmpl", "history.css" ),
( "tool_menu.css.tmpl", "tool_menu.css" ),
( "reset.css.tmpl", "reset.css" ) ]
@@ -64,7 +68,8 @@ for line in open( vars ):
for input, output in templates:
print input ,"->", output
open( os.path.join( out_dir, output ), "w" ).write( str( Template( file=input, searchList=[context] ) ) )
"""
for rule, output in images:
t = string.Template( rule ).substitute( context )
print t, "->", output
@@ -74,3 +79,4 @@ for src, bg, out in shared_images:
t = "./png_over_color.py shared_images/%s %s %s" % ( src, context[bg], os.path.join( out_dir, out ) )
print t
run( t.split() )
"""
+36
View File
@@ -7,6 +7,7 @@ body
margin: 3px;
margin-right: 5px;
margin-left: 5px;
overflow: hidden;
}
div.pageTitle
@@ -24,3 +25,38 @@ a:link, a:visited, a:active
{
color: $masthead_link;
}
#tab-bar-bottom
{
z-index: -1;
position:absolute;
top:27px; left: 0;
width: 100%;
height: 100%;
background: $masthead_active_tab_bg;
}
span.link-group
{
margin: 0;
padding: 0;
display: inline;
padding-bottom: 10px;
margin-bottom: -10px;
}
span.link-group span
{
margin: 0;
padding: 0;
display: inline;
}
span.link-group span.active-link
{
background: $masthead_active_tab_bg;
padding-left: 3px; padding-right: 3px;
margin-left: -3px; margin-right: -3px;
padding-bottom: 10px;
margin-bottom: -10px;
}
@@ -45,6 +45,19 @@ body
font-weight: bold;
}
#messagebox
{
position:absolute;
top:33px;
left:0;
width:100%;
height:24px !important;
overflow: hidden;
border-bottom: solid #999 1px;
font-size: 90%;
}
#left, #left-border, #center, #right-border, #right
{
position: absolute;
@@ -268,4 +281,37 @@ div.popupmenu-item:hover {
.dialog-box .body, .dialog-box .buttons {
padding: 5px;
}
## Messages for message box, slightly different style
.panel-error-message, .panel-warning-message, .panel-done-message, .panel-info-message
{
height: 24px;
line-height: 24px;
color: $base_text;
padding: 0px;
padding-left: 26px;
background-color: $error_message_bg;
background-image: url(error_small.png);
background-repeat: no-repeat;
background-position: 6px 50%;
}
.panel-warning-message
{
background-image: url(warn_small.png);
background-color: $warn_message_bg;
}
.panel-done-message
{
background-image: url(done_small.png);
background-color: $done_message_bg;
}
.panel-info-message
{
background-image: url(info_small.png);
background-color: $info_message_bg;
}
+11 -12
View File
@@ -10,28 +10,27 @@
<body>
<div class="document">
<div class="warningmessage">
<strong>Galaxy and 2008 Meeting Season</strong>
<div class="donemessage">
<strong>Workflows are finally here!</strong>
<hr>
Watch how you can (<i>Click link to play</i>)...
<ul>
<li><a target="_blank" href="http://www.beyondgenome.com/08Personalized_Medicine_Day1.asp">Beyond Genome</a> | San Francisco | June 9 - 11</li>
<li>ISMB 2008 | Toronto | July 19 - 23</li>
<li>Genome Informatics | Hinxton, UK | September 10 - 14</li>
<li><a target="_blank" href="http://www.ashg.org/2008meeting/pages/workshops.shtml#4">ASHG</a> | Philadelphia | November 11 - 15</li>
</ul>
<li><a target="_blank" href="http://screencast.g2.bx.psu.edu/galaxy/WorkFlow_SC4/">Create Workflows by Example</a>: Convert your Galaxy History into a Workflow.</li>
<li><a target="_blank" href="http://screencast.g2.bx.psu.edu/galaxy/WorkFlow_SC5/">Edit Workflows</a>: I want to repeat an analysis...but...with different parameters.</li>
<li><a target="_blank" href="http://screencast.g2.bx.psu.edu/galaxy/WorkFlow_SC7/">Workflows from scratch</a>: Drag, drag, drag...</li>
</ul>
<hr>
Download <strong>new</strong> Galaxy brochure <a href="/static/images/brochure.pdf">here</a>.
</div>
For more screencasts click <a target="_blank" href="http://galaxy.psu.edu/screencasts.html">here</a>.
</div>
<hr>
<div class="section" align="center">
<strong>Unsequenced Genomes of the World</strong> | June 2008
<strong>Unsequenced Genomes of the World</strong> | October 2008
<br>
<br>
<img src="images/welcomePhoto.jpg" border="0">
<br>
Przewalski's Horse (<i>Equus przewalskii</i>) | Escondido, California
Costa's hummingbird (<i>Calypte costae</i>) | Kings Canyon NP, California
<br>
<br>
</div>
+125
View File
@@ -0,0 +1,125 @@
<%inherit file="/base.mako"/>
<%def name="title()">Galaxy Administration</%def>
<h2>Administration</h2>
<p>Choose a task from the menu on the left.</p>
<p>
<strong>Security</strong> - Data security in Galaxy is a new feature, so familiarize yourself with the details, which can be found
here or in our <a href="http://g2.trac.bx.psu.edu/wiki/SecurityFeatures" target="_blank">data security page</a>. The data security
process incorporates users, groups and roles, and enables the application of certain restrictions on datasets. The default process
is to not apply any security restrictions to datasets, and data becomes more restricted with each new "permission restriction" applied
to it. With no restrictions, users should not see any difference in the way Galaxy has always worked. The general definitions of
these entities are:
<ul>
<li>
<strong>Users</strong> - registered Galaxy users that have created a Galaxy account. Users can be members of a group and can
be associated with 1 or more roles. If a user is not authenticated during a Galaxy session, they will not have access to any
of the security features, and datasets they create during that session will have no security restrictions applied to them
(i.e., they will be considered "public").
</li>
<p></p>
<li>
<strong>Groups</strong> - a set of 0 or more users which are considered members of the group. Groups can be associated with 0
or more roles, simplifying the process of applying "permission restrictions" to the data between a select group of users.
</li>
<p></p>
<li>
<strong>Roles</strong> - associate users and groups with specific permissions on datasets. For example, users in groups A and B
can be associated with role C which gives them the "access" permission on datasets D, E and F. Roles have a type which is currently
one of the following:
<ul>
<li>
<strong>private</strong> - every user is associated automatically with their own private role, and administrators cannot
manage them.
</li>
<li>
<strong>user</strong> - this is currently not used, but eventually any registered user will be able to create a new role
and this will be it's type.
</li>
<li>
<strong>sharing</strong> - a role created automatically during a Galaxy session that enables a user to share data with
another user. This can generally be considered a temporary role, but this role type is evolving.
</li>
<li><strong>admin</strong> - a role created by a Galaxy administrator.</li>
</ul>
</li>
<p></p>
<li>
<strong>Permissions</strong> - for any dataset, applying one of the following role "permission restrictions" will restrict the
use of the dataset.
<ul>
<li>
<strong>access</strong> - users associated with the role can import this dataset into their history for analysis.
<p>
If no roles with the 'access' permission restriction are associated with a dataset, the dataset is "public" and may
be shared with anyone. Public library datasets will be accessible to all users (as well as anyone not logged in
during a Galaxy session) from the list of libraries displayed when the "Access Libraries stored locally" tool is used.
</p>
<p>
Associating a dataset with a role that includes the "access" permission restriction narrows the set of users that can
access it. For example, if 'Role A' includes the "access" permission restriction and 'Role A' is associated with the dataset,
only those users and groups who are associated with 'Role A' may access the dataset.
</p>
<p>
If multiple roles that include the "access" permission restriction are associated with a dataset, access to the
dataset is derived from the intersection of the users associated with the roles. For example, if 'Role A' and 'Role B' are
associated with a dataset, only those users and groups who are associated with both 'Role A' AND 'Role B' may access the dataset.
</p>
<p>
In order for a user to make a dataset private to just themselves, they should associate the dataset with their private role
(the role identical to their Galaxy user name / email address). Associating additional roles that include the 'access'
permission restriction is not advised since the behavior will probably not be as expected. Role permission restrictions
are always logically "ANDed" together, so only the user will be able to access the dataset since they are the only member of
their "private role". To make a dataset private to themselves and one or more other users, the user should create a new role
(note: this functionality is still under development for non administrator users) and associate the dataset with that role,
not their "private role".
</p>
<p>
Private data (data associated with roles that include the "access" permission restriction) must be made public in order
to be used with external sources, such as the "view at UCSC" link, or the "Perform genome analysis and prediction with EpiGRAPH"
tool. Being "made public" means removing the association of all roles that include the "access" permission restriction from
the dataset.
<p>
</li>
<li><strong>edit metadata</strong> - users associated with the role can edit this dataset's metadata in the dataset library.</li>
<li>
<strong>manage permissions</strong> - users associated with the role can manage the roles associated with this dataset. If no
roles that include the 'manage permissions' are associated with the dataset, only administrators can modify it's permission
restrictions.
</li>
</ul>
</li>
</ul>
</p>
<p>The menu on the left provides the following features</p>
<ul>
<li>
<strong>Manage users</strong> - provides a view of the registered users and all groups and non-private roles associated
with each user.
</li>
<p></p>
<li>
<strong>Manage groups</strong> - provides a view of all groups along with the members of the group and the roles associated with
each group (both private and non-private roles). Non-private roles include a link to a page that allows you to manage the users
and groups that are associated with the role. The page also includes a view of the library datasets that are associated with the
role and the various "permission restrictions" applied to each dataset.
</li>
<p></p>
<li>
<strong>Manage roles</strong> - provides a view of all non-private roles along with the role type, and the users and groups that
are associated with the role.
</li>
<p></p>
<li>
<strong>Manage libraries</strong> - Dataset libraries enable a Galaxy administrator to upload datasets into a library. Only
administrators can create dataset libraries and maintain their contents ( datasets ) and the security applied to them. From
the Galaxy analysis view, the "Access Libraries stored locally" tool in the "Get Data" tool
section allows users to access the datasets in a library ( if the user is not restricted from accessing the datasets by the
security rules applied to them ) by "uploading" them into their histories. This "uploading" process will not make a copy of
the dataset, however, but will be a sort of pointer to the dataset on disk. This approach allows for multiple users to have
access to a single ( possibly very large ) dataset on disk.
</li>
</ul>
<p></p><p></p>
@@ -0,0 +1,111 @@
<%inherit file="/base.mako"/>
## Render a row
<%def name="render_row( group, members, roles, ctr, anchored, curr_anchor )">
%if ctr % 2 == 1:
<tr class="odd_row">
%else:
<tr>
%endif
<td>
${group.name}
<a id="group-${group.id}-popup" class="popup-arrow" style="display: none;">&#9660;</a>
<div popupmenu="group-${group.id}-popup">
<a class="action-button" href="${h.url_for( action='undelete_group', group_id=group.id )}">Undelete</a>
<a class="action-button" href="${h.url_for( action='purge_group', group_id=group.id )}">Purge</a>
</div>
</td>
<td>
<ul>
%for user in members:
<li>${user.email}</li>
%endfor
</ul>
</td>
<td>
<ul>
%for role in roles:
<li>${role.name}</li>
%endfor
</ul>
%if not anchored:
<a name="${curr_anchor}"></a>
<div style="float: right;"><a href="#TOP">top</a></div>
%endif
</td>
</tr>
</%def>
%if msg:
<div class="donemessage">${msg}</div>
%endif
<a name="TOP"><h2>Deleted Groups</h2></a>
%if len( groups_members_roles ) == 0:
There are no deleted Galaxy groups
%else:
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<%
render_quick_find = len( groups_members_roles ) > 50
ctr = 0
%>
%if render_quick_find:
<%
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
anchor_loc = 0
anchored = False
curr_anchor = 'A'
%>
<tr style="background: #EEE">
<td colspan="3" style="text-align: center; border-bottom: 1px solid #D8B365">
Jump to letter:
%for a in anchors:
| <a href="#${a}">${a}</a>
%endfor
</td>
</tr>
%endif
<tr class="header">
<td>Name</td>
<td>Members</td>
<td>Roles</td>
</tr>
%for ctr, group_tuple in enumerate( groups_members_roles ):
<%
group = group_tuple[0]
members = group_tuple[1]
roles = group_tuple[2]
%>
%if render_quick_find and not group.name.upper().startswith( curr_anchor ):
<% anchored = False %>
%endif
%if render_quick_find and group.name.upper().startswith( curr_anchor ):
%if not anchored:
${render_row( group, members, roles, ctr, anchored, curr_anchor )}
<% anchored = True %>
%else:
${render_row( group, members, roles, ctr, anchored, curr_anchor )}
%endif
%elif render_quick_find:
%for anchor in anchors[ anchor_loc: ]:
%if group.name.upper().startswith( anchor ):
%if not anchored:
<% curr_anchor = anchor %>
${render_row( group, members, roles, ctr, anchored, curr_anchor )}
<% anchored = True %>
%else:
${render_row( group, members, roles, ctr, anchored, curr_anchor )}
%endif
<%
anchor_loc = anchors.index( anchor )
break
%>
%endif
%endfor
%else:
${render_row( group, members, roles, ctr, True, '' )}
%endif
%endfor
</table>
%endif
@@ -0,0 +1,111 @@
<%inherit file="/base.mako"/>
## Render a row
<%def name="render_row( role, groups, users, ctr, anchored, curr_anchor )">
%if ctr % 2 == 1:
<tr class="odd_row">
%else:
<tr>
%endif
<td>
${role.name}
<a id="role-${role.id}-popup" class="popup-arrow" style="display: none;">&#9660;</a>
<div popupmenu="role-${role.id}-popup">
<a class="action-button" href="${h.url_for( action='undelete_role', role_id=role.id )}">Undelete</a>
<a class="action-button" href="${h.url_for( action='purge_role', role_id=role.id )}">Purge</a>
</div>
</td>
<td>
<ul>
%for group in groups:
<li>${group.name}</li>
%endfor
</ul>
</td>
<td>
<ul>
%for user in users:
<li>${user.email}</li>
%endfor
</ul>
%if not anchored:
<a name="${curr_anchor}"></a>
<div style="float: right;"><a href="#TOP">top</a></div>
%endif
</td>
</tr>
</%def>
%if msg:
<div class="donemessage">${msg}</div>
%endif
<a name="TOP"><h2>Deleted Roles</h2></a>
%if len( roles_groups_users ) == 0:
There are no deleted Galaxy roles
%else:
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<%
render_quick_find = len( roles_groups_users ) > 50
ctr = 0
%>
%if render_quick_find:
<%
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
anchor_loc = 0
anchored = False
curr_anchor = 'A'
%>
<tr style="background: #EEE">
<td colspan="3" style="text-align: center; border-bottom: 1px solid #D8B365">
Jump to letter:
%for a in anchors:
| <a href="#${a}">${a}</a>
%endfor
</td>
</tr>
%endif
<tr class="header">
<td>Name</td>
<td>Associated Groups</td>
<td>Associated Users</td>
</tr>
%for ctr, role_tuple in enumerate( roles_groups_users ):
<%
role = role_tuple[0]
groups = role_tuple[1]
users = role_tuple[2]
%>
%if render_quick_find and not role.name.upper().startswith( curr_anchor ):
<% anchored = False %>
%endif
%if render_quick_find and role.name.upper().startswith( curr_anchor ):
%if not anchored:
${render_row( role, groups, users, ctr, anchored, curr_anchor )}
<% anchored = True %>
%else:
${render_row( role, groups, users, ctr, anchored, curr_anchor )}
%endif
%elif render_quick_find:
%for anchor in anchors[ anchor_loc: ]:
%if role.name.upper().startswith( anchor ):
%if not anchored:
<% curr_anchor = anchor %>
${render_row( role, groups, users, ctr, anchored, curr_anchor )}
<% anchored = True %>
%else:
${render_row( role, groups, users, ctr, anchored, curr_anchor )}
%endif
<%
anchor_loc = anchors.index( anchor )
break
%>
%endif
%endfor
%else:
${render_row( role, groups, users, ctr, True, '' )}
%endif
%endfor
</table>
%endif
@@ -0,0 +1,115 @@
<%inherit file="/base.mako"/>
## Render a user row
<%def name="render_user_row( user, ctr )">
%if ctr % 2 == 1:
<tr class="odd_row">
%else:
<tr>
%endif
<td><input type="checkbox" name="members" value="${user.id}"/> ${user.email}</td>
</tr>
</%def>
## Render a role row
<%def name="render_role_row( role, ctr, anchored, curr_anchor )">
%if ctr % 2 == 1:
<tr class="odd_row">
%else:
<tr>
%endif
<td>
%if not anchored:
<div style="float: right;"><a href="#TOP">top</a></div>
<a name="${curr_anchor}"><input type="checkbox" name="roles" value="${role.id}"/> ${role.name}</a>
%else:
<input type="checkbox" name="roles" value="${role.id}"/> ${role.name}
%endif
</td>
</tr>
</%def>
%if msg:
<div class="donemessage">${msg}</div>
%endif
<a name="TOP"><h2>Create Group</h2></a>
<form name="group_create" action="${h.url_for( controller='admin', action='new_group' )}" method="post" >
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<tr><td colspan="2">Name: <input name="name" type="textfield" value="" size=40"></td></tr>
<%
render_quick_find = len( users ) > 50
ctr = 0
%>
%if render_quick_find:
<%
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
anchor_loc = 0
anchored = False
curr_anchor = 'A'
%>
<tr style="background: #EEE">
<td colspan="2" style="border-bottom: 1px solid #D8B365; text-align: center;">
Jump to letter:
%for a in anchors:
| <a href="#${a}">${a}</a>
%endfor
</td>
</tr>
%endif
<tr class="header">
<td>Check to add user</td>
<td>Check to add role</td>
</tr>
<tr>
## Render users
<td valign="top">
<table border="0" cellspacing="0" cellpadding="0" width="100%">
%for ctr, user in enumerate( users ):
${render_user_row( user, ctr )}
%endfor
</table>
</td>
## Render roles
<td valign="top">
<% curr_anchor = 'A' %>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
%for ctr, role in enumerate( roles ):
%if render_quick_find and not role.name.upper().startswith( curr_anchor ):
<% anchored = False %>
%endif
%if render_quick_find and role.name.upper().startswith( curr_anchor ):
%if not anchored:
${render_role_row( role, ctr, anchored, curr_anchor )}
<% anchored = True %>
%else:
${render_role_row( role, ctr, anchored, curr_anchor )}
%endif
%elif render_quick_find:
%for anchor in anchors[ anchor_loc: ]:
%if role.name.upper().startswith( anchor ):
%if not anchored:
<% curr_anchor = anchor %>
${render_role_row( role, ctr, anchored, curr_anchor )}
<% anchored = True %>
%else:
${render_role_row( role, ctr, anchored, curr_anchor )}
%endif
<%
anchor_loc = anchors.index( anchor )
break
%>
%endif
%endfor
%else:
${render_role_row( role, ctr, True, '' )}
%endif
%endfor
</table>
</td>
</tr>
<tr><td colspan="2"><button name="create_group_button" value="group_create">Create</button></td></tr>
</table>
</form>
@@ -0,0 +1,73 @@
<%inherit file="/base.mako"/>
<%def name="title()">Permitted Actions on Datasets</%def>
<div class="toolForm">
<div class="form-row">
<a href="${h.url_for( controller='admin', action='libraries' )}">Libraries</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<a href="${h.url_for( controller='admin', action='groups' )}">Groups</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<tr><td><a href="${h.url_for( controller='admin', action='users' )}">Users</a></td></tr>
</div>
<h3 align="center">Manage Permitted Actions on Datasets for Group '${group.name}'</h3>
<table align="center" class="colored">
%if msg:
<tr><td colspan="3"><p class="ok_bgr">${msg}</p></td></tr>
%endif
<tr><td colspan="3">&nbsp;</td>
%if len( group.datasets ) == 0:
<tr><td colspan="3">The group you selected has no associated datasets.</td></tr>
%else:
<tr class="header">
<td>Association Names/Info</td>
<td>Permitted Actions</td>
</tr>
<% ctr = 0 %>
<form name="group_dataset_permitted_actions_edit" action="${h.url_for( controller='admin',
action='group_dataset_permitted_actions_edit',
group_id=group.id )}" method="post" >
<input type="hidden" name="gdas" value="${ ','.join( [ str(gda.id) for gda in gdas ] ) }"/>
%for gda in gdas:
<% permissions = trans.app.security_agent.get_dataset_permissions( gda, group.id ) %>
%if ctr % 2 == 1:
<tr class="odd_row">
%else:
<tr class="tr">
%endif
<td>
%if gda.dataset.library_associations:
<strong>Library name(s):</strong><br/>
%endif
<ul>
%for da in gda.dataset.library_associations:
<li>${da.name} (${da.info})</li>
%endfor
</ul>
%if gda.dataset.history_associations:
<strong>History name(s):</strong><br/>
%endif
<ul>
%for da in gda.dataset.history_associations:
<li>${da.name} (${da.info})</li>
%endfor
</ul>
</td>
<td>
%for pa in trans.app.model.Dataset.permitted_actions:
<% pa_val = trans.app.security_agent.permitted_actions.__dict__[pa] %>
<input type="checkbox" name="gda_actions_${gda.id}" value="${pa}"
%if pa_val in permissions[1]:
checked
%endif
/>
${pa_val}<br/>${trans.app.security_agent.get_permitted_action_description(pa)}<br/>
<br/>
%endfor
<br/>
</td>
</tr>
<% ctr += 1 %>
%endfor
<tr><td colspan="3"><center><button name="group_dataset_permitted_actions_edit_button" value="group_dataset_permitted_actions_edit">Update</button></center></td></tr>
</form>
%endif
</table>
</div>
@@ -0,0 +1,96 @@
<%inherit file="/base.mako"/>
## Render a row
<%def name="render_row( user, ctr, anchored, curr_anchor, check )">
%if ctr % 2 == 1:
<tr class="odd_row">
%else:
<tr>
%endif
<td>
%if check:
<input type="checkbox" name="members" value="${user.id}" checked/> ${user.email}
%else:
<input type="checkbox" name="members" value="${user.id}"/> ${user.email}
%endif
</td>
</tr>
</%def>
%if msg:
<div class="donemessage">${msg}</div>
%endif
<a name="TOP"><h2>Members of group '${group.name}'</h2></a>
%if len( users ) == 0:
<tr><td>There are no Galaxy users</td></tr>
%else:
<form name="update_group_members" action="${h.url_for( controller='admin', action='update_group_members', group_id=group.id )}" method="post" >
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<%
render_quick_find = len( users ) > 50
ctr = 0
%>
%if render_quick_find:
<%
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
anchor_loc = 0
anchored = False
curr_anchor = 'A'
%>
<tr style="background: #EEE">
<td style="border-bottom: 1px solid #D8B365; text-align: center;">
Jump to letter:
%for a in anchors:
| <a href="#${a}">${a}</a>
%endfor
</td>
</tr>
%endif
<tr class="header"><td>Check to add member</td></tr>
%for ctr, user in enumerate( users ):
<% check = False %>
%for member in members:
%if member.email == user.email:
<%
check = True
break
%>
%endif
%endfor
%if render_quick_find and not user.email.upper().startswith( curr_anchor ):
<% anchored = False %>
%endif
%if render_quick_find and user.email.upper().startswith( curr_anchor ):
%if not anchored:
${render_row( user, ctr, anchored, curr_anchor, check )}
<% anchored = True %>
%else:
${render_row( user, ctr, anchored, curr_anchor, check )}
%endif
%elif render_quick_find:
%for anchor in anchors[ anchor_loc: ]:
%if user.email.upper().startswith( anchor ):
%if not anchored:
<% curr_anchor = anchor %>
${render_row( user, ctr, anchored, curr_anchor, check )}
<% anchored = True %>
%else:
${render_row( user, ctr, anchored, curr_anchor, check )}
%endif
<%
anchor_loc = anchors.index( anchor )
break
%>
%endif
%endfor
%else:
${render_row( user, ctr, True, '', check )}
%endif
%endfor
<tr><td><button name="group_members_edit_button" value="update_group_members">Update Members</button></td></tr>
</table>
</form>
%endif
@@ -0,0 +1,96 @@
<%inherit file="/base.mako"/>
## Render a row
<%def name="render_row( role, ctr, anchored, curr_anchor, check )">
%if ctr % 2 == 1:
<tr class="odd_row">
%else:
<tr>
%endif
<td>
%if check:
<input type="checkbox" name="roles" value="${role.id}" checked/> ${role.name}
%else:
<input type="checkbox" name="roles" value="${role.id}"/> ${role.name}
%endif
</td>
</tr>
</%def>
%if msg:
<div class="donemessage">${msg}</div>
%endif
<a name="TOP"><h2>Roles associated with group '${group.name}'</h2></a>
%if len( roles ) == 0:
<tr><td>There are no Galaxy roles</td></tr>
%else:
<form name="update_group_roles" action="${h.url_for( controller='admin', action='update_group_roles', group_id=group.id )}" method="post" >
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<%
render_quick_find = len( roles ) > 50
ctr = 0
%>
%if render_quick_find:
<%
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
anchor_loc = 0
anchored = False
curr_anchor = 'A'
%>
<tr style="background: #EEE">
<td style="border-bottom: 1px solid #D8B365; text-align: center;">
Jump to letter:
%for a in anchors:
| <a href="#${a}">${a}</a>
%endfor
</td>
</tr>
%endif
<tr class="header"><td>Check to add role</td></tr>
%for ctr, role in enumerate( roles ):
<% check = False %>
%for group_role in group_roles:
%if group_role.name == role.name:
<%
check = True
break
%>
%endif
%endfor
%if render_quick_find and not role.name.upper().startswith( curr_anchor ):
<% anchored = False %>
%endif
%if render_quick_find and role.name.upper().startswith( curr_anchor ):
%if not anchored:
${render_row( role, ctr, anchored, curr_anchor, check )}
<% anchored = True %>
%else:
${render_row( role, ctr, anchored, curr_anchor, check )}
%endif
%elif render_quick_find:
%for anchor in anchors[ anchor_loc: ]:
%if role.name.upper().startswith( anchor ):
%if not anchored:
<% curr_anchor = anchor %>
${render_row( role, ctr, anchored, curr_anchor, check )}
<% anchored = True %>
%else:
${render_row( role, ctr, anchored, curr_anchor, check )}
%endif
<%
anchor_loc = anchors.index( anchor )
break
%>
%endif
%endfor
%else:
${render_row( role, ctr, True, '', check )}
%endif
%endfor
<tr><td><button name="group_roles_edit_button" value="update_group_roles">Update Role Associations</button></td></tr>
</table>
</form>
%endif
@@ -0,0 +1,128 @@
<%inherit file="/base.mako"/>
<%
import galaxy.model
%>
## Render a row
<%def name="render_row( group, members, roles, ctr, anchored, curr_anchor )">
%if ctr % 2 == 1:
<tr class="odd_row">
%else:
<tr>
%endif
<td>
${group.name}
<a id="group-${group.id}-popup" class="popup-arrow" style="display: none;">&#9660;</a>
<div popupmenu="group-${group.id}-popup">
<a class="action-button" href="${h.url_for( action='group_members_edit', group_id=group.id )}">Change members</a>
<a class="action-button" href="${h.url_for( action='mark_group_deleted', group_id=group.id )}">Mark group deleted</a>
</div>
</td>
<td>
<ul>
%for user in members:
<li>
<a href="${h.url_for( controller='admin', action='user', user_id=user.id )}">${user.email}</a>
</li>
%endfor
</ul>
</td>
<td>
<ul>
%for role in roles:
<li>
%if not role.type == galaxy.model.Role.types.PRIVATE:
<a href="${h.url_for( controller='admin', action='role', role_id=role.id )}">${role.name}</a>
%else:
${role.name}
%endif
</li>
%endfor
</ul>
%if not anchored:
<a name="${curr_anchor}"></a>
<div style="float: right;"><a href="#TOP">top</a></div>
%endif
</td>
</tr>
</%def>
%if msg:
<div class="donemessage">${msg}</div>
%endif
<a name="TOP"><h2>Groups</h2></a>
<ul class="manage-table-actions">
<li><a class="action-button" href="${h.url_for( controller='admin', action='create_group' )}">Create a new group</a></li>
<li><a class="action-button" href="${h.url_for( controller='admin', action='deleted_groups' )}">Manage deleted groups</a></li>
</ul>
%if len( groups_members_roles ) == 0:
There are no Galaxy groups
%else:
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<%
render_quick_find = len( groups_members_roles ) > 50
ctr = 0
%>
%if render_quick_find:
<%
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
anchor_loc = 0
anchored = False
curr_anchor = 'A'
%>
<tr style="background: #EEE">
<td colspan="3" style="text-align: center; border-bottom: 1px solid #D8B365">
Jump to letter:
%for a in anchors:
| <a href="#${a}">${a}</a>
%endfor
</td>
</tr>
%endif
<tr class="header">
<td>Name</td>
<td>Members</td>
<td>Associated Roles</td>
</tr>
%for ctr, group_tuple in enumerate( groups_members_roles ):
<%
group = group_tuple[0]
members = group_tuple[1]
roles = group_tuple[2]
%>
%if render_quick_find and not group.name.upper().startswith( curr_anchor ):
<% anchored = False %>
%endif
%if render_quick_find and group.name.upper().startswith( curr_anchor ):
%if not anchored:
${render_row( group, members, roles, ctr, anchored, curr_anchor )}
<% anchored = True %>
%else:
${render_row( group, members, roles, ctr, anchored, curr_anchor )}
%endif
%elif render_quick_find:
%for anchor in anchors[ anchor_loc: ]:
%if group.name.upper().startswith( anchor ):
%if not anchored:
<% curr_anchor = anchor %>
${render_row( group, members, roles, ctr, anchored, curr_anchor )}
<% anchored = True %>
%else:
${render_row( group, members, roles, ctr, anchored, curr_anchor )}
%endif
<%
anchor_loc = anchors.index( anchor )
break
%>
%endif
%endfor
%else:
${render_row( group, members, roles, ctr, True, '' )}
%endif
%endfor
</table>
%endif
@@ -0,0 +1,13 @@
<%inherit file="/base.mako"/>
<%def name="title()">Dataset Security</%def>
<div class="toolForm">
<div class="toolFormTitle">Dataset Security</div>
<table align="center" class="colored">
%if msg:
<tr><td><p class="ok_bgr">${msg}</p></td></tr>
%endif
<tr><td><a href="${h.url_for( controller='admin', action='groups' )}">Groups</a></td></tr>
<tr><td><a href="${h.url_for( controller='admin', action='users' )}">Users</a></td></tr>
</table>
</div>
+112
View File
@@ -0,0 +1,112 @@
<%inherit file="/base.mako"/>
<%def name="javascripts()">
${parent.javascripts()}
<script type="text/javascript">
$(function(){
$("input:text:first").focus();
})
</script>
</%def>
<%def name="render_select( name, options )">
<select name="${name}" id="${name}" style="min-width: 250px; height: 150px;" multiple>
%for option in options:
<option value="${option[0]}">${option[1]}</option>
%endfor
</select>
</%def>
<script type="text/javascript">
$().ready(function() {
$('#users_add').click(function() {
return !$('#out_users option:selected').remove().appendTo('#in_users');
});
$('#users_remove').click(function() {
return !$('#in_users option:selected').remove().appendTo('#out_users');
});
$('#groups_add').click(function() {
return !$('#out_groups option:selected').remove().appendTo('#in_groups');
});
$('#groups_remove').click(function() {
return !$('#in_groups option:selected').remove().appendTo('#out_groups');
});
$('form#associate_role_user_group').submit(function() {
$('#in_users option').each(function(i) {
$(this).attr("selected", "selected");
});
$('#in_groups option').each(function(i) {
$(this).attr("selected", "selected");
});
});
});
</script>
<div class="toolForm">
<div class="toolFormTitle">Role '${role.name}'</div>
<div class="toolFormBody">
<form name="associate_role_user_group" id="associate_role_user_group" action="${h.url_for( action='role_members_edit', role_id=role.id )}" method="post" >
<div class="form-row">
<div style="float: left; margin-right: 10px;">
Users associated with '${role.name}'<br/>
${render_select( "in_users", in_users )}<br/>
<input type="submit" id="users_remove_button" value=">>"/>
</div>
<div>
Users not associated with '${role.name}'<br/>
${render_select( "out_users", out_users )}<br/>
<input type="submit" id="users_add_button" value="<<"/>
</div>
</div>
<div class="form-row">
<div style="float: left; margin-right: 10px;">
Groups associated with '${role.name}'<br/>
${render_select( "in_groups", in_groups )}<br/>
<input type="submit" id="groups_remove_button" value=">>"/>
</div>
<div>
Groups not associated with '${role.name}'<br/>
${render_select( "out_groups", out_groups )}<br/>
<input type="submit" id="groups_add_button" value="<<"/>
</div>
</div>
<div class="form-row">
<input type="submit" name="role_button" value="Save"/>
</div>
</form>
</div>
</div>
<br clear="left"/>
<br/>
%if len( library_dataset_actions ) > 0:
<h3>Library datasets associated with role '${role.name}'</h3>
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<tr>
<td>
<ul>
%for ctr, library, in enumerate( library_dataset_actions.keys() ):
<li>
<img src="${h.url_for( '/static/images/silk/book_open.png' )}" class="rowIcon"/>
${library.name}
<ul>
%for folder_path, permissions in library_dataset_actions[ library ].items():
<li>
<img src="/static/images/silk/folder_page.png" class="rowIcon"/>
${folder_path}
<ul>
% for permission in permissions:
<ul>
<li>${permission}</li>
</ul>
%endfor
</ul>
</li>
%endfor
</ul>
</li>
%endfor
</ul>
</td>
</tr>
</table>
%endif
@@ -0,0 +1,118 @@
<%inherit file="/base.mako"/>
## Render a user row
<%def name="render_user_row( user, ctr )">
%if ctr % 2 == 1:
<tr class="odd_row">
%else:
<tr>
%endif
<td><input type="checkbox" name="users" value="${user.id}"/> ${user.email}</td>
</tr>
</%def>
## Render a group row
<%def name="render_group_row( group, ctr, anchored, curr_anchor )">
%if ctr % 2 == 1:
<tr class="odd_row">
%else:
<tr>
%endif
<td>
%if not anchored:
<div style="float: right;"><a href="#TOP">top</a></div>
<a name="${curr_anchor}"><input type="checkbox" name="groups" value="${group.id}"/> ${group.name}</a>
%else:
<input type="checkbox" name="groups" value="${group.id}"/> ${group.name}
%endif
</td>
</tr>
</%def>
%if msg:
<div class="donemessage">${msg}</div>
%endif
<a name="TOP"><h2>Create Role</h2></a>
<form name="role_create" action="${h.url_for( controller='admin', action='new_role' )}" method="post" >
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<tr>
<td>Name: <input name="name" type="textfield" value="" size=40"></td>
<td>Description: <input name="description" type="textfield" value="" size=40"></td>
</tr>
<%
render_quick_find = len( users ) > 50
ctr = 0
%>
%if render_quick_find:
<%
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
anchor_loc = 0
anchored = False
curr_anchor = 'A'
%>
<tr style="background: #EEE">
<td colspan="2" style="border-bottom: 1px solid #D8B365; text-align: center;">
Jump to letter:
%for a in anchors:
| <a href="#${a}">${a}</a>
%endfor
</td>
</tr>
%endif
<tr class="header">
<td>Check to add user</td>
<td>Check to add group</td>
</tr>
<tr>
## Render users
<td valign="top">
<table border="0" cellspacing="0" cellpadding="0" width="100%">
%for ctr, user in enumerate( users ):
${render_user_row( user, ctr )}
%endfor
</table>
</td>
## Render roles
<td valign="top">
<% curr_anchor = 'A' %>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
%for ctr, group in enumerate( groups ):
%if render_quick_find and not group.name.upper().startswith( curr_anchor ):
<% anchored = False %>
%endif
%if render_quick_find and group.name.upper().startswith( curr_anchor ):
%if not anchored:
${render_group_row( group, ctr, anchored, curr_anchor )}
<% anchored = True %>
%else:
${render_group_row( group, ctr, anchored, curr_anchor )}
%endif
%elif render_quick_find:
%for anchor in anchors[ anchor_loc: ]:
%if group.name.upper().startswith( anchor ):
%if not anchored:
<% curr_anchor = anchor %>
${render_group_row( group, ctr, anchored, curr_anchor )}
<% anchored = True %>
%else:
${render_group_row( group, ctr, anchored, curr_anchor )}
%endif
<%
anchor_loc = anchors.index( anchor )
break
%>
%endif
%endfor
%else:
${render_group_row( group, ctr, True, '' )}
%endif
%endfor
</table>
</td>
</tr>
<tr><td colspan="2"><button name="create_role_button" value="role_create">Create</button></td></tr>
</table>
</form>
+119
View File
@@ -0,0 +1,119 @@
<%inherit file="/base.mako"/>
<%
from galaxy.web.controllers.admin import entities, unentities
from xml.sax.saxutils import escape, unescape
import galaxy.model
%>
## Render a row
<%def name="render_row( role, ctr, anchored, curr_anchor )">
%if ctr % 2 == 1:
<tr class="odd_row">
%else:
<tr>
%endif
<td>
${role.name}
<a id="role-${role.id}-popup" class="popup-arrow" style="display: none;">&#9660;</a>
<div popupmenu="role-${role.id}-popup">
<a class="action-button" href="${h.url_for( action='role', role_id=role.id )}">Change associated users and groups</a>
<a class="action-button" href="${h.url_for( action='mark_role_deleted', role_id=role.id )}">Mark role deleted</a>
</div>
</td>
<td>${role.type}</td>
<td>
<ul>
%for ura in role.users:
<li><a href="${h.url_for( action='user_groups_edit', user_id=ura.user.id )}">${ura.user.email}</a></li>
%endfor
</ul>
</td>
<td>
<ul>
%for gra in role.groups:
<li><a href="${h.url_for( action='group_members_edit', group_id=gra.group.id )}">${gra.group.name}</a></li>
%endfor
</ul>
%if not anchored:
<a name="${curr_anchor}"></a>
<div style="float: right;"><a href="#TOP">top</a></div>
%endif
</td>
</tr>
</%def>
%if msg:
<div class="donemessage">${msg}</div>
%endif
<a name="TOP"><h2>Non-private Roles</h2></a>
<ul class="manage-table-actions">
<li><a class="action-button" href="${h.url_for( controller='admin', action='create_role' )}">Create a new role</a></li>
<li><a class="action-button" href="${h.url_for( controller='admin', action='deleted_roles' )}">Manage deleted roles</a></li>
</ul>
%if len( roles ) == 0:
There are no non-private Galaxy roles
%else:
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<%
render_quick_find = len( roles ) > 50
ctr = 0
%>
%if render_quick_find:
<%
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
anchor_loc = 0
anchored = False
curr_anchor = 'A'
%>
<tr style="background: #EEE">
<td colspan="4" style="text-align: center; border-bottom: 1px solid #D8B365">
Jump to letter:
%for a in anchors:
| <a href="#${a}">${a}</a>
%endfor
</td>
</tr>
%endif
<tr class="header">
<td>Name</td>
<td>Type</td>
<td>Users</td>
<td>Groups</td>
</tr>
%for ctr, role in enumerate( roles ):
%if render_quick_find and not role.name.upper().startswith( curr_anchor ):
<% anchored = False %>
%endif
%if render_quick_find and role.name.upper().startswith( curr_anchor ):
%if not anchored:
${render_row( role, ctr, anchored, curr_anchor )}
<% anchored = True %>
%else:
${render_row( role, ctr, anchored, curr_anchor )}
%endif
%elif render_quick_find:
%for anchor in anchors[ anchor_loc: ]:
%if role.name.upper().startswith( anchor ):
%if not anchored:
<% curr_anchor = anchor %>
${render_row( role, ctr, anchored, curr_anchor )}
<% anchored = True %>
%else:
${render_row( role, ctr, anchored, curr_anchor )}
%endif
<%
anchor_loc = anchors.index( anchor )
break
%>
%endif
%endfor
%else:
${render_row( role, ctr, True, '' )}
%endif
%endfor
</table>
%endif
@@ -0,0 +1,64 @@
<%inherit file="/base.mako"/>
<%
import galaxy.model
%>
## Render a role
<%def name="render_role( user, role )">
<li>
%if not role.type == galaxy.model.Role.types.PRIVATE:
<a href="${h.url_for( action='role', role=role, edit=True )}">${role.name}</a>
%else:
${role.name}
%endif
<a id="role-${role.id}-popup" class="popup-arrow" style="display: none;">&#9660;</a>
<div popupmenu="role-${role.id}-popup">
<a class="action-button" href="${h.url_for( action='remove_user_from_role', user_id=user.id, role_id=role.id )}">Remove user from role</a>
</div>
</li>
</%def>
## Render a group
<%def name="render_group( user, group )">
<li>
<a href="${h.url_for( action='group_members_edit', group_id=group.id )}">${group.name}</a>
<a id="group-${group.id}-popup" class="popup-arrow" style="display: none;">&#9660;</a>
<div popupmenu="group-${group.id}-popup">
<a class="action-button" href="${h.url_for( action='remove_user_from_group', user_id=user.id, group_id=group.id )}">Remove user from group</a>
</div>
</li>
</%def>
%if msg:
<div class="donemessage">${msg}</div>
%endif
<h2>User '${user.email}'</h2>
%if len( groups ) == 0 and len( roles ) == 0:
User '${user.email}' belongs to no groups and is associated with no roles
%else:
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<tr class="header">
<td>Groups</td>
<td>Roles</td>
</tr>
<tr>
<td>
<ul>
%for group in groups:
${render_group( user, group )}
%endfor
</ul>
</td>
<td>
<ul>
%for role in roles:
${render_role( user, role )}
%endfor
</ul>
</td>
</tr>
</table>
%endif
@@ -0,0 +1,96 @@
<%inherit file="/base.mako"/>
## Render a row
<%def name="render_row( group, ctr, anchored, curr_anchor, check )">
%if ctr % 2 == 1:
<tr class="odd_row">
%else:
<tr>
%endif
<td>
%if check:
<input type="checkbox" name="groups" value="${group.id}" checked/> ${group.name}
%else:
<input type="checkbox" name="groups" value="${group.id}"/> ${group.name}
%endif
</td>
</tr>
</%def>
%if msg:
<div class="donemessage">${msg}</div>
%endif
<a name="TOP"><h2>User '${user.email}' Associated Groups</h2></a>
%if len( groups ) == 0:
<tr><td>User ${user.email} is not a member of any groups</td></tr>
%else:
<form name="update_user_groups" action="${h.url_for( controller='admin', action='update_user_groups', user_id=user.id )}" method="post" >
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<%
render_quick_find = len( groups ) > 50
ctr = 0
%>
%if render_quick_find:
<%
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
anchor_loc = 0
anchored = False
curr_anchor = 'A'
%>
<tr style="background: #EEE">
<td style="border-bottom: 1px solid #D8B365; text-align: center;">
Jump to letter:
%for a in anchors:
| <a href="#${a}">${a}</a>
%endfor
</td>
</tr>
%endif
<tr class="header"><td>Check to add member</td></tr>
%for ctr, group in enumerate( groups ):
<% check = False %>
%for user_group in user_groups:
%if user_group.id == group.id:
<%
check = True
break
%>
%endif
%endfor
%if render_quick_find and not group.name.upper().startswith( curr_anchor ):
<% anchored = False %>
%endif
%if render_quick_find and group.name.upper().startswith( curr_anchor ):
%if not anchored:
${render_row( group, ctr, anchored, curr_anchor, check )}
<% anchored = True %>
%else:
${render_row( group, ctr, anchored, curr_anchor, check )}
%endif
%elif render_quick_find:
%for anchor in anchors[ anchor_loc: ]:
%if group.name.upper().startswith( anchor ):
%if not anchored:
<% curr_anchor = anchor %>
${render_row( group, ctr, anchored, curr_anchor, check )}
<% anchored = True %>
%else:
${render_row( group, ctr, anchored, curr_anchor, check )}
%endif
<%
anchor_loc = anchors.index( anchor )
break
%>
%endif
%endfor
%else:
${render_row( group, ctr, True, '', check )}
%endif
%endfor
<tr><td><button name="user_groups_edit_button" value="update_user_groups">Update Group Associations</button></td></tr>
</table>
</form>
%endif
+114
View File
@@ -0,0 +1,114 @@
<%inherit file="/base.mako"/>
<%
import galaxy.model
%>
## Render a row
<%def name="render_row( user, groups, roles, ctr, anchored, curr_anchor )">
%if ctr % 2 == 1:
<tr class="odd_row">
%else:
<tr>
%endif
<td>
<a href="${h.url_for( controller='admin', action='user', user_id=user.id )}">${user.email}</a>
<a id="user-${user.id}-popup" class="popup-arrow" style="display: none;">&#9660;</a>
<div popupmenu="user-${user.id}-popup">
<a class="action-button" href="${h.url_for( action='user_groups_edit', user_id=user.id )}">Change associated groups</a>
</div>
</td>
<td>
<ul>
%for group in groups:
<li><a href="${h.url_for( controller='admin', action='group_members_edit', group_id=group.id )}">${group.name}</a></li>
%endfor
</ul>
</td>
<td>
<ul>
%for role in roles:
<li><a href="${h.url_for( controller='admin', action='role', role_id=role.id )}">${role.name}</a></li>
%endfor
</ul>
%if not anchored:
<a name="${curr_anchor}"></a>
<div style="float: right;"><a href="#TOP">top</a></div>
%endif
</td>
</tr>
</%def>
%if msg:
<div class="donemessage">${msg}</div>
%endif
<a name="TOP"><h2>Users</h2></a>
%if len( users_groups_roles ) == 0:
There are no Galaxy users
%else:
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<%
render_quick_find = len( users_groups_roles ) > 50
ctr = 0
%>
%if render_quick_find:
<%
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
anchor_loc = 0
anchored = False
curr_anchor = 'A'
%>
<tr style="background: #EEE">
<td colspan="3" style="border-bottom: 1px solid #D8B365; text-align: center;">
Jump to letter:
%for a in anchors:
| <a href="#${a}">${a}</a>
%endfor
</td>
</tr>
%endif
<tr class="header">
<td>Email</td>
<td>Groups</td>
<td>Associated Non-private Roles</td>
</tr>
%for ctr, user_tuple in enumerate( users_groups_roles ):
<%
user = user_tuple[0]
groups = user_tuple[1]
roles = user_tuple[2]
%>
%if render_quick_find and not user.email.upper().startswith( curr_anchor ):
<% anchored = False %>
%endif
%if render_quick_find and user.email.upper().startswith( curr_anchor ):
%if not anchored:
${render_row( user, groups, roles, ctr, anchored, curr_anchor )}
<% anchored = True %>
%else:
${render_row( user, groups, roles, ctr, anchored, curr_anchor )}
%endif
%elif render_quick_find:
%for anchor in anchors[ anchor_loc: ]:
%if user.email.upper().startswith( anchor ):
%if not anchored:
<% curr_anchor = anchor %>
${render_row( user, groups, roles, ctr, anchored, curr_anchor )}
<% anchored = True %>
%else:
${render_row( user, groups, roles, ctr, anchored, curr_anchor )}
%endif
<%
anchor_loc = anchors.index( anchor )
break
%>
%endif
%endfor
%else:
${render_row( user, groups, roles, ctr, True, '' )}
%endif
%endfor
</table>
%endif
+113
View File
@@ -0,0 +1,113 @@
<%inherit file="/base_panels.mako"/>
<%def name="init()">
<%
self.has_left_panel=True
self.has_right_panel=False
self.active_view="admin"
%>
</%def>
<%def name="stylesheets()">
${parent.stylesheets()}
## TODO: Clean up these styles and move into panel_layout.css (they are
## used here and in the editor).
<style type="text/css">
#left {
background: #C1C9E5 url(${h.url_for('/static/style/menu_bg.png')}) top repeat-x;
}
div.toolMenu {
margin: 5px;
margin-left: 10px;
margin-right: 10px;
}
div.toolSectionPad {
margin: 0;
padding: 0;
height: 5px;
font-size: 0px;
}
div.toolSectionDetailsInner {
margin-left: 5px;
margin-right: 5px;
}
div.toolSectionTitle {
padding-bottom: 0px;
font-weight: bold;
}
div.toolMenuGroupHeader {
font-weight: bold;
padding-top: 0.5em;
padding-bottom: 0.5em;
color: #333;
font-style: italic;
border-bottom: dotted #333 1px;
margin-bottom: 0.5em;
}
div.toolTitle {
padding-top: 5px;
padding-bottom: 5px;
margin-left: 16px;
margin-right: 10px;
display: list-item;
list-style: square outside;
}
a:link, a:visited, a:active
{
color: #303030;
}
</style>
</%def>
<%def name="left_panel()">
<div class="unified-panel-header" unselectable="on">
<div class='unified-panel-header-inner'>Administration</div>
</div>
<div class="unified-panel-body" style="overflow: auto;">
<div class="toolMenu">
<div class="toolSectionList">
<div class="toolSectionTitle">
<span>Security</span>
</div>
<div class="toolSectionBody">
<div class="toolSectionBg">
<div class="toolTitle"><a href="${h.url_for( action='users' )}" target="galaxy_main">Manage users</a></div>
<div class="toolTitle"><a href="${h.url_for( action='groups' )}" target="galaxy_main">Manage groups</a></div>
<div class="toolTitle"><a href="${h.url_for( action='roles' )}" target="galaxy_main">Manage roles</a></div>
</div>
</div>
<div class="toolSectionPad"></div>
<div class="toolSectionTitle">
<span>Data</span>
</div>
<div class="toolSectionBody">
<div class="toolSectionBg">
<div class="toolTitle"><a href="${h.url_for( action='libraries' )}" target="galaxy_main">Manage libraries</a></div>
</div>
</div>
<div class="toolSectionPad"></div>
<div class="toolSectionTitle">
<span>Tools</span>
</div>
<div class="toolSectionBody">
<div class="toolSectionBg">
<div class="toolTitle"><a href="${h.url_for( action='reload_tool' )}" target="galaxy_main">Reload a tool's configuration</a></div>
</div>
</div>
</div>
</div>
</div>
</%def>
<%def name="center_panel()">
<iframe name="galaxy_main" id="galaxy_main" frameborder="0" style="position: absolute; width: 100%; height: 100%;" src="${h.url_for( action='center' )}"> </iframe>
</%def>
@@ -0,0 +1,30 @@
<%inherit file="/base.mako"/>
<%def name="title()">Add Dataset to Library from History</%def>
%if error_msg:
<p>
<div class="errormessage">${error_msg}</div>
<div style="clear: both"></div>
</p>
%endif
%if ok_msg:
<p>
<div class="donemessage">${ok_msg}</div>
<div style="clear: both"></div>
</p>
%endif
<p/>
<div class="toolForm">
<div class="toolFormTitle">Active Datasets in your current history (${history.name})</div>
<div class="toolFormBody">
<form name="add_dataset_from_history">
<input type="hidden" name="folder_id" value="${folder.id}">
%for dataset in history.active_datasets:
<div class="form-row">
<input name="ids" value="${dataset.id}" type="checkbox">${dataset.hid}: ${dataset.name}
</div>
%endfor
<input type="submit" name="submit" value="Add Datasets">
</form>
</div>
</div>
+184
View File
@@ -0,0 +1,184 @@
<%inherit file="/base.mako"/>
<%namespace file="common.mako" import="render_dataset" />
<%def name="title()">Import from Library</%def>
<%def name="stylesheets()">
<link href="${h.url_for('/static/style/base.css')}" rel="stylesheet" type="text/css" />
<link href="${h.url_for('/static/style/library.css')}" rel="stylesheet" type="text/css" />
</%def>
<script type="text/javascript">
//var q = jQuery.noConflict();
$( document ).ready( function () {
// Hide all the folder contents
$("ul").filter("ul#subFolder").hide();
// Handle the hide/show triangles
$("li.libraryOrFolderRow").wrap( "<a href='#' class='expandLink'></a>" ).click( function() {
var contents = $(this).parent().next("ul");
if ( this.id == "libraryRow" ) {
var icon_open = "${h.url_for( '/static/images/silk/book_open.png' )}";
var icon_closed = "${h.url_for( '/static/images/silk/book.png' )}";
} else {
var icon_open = "${h.url_for( '/static/images/silk/folder_page.png' )}";
var icon_closed = "${h.url_for( '/static/images/silk/folder.png' )}";
}
if ( contents.is(":visible") ) {
contents.slideUp("fast");
$(this).children().find("img.expanderIcon").each( function() { this.src = "${h.url_for( '/static/images/silk/resultset_next.png' )}"; });
$(this).children().find("img.rowIcon").each( function() { this.src = icon_closed; });
} else {
contents.slideDown("fast");
$(this).children().find("img.expanderIcon").each( function() { this.src = "${h.url_for( '/static/images/silk/resultset_bottom.png' )}"; });
$(this).children().find("img.rowIcon").each( function() { this.src = icon_open; });
}
});
// Hide all dataset bodies
$("div.historyItemBody").hide();
// Handle the dataset body hide/show link.
$("div.historyItemWrapper").each( function() {
var id = this.id;
var li = $(this).parent();
var body = $(this).children( "div.historyItemBody" );
var peek = body.find( "pre.peek" )
$(this).children( ".historyItemTitleBar" ).find( ".historyItemTitle" ).wrap( "<a href='#'></a>" ).click( function() {
if ( body.is(":visible") ) {
if ( $.browser.mozilla ) { peek.css( "overflow", "hidden" ) }
body.slideUp( "fast" );
li.removeClass( "datasetHighlighted" );
}
else {
body.slideDown( "fast", function() {
if ( $.browser.mozilla ) { peek.css( "overflow", "auto" ); }
});
li.addClass( "datasetHighlighted" );
}
return false;
});
});
});
function checkForm() {
if ( $("select#with-selected-select option:selected").text() == "delete" ) {
if ( confirm( "Are you sure you want to delete these datasets?" ) ) {
return true;
} else {
return false;
}
}
}
</script>
<%def name="render_folder( parent, parent_pad )">
<%
##if not trans.app.security_agent.check_folder_contents( trans.user, parent ):
## return ""
pad = parent_pad + 20
if parent_pad == 0:
expander = "/static/images/silk/resultset_bottom.png"
folder = "/static/images/silk/folder_page.png"
subfolder = False
else:
expander = "/static/images/silk/resultset_next.png"
folder = "/static/images/silk/folder.png"
subfolder = True
%>
<li class="folderRow libraryOrFolderRow" style="padding-left: ${pad}px;">
<div class="rowTitle">
<img src="${h.url_for( expander )}" class="expanderIcon"/><img src="${h.url_for( folder )}" class="rowIcon"/>
${parent.name}
%if parent.description:
<i>- ${parent.description}</i>
%endif
<a id="folder-${parent.id}-popup" class="popup-arrow" style="display: none;">&#9660;</a>
</div>
<div popupmenu="folder-${parent.id}-popup">
<a class="action-button" href="${h.url_for( action='dataset', folder_id=parent.id )}">Add new dataset to this folder</a>
<a class="action-button" href="${h.url_for( action='add_dataset_to_folder_from_history', folder_id=parent.id )}">Copy a dataset from your history to this folder</a>
<a class="action-button" href="${h.url_for( action='folder', new=True, id=parent.id )}">Create a new subfolder in this folder</a>
<a class="action-button" href="${h.url_for( action='folder', rename=True, id=parent.id )}">Rename this folder</a>
%if subfolder:
<a class="action-button" confirm="Are you sure you want to delete folder '${parent.name}'?" href="${h.url_for( action='folder', delete=True, id=parent.id )}">Remove this folder and its contents</a>
%endif
</div>
</li>
%if subfolder:
<ul id="subFolder">
%else:
<ul>
%endif
%for folder in parent.active_folders:
${render_folder( folder, pad )}
%endfor
%for dataset in parent.active_datasets:
##%if trans.app.security_agent.allow_action( trans.user, trans.app.security_agent.permitted_actions.DATASET_ACCESS, dataset=dataset.dataset ):
<li class="datasetRow" style="padding-left: ${pad + 18}px;">${render_dataset( dataset )}</li>
##%endif
%endfor
</ul>
</%def>
<h2>Libraries</h2>
%if message:
<%
try:
messagetype
except:
messagetype = "done"
%>
<p />
<div class="${messagetype}message">
${message}
</div>
<p />
%endif
<ul class="manage-table-actions">
<li>
<a class="action-button" href="${h.url_for( action='library', new=True )}">
<img src="${h.url_for( '/static/images/silk/add.png' )}" />
<span>Create a new library</span>
</a>
</li>
</ul>
<form name="update_multiple_datasets" action="${h.url_for( action='datasets' )}" onSubmit="javascript:return checkForm();" method="post">
<ul>
%if libraries:
%for library in libraries:
##%if trans.app.security_agent.check_folder_contents( trans.user, library ):
<li class="libraryRow libraryOrFolderRow" id="libraryRow"><div class="rowTitle"><table cellspacing="0" cellpadding="0" border="0" width="100%" class="libraryTitle"><tr>
<th width="*">
<img src="${h.url_for( '/static/images/silk/resultset_bottom.png' )}" class="expanderIcon"/><img src="${h.url_for( '/static/images/silk/book_open.png' )}" class="rowIcon"/>
${library.name}
%if library.description:
<i>- ${library.description}</i>
%endif
<a id="library-${library.id}-popup" class="popup-arrow" style="display: none;">&#9660;</a>
<div popupmenu="library-${library.id}-popup">
<a class="action-button" href="${h.url_for( action='library', rename=True, id=library.id )}">Rename this library</a>
<a class="action-button" confirm="Are you sure you want to delete library '${library.name}'?" href="${h.url_for( action='library', delete=True, id=library.id )}">Remove this library and its contents</a>
</div>
</th>
<th width="100">Format</th>
<th width="50">Db</th>
<th width="200">Info</th>
</tr></table></div></li>
<ul>
${render_folder( library.root_folder, 0 )}
</ul>
<br/>
##%endif
%endfor
</ul>
<div style="float: right;">
With selected datasets:
<select name="action" id="with-selected-select">
<option value="None" selected></option>
<option value="edit">edit permissions</option>
<option value="delete">delete</option>
</select>
<input type="submit" class="primary-button" name="with-selected" id="with-selected-submit" value="go"/>
</div>
</form>
%else:
There are no libraries.
%endif
+100
View File
@@ -0,0 +1,100 @@
<%doc>
Shamelessly stolen from history... this needs to be cleaned up to remove a
bunch of stuff that doesn't apply to library datasets (like state, etc).
</%doc>
## Render the dataset `data`
<%def name="render_dataset( data )">
<%
if data.state in ['no state','',None]:
data_state = "queued"
else:
data_state = data.state
%>
##%if not trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_ACCESS, dataset = data.dataset ):
## <div class="historyItemWrapper historyItem historyItem-${data_state} historyItem-noPermission" id="historyItem-${data.id}">
##%else:
<div class="historyItemWrapper historyItem historyItem-${data_state}" id="historyItem-${data.id}">
##%endif
## Header row for history items (name, state, action buttons)
<div style="overflow: hidden;" class="historyItemTitleBar">
<div style="float: left; padding-right: 3px;">
<div style='display: none;' id="progress-${data.id}">
<img src="${h.url_for('/static/style/data_running.gif')}" border="0" align="middle" >
</div>
%if data_state == 'running':
<div><img src="${h.url_for('/static/style/data_running.gif')}" border="0" align="middle"></div>
%elif data_state != 'ok':
<div><img src="${h.url_for( "/static/style/data_%s.png" % data_state )}" border="0" align="middle"></div>
%endif
</div>
<%doc>
<div style="float: right;">
<a href="${h.url_for( controller='dataset', dataset_id=data.id, action='display', filename='index')}" target="galaxy_main"><img src="${h.url_for('/static/images/eye_icon.png')}" rollover="${h.url_for('/static/images/eye_icon_dark.png')}" width='16' height='16' alt='display data' title='display data' class='displayButton' border='0'></a>
<a href="${h.url_for( action='edit', id=data.id )}" target="galaxy_main"><img src="${h.url_for('/static/images/pencil_icon.png')}" rollover="${h.url_for('/static/images/pencil_icon_dark.png')}" width='16' height='16' alt='edit attributes' title='edit attributes' class='editButton' border='0'></a>
<a href="${h.url_for( action='delete', id=data.id )}" class="historyItemDelete" id="historyItemDelter-${data.id}"><img src="${h.url_for('/static/images/delete_icon.png')}" rollover="${h.url_for('/static/images/delete_icon_dark.png')}" width='16' height='16' alt='delete' class='deleteButton' border='0'></a>
</div>
</%doc>
<table cellspacing="0" cellpadding="0" border="0" width="100%"><tr>
<td width="*">
<input type="checkbox" name="dataset_ids" value="${data.id}"/>
<span class="historyItemTitle"><b>${data.display_name()}</b></span>
<a id="dataset-${data.id}-popup" class="popup-arrow" style="display: none;">&#9660;</a>
<div popupmenu="dataset-${data.id}-popup">
<a class="action-button" href="${h.url_for( action='dataset', id=data.id )}">Edit this dataset's attributes and permissions</a>
<a class="action-button" confirm="Are you sure you want to delete dataset '${data.name}'?" href="${h.url_for( action='dataset', delete=True, id=data.id )}">Remove this dataset</a>
</div>
</td>
<td width="100">${data.ext}</td>
<td width="50"><span class="${data.dbkey}">${data.dbkey}</span></td>
<td width="200">${data.info}</td>
</tr></table>
</div>
## Body for history items, extra info and actions, data "peek"
<div id="info${data.id}" class="historyItemBody">
<div>
${data.blurb}
</div>
<div>
%if data.has_data:
<a href="${h.url_for( action='display', id=data.id, tofile='yes', toext='data.ext' )}" target="_blank">save</a>
%for display_app in data.datatype.get_display_types():
<% display_links = data.datatype.get_display_links( data, display_app, app, request.base ) %>
%if len( display_links ) > 0:
| ${data.datatype.get_display_label(display_app)}
%for display_name, display_link in display_links:
<a target="_blank" href="${display_link}">${display_name}</a>
%endfor
%endif
%endfor
%endif
</div>
%if data.peek != "no peek":
<div><pre id="peek${data.id}" class="peek">${data.display_peek()}</pre></div>
%endif
## Recurse for child datasets
%if len( data.children ) > 0:
## FIXME: This should not be in the template, there should
## be a 'visible_children' method on dataset.
<%
children = []
for child in data.children:
if child.visible:
children.append( child )
%>
%if len( children ) > 0:
<div>
There are ${len( children )} secondary datasets.
%for idx, child in enumerate(children):
${render_dataset( child, idx + 1 )}
%endfor
</div>
%endif
%endif
</div>
</div>
</%def>
+99
View File
@@ -0,0 +1,99 @@
<%inherit file="/base.mako"/>
<%namespace file="/dataset/security_common.mako" import="render_permission_form" />
<%def name="title()">Edit Dataset Attributes</%def>
<%def name="datatype( dataset, datatypes )">
<select name="datatype">
## $datatypes.sort()
%for ext in datatypes:
%if dataset.ext == ext:
<option value="${ext}" selected="yes">${ext}</option>
%else:
<option value="${ext}">${ext}</option>
%endif
%endfor
</select>
</%def>
%if isinstance( dataset, list ):
${render_permission_form( dataset[0].dataset, h.url_for( action='dataset' ), 'id', ",".join( [ str(d.id) for d in dataset ] ), trans.app.model.Role.query().all() )}
%else:
${render_permission_form( dataset.dataset, h.url_for( action='dataset' ), 'id', dataset.id, trans.app.model.Role.query().all() )}
%endif
%if not isinstance( dataset, list ):
<div class="toolForm">
<div class="toolFormTitle">Edit Attributes</div>
<div class="toolFormBody">
<form name="edit_attributes" action="${h.url_for( controller='admin', action='dataset' )}" method="post">
<input type="hidden" name="id" value="${dataset.id}">
<div class="form-row">
<label>Name:</label>
<div style="float: left; width: 250px; margin-right: 10px;">
<input type="text" name="name" value="${dataset.name}" size="40">
</div>
<div style="clear: both"></div>
</div>
<div class="form-row">
<label>Info:</label>
<div style="float: left; width: 250px; margin-right: 10px;">
<input type="text" name="info" value="${dataset.info}" size="40">
</div>
<div style="clear: both"></div>
</div>
%for name, spec in dataset.metadata.spec.items():
%if spec.visible:
<div class="form-row">
<label>${spec.desc}:</label>
<div style="float: left; width: 250px; margin-right: 10px;">
${dataset.metadata.get_html_by_name( name )}
</div>
<div style="clear: both"></div>
</div>
%endif
%endfor
<div class="form-row">
<input type="submit" name="save" value="Save">
</div>
</form>
<form name="auto_detect" action="${h.url_for( controller='admin', action='dataset' )}" method="post">
<input type="hidden" name="id" value="${dataset.id}">
<div style="float: left; width: 250px; margin-right: 10px;">
<input type="submit" name="detect" value="Auto-detect">
</div>
<div class="toolParamHelp" style="clear: both;">
This will inspect the dataset and attempt to correct the above column values
if they are not accurate.
</div>
</form>
</div>
</div>
<p/>
<div class="toolForm">
<div class="toolFormTitle">Change data type</div>
<div class="toolFormBody">
<form name="change_datatype" action="${h.url_for( controller='admin', action='dataset' )}" method="post">
<input type="hidden" name="id" value="${dataset.id}">
<div class="form-row">
<label>New Type:</label>
<div style="float: left; width: 250px; margin-right: 10px;">
${datatype( dataset, datatypes )}
</div>
<div class="toolParamHelp" style="clear: both;">
This will change the datatype of the existing dataset
but <i>not</i> modify its contents. Use this if Galaxy
has incorrectly guessed the type of your dataset.
</div>
<div style="clear: both"></div>
</div>
<div class="form-row">
<input type="submit" name="change" value="Save">
</div>
</form>
</div>
</div>
<p/>
%endif
<a href="${h.url_for( controller='admin', action='library_browser' )}">Return to the library browser</a>
+101
View File
@@ -0,0 +1,101 @@
<%inherit file="/base.mako"/>
<% import os %>
<%def name="title()">Create New Library Dataset</%def>
%if msg:
<p class="ok_bgr">${msg}</p></td></tr>
%endif
<div class="toolForm" id="new_dataset">
<div class="toolFormTitle">Create a new Library Dataset</div>
<div class="toolFormBody">
<form name="tool_form" action="${h.url_for( controller='admin', action='dataset' )}" enctype="multipart/form-data" method="post">
<input type="hidden" name="folder_id" value="${folder_id}">
<div class="form-row">
<label>File:</label>
<div style="float: left; width: 250px; margin-right: 10px;"><input type="file" name="file_data"></div>
<div class="toolParamHelp" style="clear: both;">
Upload a single file. Use the "Server Directory" feature below to upload an entire directory of files.
</div>
<div style="clear: both"></div>
</div>
<div class="form-row">
<label>URL/Text:</label>
<div style="float: left; width: 250px; margin-right: 10px;"><textarea name="url_paste" rows="5" cols="35"></textarea></div>
<div class="toolParamHelp" style="clear: both;">
Specify a list of URLs (one per line) or paste the contents of a file.
</div>
<div style="clear: both"></div>
</div>
%if trans.app.config.library_import_dir is not None:
<div class="form-row">
<label>Server Directory</label>
<div style="float: left; width: 250px; margin-right: 10px;">
<select name="server_dir">
<option>None</option>
%for dir in os.listdir( trans.app.config.library_import_dir ):
<option>${dir}</option>
%endfor
</select>
</div>
<div class="toolParamHelp" style="clear: both;">
Upload all files in a subdirectory of <strong>${trans.app.config.library_import_dir}</strong> on the Galaxy server.
</div>
<div style="clear: both"></div>
</div>
%endif
<div class="form-row">
<label>Convert spaces to tabs:</label>
<div style="float: left; width: 250px; margin-right: 10px;"><div><input type="checkbox" name="space_to_tab" value="Yes">Yes</div></div>
<div class="toolParamHelp" style="clear: both;">
Use this option if you are manually entering intervals.
</div>
<div style="clear: both"></div>
</div>
<div class="form-row">
<label>File Format:</label>
<div style="float: left; width: 250px; margin-right: 10px;">
<select name="extension">
<option value="auto" selected>Auto-detect</option>
%for file_format in file_formats:
<option value="${file_format}">${file_format}</option>
%endfor
</select>
</div>
<div style="clear: both"></div>
</div>
<div class="form-row">
<label>Genome:</label>
<div style="float: left; width: 250px; margin-right: 10px;">
<select name="dbkey">
%for dbkey in dbkeys:
%if dbkey[1] == last_used_build:
<option value="${dbkey[1]}" selected>${dbkey[0]}</option>
%else:
<option value="${dbkey[1]}">${dbkey[0]}</option>
%endif
%endfor
</select>
</div>
<div style="clear: both"></div>
</div>
<div class="form-row">
<div style="float: left; width: 250px; margin-right: 10px;">
<label>Restrict dataset access to specific roles:</label>
<select name="roles" multiple="true" size="5">
%for role in roles:
<option value="${role.id}">${role.name}</option>
%endfor
</select>
</div>
<div class="toolParamHelp" style="clear: both;">
Multi-select list - hold the appropriate key while clicking to select multiple roles. More restrictions can be applied after the upload is complete. Selecting no roles makes a dataset public.
</div>
</div>
<div style="clear: both"></div>
<div class="form-row">
<input type="submit" class="primary-button" name="create_dataset" value="Add Dataset(s) to Folder">
</div>
</form>
</div>
</div>
+29
View File
@@ -0,0 +1,29 @@
<%inherit file="/base.mako"/>
%if msg:
<div class="donemessage">${msg}</div>
%endif
<div class="toolForm">
<div class="toolFormTitle">Reload Tool</div>
<div class="toolFormBody">
<form name="tool_reload" action="${h.url_for( controller='admin', action='tool_reload' )}" method="post" >
<div class="form-row">
<label>
Tool to reload:
</label>
<select name="tool_id">
%for i, section in enumerate( toolbox.sections ):
<optgroup label="${section.name}">
%for t in section.tools:
<option value="${t.id}">${t.name}</option>
%endfor
%endfor
</select>
</div>
<div class="form-row">
<button name="action" value="tool_reload">Reload</button>
</div>
</form>
</div>
</div>
-33
View File
@@ -1,33 +0,0 @@
<%inherit file="/base.mako"/>
<%def name="title()">Galaxy Administration</%def>
<table align="center" width="70%" class="border" cellpadding="5" cellspacing="5">
<tr>
<td>
<h3 align="center">Galaxy Administration</h3>
%if msg:
<p class="ok_bgr">${msg}</p>
%endif
</td>
</tr>
<tr>
<td>
<form method="post" action="admin">
<p>Admin password: <input type="password" name="passwd" size="8"></p>
<p>
Reload tool:
<select name="tool_id">
%for i, section in enumerate( toolbox.sections ):
<optgroup label="${section.name}">
%for t in section.tools:
<option value="${t.id}">${t.name}</option>
%endfor
%endfor
</select>
<button name="action" value="tool_reload">Reload</button>
</p>
</form>
</td>
</tr>
</table>
+52 -4
View File
@@ -1,6 +1,18 @@
## This needs to be on the first line, otherwise IE6 goes into quirks mode
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%
self.has_left_panel=True
self.has_right_panel=True
self.message_box_visible=False
self.message_box_class=""
self.active_view=None
%>
<%def name="init()">
## Override
</%def>
## Default title
<%def name="title()">Galaxy</%def>
@@ -8,6 +20,22 @@
<%def name="stylesheets()">
<link rel="stylesheet" type="text/css" href="${h.url_for('/static/style/reset.css')}" />
<link rel="stylesheet" type="text/css" href="${h.url_for('/static/style/panel_layout.css')}" />
<style type="text/css">
#center {
%if not self.has_left_panel:
left: 0;
%endif
%if not self.has_right_panel:
right: 0;
%endif
}
%if self.message_box_visible:
#left, #left-border, #center, #right-border, #right
{
top: 64px;
}
%endif
</style>
</%def>
## Default javascripts
@@ -27,19 +55,30 @@
<script type="text/javascript" src="${h.url_for('/static/scripts/galaxy.panels.js')}"></script>
<script type="text/javascript">
ensure_dd_helper();
var lp = make_left_panel( $("#left"), $("#center"), $("#left-border" ) );
var rp = make_right_panel( $("#right"), $("#center"), $("#right-border" ) );
handle_minwidth_hint = rp.handle_minwidth_hint;
%if self.has_left_panel:
var lp = make_left_panel( $("#left"), $("#center"), $("#left-border" ) );
%endif
%if self.has_right_panel:
var rp = make_right_panel( $("#right"), $("#center"), $("#right-border" ) );
handle_minwidth_hint = rp.handle_minwidth_hint;
%endif
</script>
</%def>
## Masthead
<%def name="masthead()">
<iframe name="galaxy_masthead" src="${h.url_for( 'masthead' )}" width="38" height="100%" frameborder="0" scroll="no" style="margin: 0; border: 0 none; width: 100%; height: 38px; overflow: hidden;"> </iframe>
<iframe name="galaxy_masthead" src="${h.url_for( controller='root', action='masthead', active_view=self.active_view )}" width="38" height="100%" frameborder="0" scroll="no" style="margin: 0; border: 0 none; width: 100%; height: 38px; overflow: hidden;"> </iframe>
</%def>
## Messagebox
<%def name="message_box_content()">
</%def>
## Document
<html lang="en">
${self.init()}
<head>
<title>${self.title()}</title>
${self.javascripts()}
@@ -53,17 +92,26 @@
<div id="masthead">
${self.masthead()}
</div>
<div id="messagebox" class="panel-${self.message_box_class}-message">
%if self.message_box_visible:
${self.message_box_content()}
%endif
</div>
%if self.has_left_panel:
<div id="left">
${self.left_panel()}
</div>
<div id="left-border"><div id="left-border-inner" style="display: none;"></div></div>
%endif
<div id="center">
${self.center_panel()}
</div>
%if self.has_right_panel:
<div id="right-border"><div id="right-border-inner" style="display: none;"></div></div>
<div id="right">
${self.right_panel()}
</div>
%endif
## Allow other body level elements
${next.body()}
</body>
+96 -39
View File
@@ -1,5 +1,5 @@
<%inherit file="/base.mako"/>
<%def name="title()">History Item Attributes</%def>
<%def name="title()">Edit Dataset Attributes</%def>
<%def name="datatype( dataset, datatypes )">
@@ -15,11 +15,19 @@
</select>
</%def>
<%
if isinstance( data, trans.app.model.HistoryDatasetAssociation ):
id_name = 'id'
elif isinstance( data, trans.app.model.LibraryFolderDatasetAssociation ):
id_name = 'lid'
%>
%if ( id_name == 'id' or trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_EDIT_METADATA, dataset = data ) ):
<div class="toolForm">
<div class="toolFormTitle">Edit Attributes</div>
<div class="toolFormBody">
<form name="edit_attributes" action="${h.url_for( action='edit' )}" method="post">
<input type="hidden" name="id" value="${data.id}">
<input type="hidden" name="${id_name}" value="${data.id}">
<div class="form-row">
<label>
Name:
@@ -56,7 +64,7 @@
</div>
</form>
<form name="auto_detect" action="${h.url_for( action='edit' )}" method="post">
<input type="hidden" name="id" value="${data.id}">
<input type="hidden" name="${id_name}" value="${data.id}">
<div style="float: left; width: 250px; margin-right: 10px;">
<input type="submit" name="detect" value="Auto-detect">
</div>
@@ -73,39 +81,41 @@
<p />
<% converters = data.get_converter_types() %>
%if len( converters ) > 0:
<div class="toolForm">
<div class="toolFormTitle">Convert to new format</div>
<div class="toolFormBody">
<form name="convert_data" action="${h.url_for( action='edit' )}" method="post">
<input type="hidden" name="id" value="${data.id}">
<div class="form-row">
<label>
Convert to:
</label>
<div style="float: left; width: 250px; margin-right: 10px;">
<select name="target_type">
%for key, value in converters.items():
<option value="${key}">${value.name[8:]}</option>
%endfor
</select>
%if id_name == 'id':
<% converters = data.get_converter_types() %>
%if len( converters ) > 0:
<div class="toolForm">
<div class="toolFormTitle">Convert to new format</div>
<div class="toolFormBody">
<form name="convert_data" action="${h.url_for( action='edit' )}" method="post">
<input type="hidden" name="${id_name}" value="${data.id}">
<div class="form-row">
<label>
Convert to:
</label>
<div style="float: left; width: 250px; margin-right: 10px;">
<select name="target_type">
%for key, value in converters.items():
<option value="${key}">${value.name[8:]}</option>
%endfor
</select>
</div>
<div class="toolParamHelp" style="clear: both;">
This will create a new dataset with the contents of this
dataset converted to a new format.
</div>
<div style="clear: both"></div>
</div>
<div class="toolParamHelp" style="clear: both;">
This will create a new dataset with the contents of this
dataset converted to a new format.
<div class="form-row">
<input type="submit" name="convert_data" value="Convert">
</div>
<div style="clear: both"></div>
</div>
<div class="form-row">
<input type="submit" name="convert_data" value="Convert">
</div>
</form>
</div>
</div>
<p />
</form>
</div>
</div>
<p />
%endif
%endif
@@ -113,7 +123,7 @@
<div class="toolFormTitle">Change data type</div>
<div class="toolFormBody">
<form name="change_datatype" action="${h.url_for( action='edit' )}" method="post">
<input type="hidden" name="id" value="${data.id}">
<input type="hidden" name="${id_name}" value="${data.id}">
<div class="form-row">
<label>
New Type:
@@ -136,11 +146,58 @@
</div>
</div>
<p>
<p />
%else:
<div class="toolForm">
<div class="toolFormTitle">Copy History Item</div>
<div class="toolFormTitle">View Attributes</div>
<div class="toolFormBody">
Click <a href="${h.url_for( controller='dataset', action='copy_datasets', source_dataset_ids=data.id, target_history_ids=data.history_id )}" target="galaxy_main">here</a> to make a copy of this history item.
<div class="form-row">
<strong>Name:</strong> ${data.name}
<div style="clear: both"></div>
<strong>Info:</strong> ${data.info}
<div style="clear: both"></div>
<strong>Data Format:</strong> ${data.ext}
<div style="clear: both"></div>
%for element in metadata:
<strong>${element.spec.desc}:</strong> ${element.value[0]}
<div style="clear: both"></div>
%endfor
</div>
</div>
</div>
</p>
<p />
%endif
%if trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset = data ):
<%namespace file="/dataset/security_common.mako" import="render_permission_form" />
${render_permission_form( data.dataset, h.url_for( action='edit' ), id_name, data.id, trans.user.all_roles() )}
%elif trans.user:
<div class="toolForm">
<div class="toolFormTitle">View permissions</div>
<div class="toolFormBody">
<div class="form-row">
%if data.dataset.actions:
<ul>
%for action, roles in trans.app.security_agent.get_dataset_permissions( data.dataset ).items():
%if roles:
<li>${action.description}</li>
<ul>
%for role in roles:
<li>${role.name}</li>
%endfor
</ul>
%endif
%endfor
</ul>
%else:
<p>This dataset is accessible by everyone (it is public).</p>
%endif
</div>
</div>
</div>
%endif
+85
View File
@@ -0,0 +1,85 @@
<%def name="render_select( current_actions, action_key, action, all_roles )">
<%
in_roles = []
for a in current_actions:
if a.action == action.action:
in_roles.append( a.role )
out_roles = filter( lambda x: x not in in_roles, all_roles )
%>
<p><label>${action.action}:</label> ${action.description}</p>
<div style="float: left; margin-right: 10px;">
Roles associated:<br/>
<select name="${action_key}_in" id="${action_key}_in_select" class="in_select" style="width: 200px; height: 150px;" multiple>
%for role in in_roles:
<option value="${role.id}">${role.name}</option>
%endfor
</select> <br/>
<input type="submit" id="${action_key}_remove_button" class="role_remove_button" value=">>"/>
</div>
<div>
Roles not associated:<br/>
<select name="${action_key}_out" id="${action_key}_out_select" style="width: 200px; height: 150px;" multiple>
%for role in out_roles:
<option value="${role.id}">${role.name}</option>
%endfor
</select> <br/>
<input type="submit" id="${action_key}_add_button" class="role_add_button" value="<<"/>
</div>
</%def>
<%def name="render_permission_form( obj, form_url, id_name, id, all_roles )">
<%
if isinstance( obj, trans.app.model.User ):
current_actions = obj.default_permissions
elif isinstance( obj, trans.app.model.History ):
current_actions = obj.default_permissions
elif isinstance( obj, trans.app.model.Dataset ):
current_actions = obj.actions
else:
current_actions = obj.dataset.actions
%>
<script type="text/javascript">
var q = jQuery.noConflict();
q( document ).ready( function () {
q('.role_add_button').click(function() {
var action = this.id.substring( 0, this.id.lastIndexOf( '_add_button' ) )
var in_select = '#' + action + '_in_select';
var out_select = '#' + action + '_out_select';
return !q(out_select + ' option:selected').remove().appendTo(in_select);
});
q('.role_remove_button').click(function() {
var action = this.id.substring( 0, this.id.lastIndexOf( '_remove_button' ) )
var in_select = '#' + action + '_in_select';
var out_select = '#' + action + '_out_select';
return !q(in_select + ' option:selected').remove().appendTo(out_select);
});
q('form#edit_role_associations').submit(function() {
q('.in_select option').each(function(i) {
q(this).attr("selected", "selected");
});
});
});
</script>
<div class="toolForm">
<div class="toolFormTitle">Associate with roles and set permissions</div>
<div class="toolFormBody">
<form name="edit_role_associations" id="edit_role_associations" action="${form_url}" method="post">
<input type="hidden" name="${id_name}" value="${id}">
<div class="form-row">
</div>
%for k, v in trans.app.model.Dataset.permitted_actions.items():
<div class="form-row">
${render_select( current_actions, k, v, all_roles )}
</div>
%endfor
<div class="form-row">
<input type="submit" name="update_roles" value="Save"/>
</div>
</form>
</div>
</div>
<p/>
</%def>
+2
View File
@@ -21,9 +21,11 @@ $(function(){
cls += " form-row-error"
%>
<div class="${cls}">
%if input.use_label:
<label>
${input.label}:
</label>
%endif
<div style="float: left; width: 250px; margin-right: 10px;">
<input type="${input.type}" name="${input.name}" value="${input.value}" size="40">
</div>
+1
View File
@@ -17,6 +17,7 @@
<li><a href="${h.url_for('/history_new')}">Create</a> a new empty history</li>
%endif
<li><a href="${h.url_for( controller='workflow', action='build_from_current_history' )}">Construct workflow</a> from the current history</li>
<li><a href="${h.url_for( action='history_set_default_permissions' )}">Change default permissions</a> for the current history</li>
<li><a href="${h.url_for( action='history_share' )}" target="galaxy_main">Share</a> current history</div>
%endif
<li><a href="${h.url_for( action='history', show_deleted=True)}" target="galaxy_history">Show deleted</a> datasets in history</li>
+7
View File
@@ -0,0 +1,7 @@
<%inherit file="/base.mako"/>
<%def name="title()">Change Default Permissions on New Datasets in This History</%def>
<%namespace file="/dataset/security_common.mako" import="render_permission_form" />
%if trans.user:
${render_permission_form( trans.history, h.url_for(), 'id', None, trans.user.all_roles() )}
%endif
+88 -1
View File
@@ -1,6 +1,7 @@
<%inherit file="/base.mako"/>
<%def name="title()">Share histories</%def>
%if not can_change and not cannot_change:
<div class="toolForm">
<div class="toolFormTitle">Share Histories</div>
<table>
@@ -27,4 +28,90 @@
<tr><td colspan="2" align="right"><input type="submit" name="history_share_btn" value="Submit"></td></tr>
</form>
</table>
</div>
</div>
%else:
<style type="text/css">
th
{
text-align: left;
}
td
{
vertical-align: top;
}
</style>
<form action="${h.url_for( action='history_share' )}" method="post">
%for history in histories:
<input type="hidden" name="id" value="${history.id}">
%endfor
<input type="hidden" name="email" value="${email}">
<div class="warningmessage">
The history or histories you've chosen to share contain datasets that the user you're sharing with does not have permission to access. These datasets are shown below. Datasets which the user already has permission to access are not shown.
</div>
<p/>
%if can_change:
<div class="donemessage">
The following datasets can be shared with ${email} by updating their permissions:
<p/>
<table cellpadding="0" cellspacing="8" border="0">
<tr><th>Histories</th><th>Datasets</th></tr>
%for history, datasets in can_change.items():
<tr>
<td>${history.name}</td>
<td>
%for dataset in datasets:
${dataset.name}<br/>
%endfor
</td>
</tr>
%endfor
</table>
</div>
<p/>
%endif
%if cannot_change:
<div class="errormessage">
The following datasets cannot be shared with ${email} because you do not have permission to change the permissions on them.
<p/>
<table cellpadding="0" cellspacing="8" border="0">
<tr><th>Histories</th><th>Datasets</th></tr>
%for history, datasets in cannot_change.items():
<tr>
<td>${history.name}</td>
<td>
%for dataset in datasets:
${dataset.name}<br/>
%endfor
</td>
</tr>
%endfor
</table>
</div>
<p/>
%endif
<div>
<b>How would you like to proceed?</b>
<p/>
%if can_change:
<input type="radio" name="action" value="public"> Set datasets above to public access
%if cannot_change:
(where possible)
%endif
<br/>
<input type="radio" name="action" value="private"> Set datasets above to private access for me and the user(s) with whom I am sharing
%if cannot_change:
(where possible)
%endif
<br/>
%endif
<input type="radio" name="action" value="share"> Share anyway
%if can_change:
(don't change any permissions)
%endif
<br/>
<input type="radio" name="action" value="no_share"> Don't share<br/>
<br/>
<input type="submit" name="submit" value="Ok"><br/>
</div>
</form>
%endif
+144
View File
@@ -0,0 +1,144 @@
<%inherit file="/base.mako"/>
<%namespace file="common.mako" import="render_dataset" />
<%def name="title()">Import from Library</%def>
<%def name="stylesheets()">
<link href="${h.url_for('/static/style/base.css')}" rel="stylesheet" type="text/css" />
<link href="${h.url_for('/static/style/library.css')}" rel="stylesheet" type="text/css" />
</%def>
<script type="text/javascript">
var q = jQuery.noConflict();
q( document ).ready( function () {
// Hide all the folder contents
q("ul").filter("ul#subFolder").hide();
// Handle the hide/show triangles
q("li.libraryOrFolderRow").wrap( "<a href='#' class='expandLink'></a>" ).click( function() {
var contents = q(this).parent().next("ul");
if ( this.id == "libraryRow" ) {
var icon_open = "${h.url_for( '/static/images/silk/book_open.png' )}";
var icon_closed = "${h.url_for( '/static/images/silk/book.png' )}";
} else {
var icon_open = "${h.url_for( '/static/images/silk/folder_page.png' )}";
var icon_closed = "${h.url_for( '/static/images/silk/folder.png' )}";
}
if ( contents.is(":visible") ) {
contents.slideUp("fast");
q(this).children().find("img.expanderIcon").each( function() { this.src = "${h.url_for( '/static/images/silk/resultset_next.png' )}"; });
q(this).children().find("img.rowIcon").each( function() { this.src = icon_closed; });
} else {
contents.slideDown("fast");
q(this).children().find("img.expanderIcon").each( function() { this.src = "${h.url_for( '/static/images/silk/resultset_bottom.png' )}"; });
q(this).children().find("img.rowIcon").each( function() { this.src = icon_open; });
}
});
// Hide all dataset bodies
q("div.historyItemBody").hide();
// Handle the dataset body hide/show link.
q("div.historyItemWrapper").each( function() {
var id = this.id;
var li = q(this).parent();
var body = q(this).children( "div.historyItemBody" );
var peek = body.find( "pre.peek" )
q(this).children( ".historyItemTitleBar" ).find( ".historyItemTitle" ).wrap( "<a href='#'></a>" ).click( function() {
if ( body.is(":visible") ) {
if ( q.browser.mozilla ) { peek.css( "overflow", "hidden" ) }
body.slideUp( "fast" );
li.removeClass( "datasetHighlighted" );
}
else {
body.slideDown( "fast", function() {
if ( q.browser.mozilla ) { peek.css( "overflow", "auto" ); }
});
li.addClass( "datasetHighlighted" );
}
return false;
});
});
});
</script>
<![if gte IE 7]>
<script type="text/javascript">
q( document ).ready( function() {
// Add rollover effect to any image with a 'rollover' attribute
preload_images = {}
q( "img[@rollover]" ).each( function() {
var r = q(this).attr('rollover');
var s = q(this).attr('src');
preload_images[r] = true;
q(this).hover(
function() { q(this).attr( 'src', r ) },
function() { q(this).attr( 'src', s ) }
)
})
for ( r in preload_images ) { q( "<img>" ).attr( "src", r ) }
})
</script>
<![endif]>
<%def name="render_folder( parent, parent_pad )">
<%
if not trans.app.security_agent.check_folder_contents( trans.user, parent ):
return ""
pad = parent_pad + 20
if parent_pad == 0:
expander = "/static/images/silk/resultset_bottom.png"
folder = "/static/images/silk/folder_page.png"
subfolder = False
else:
expander = "/static/images/silk/resultset_next.png"
folder = "/static/images/silk/folder.png"
subfolder = True
%>
<li class="folderRow libraryOrFolderRow" style="padding-left: ${pad}px;">
<div class="rowTitle">
<img src="${h.url_for( expander )}" class="expanderIcon"/><img src="${h.url_for( folder )}" class="rowIcon"/>
${parent.name}
%if parent.description:
<i>- ${parent.description}</i>
%endif
</div>
</li>
%if subfolder:
<ul id="subFolder">
%else:
<ul>
%endif
%for folder in parent.active_folders:
${render_folder( folder, pad )}
%endfor
%for dataset in parent.active_datasets:
%if trans.app.security_agent.allow_action( trans.user, trans.app.security_agent.permitted_actions.DATASET_ACCESS, dataset=dataset.dataset ):
<li class="datasetRow" style="padding-left: ${pad + 18}px;">${render_dataset( dataset )}</li>
%endif
%endfor
</ul>
</%def>
<h2>Libraries</h2>
<form name="import_from_library" action="${h.url_for( '/library/import_datasets' )}" method="post">
<ul>
%for library in libraries:
%if trans.app.security_agent.check_folder_contents( trans.user, library ):
<li class="libraryRow libraryOrFolderRow" id="libraryRow"><div class="rowTitle"><table cellspacing="0" cellpadding="0" border="0" width="100%" class="libraryTitle"><tr>
<th width="*">
<img src="${h.url_for( '/static/images/silk/resultset_bottom.png' )}" class="expanderIcon"/><img src="${h.url_for( '/static/images/silk/book_open.png' )}" class="rowIcon"/>
${library.name}
%if library.description:
<i>- ${library.description}</i>
%endif
</th>
<th width="100">Format</th>
<th width="50">Db</th>
<th width="200">Info</th>
</tr></table></div></li>
<ul>
${render_folder( library.root_folder, 0 )}
</ul>
<br/>
%endif
%endfor
</ul>
<input type="submit" class="primary-button" name="import_dataset" value="Import selected datasets"/>
</form>
+107
View File
@@ -0,0 +1,107 @@
<%doc>
Shamelessly stolen from history... this needs to be cleaned up to remove a
bunch of stuff that doesn't apply to library datasets (like state, etc).
</%doc>
## Render the dataset `data`
<%def name="render_dataset( data )">
<%
if data.state in ['no state','',None]:
data_state = "queued"
else:
data_state = data.state
%>
%if not trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_ACCESS, dataset = data.dataset ):
<div class="historyItemWrapper historyItem historyItem-${data_state} historyItem-noPermission" id="historyItem-${data.id}">
%else:
<div class="historyItemWrapper historyItem historyItem-${data_state}" id="historyItem-${data.id}">
%endif
## Header row for history items (name, state, action buttons)
<div style="overflow: hidden;" class="historyItemTitleBar">
<div style="float: left; padding-right: 3px;">
<div style='display: none;' id="progress-${data.id}">
<img src="${h.url_for('/static/style/data_running.gif')}" border="0" align="middle" >
</div>
%if data_state == 'running':
<div><img src="${h.url_for('/static/style/data_running.gif')}" border="0" align="middle"></div>
%elif data_state != 'ok':
<div><img src="${h.url_for( "/static/style/data_%s.png" % data_state )}" border="0" align="middle"></div>
%endif
</div>
<%doc>
<div style="float: right;">
<a href="${h.url_for( controller='dataset', dataset_id=data.id, action='display', filename='index')}" target="galaxy_main"><img src="${h.url_for('/static/images/eye_icon.png')}" rollover="${h.url_for('/static/images/eye_icon_dark.png')}" width='16' height='16' alt='display data' title='display data' class='displayButton' border='0'></a>
<a href="${h.url_for( action='edit', id=data.id )}" target="galaxy_main"><img src="${h.url_for('/static/images/pencil_icon.png')}" rollover="${h.url_for('/static/images/pencil_icon_dark.png')}" width='16' height='16' alt='edit attributes' title='edit attributes' class='editButton' border='0'></a>
<a href="${h.url_for( action='delete', id=data.id )}" class="historyItemDelete" id="historyItemDelter-${data.id}"><img src="${h.url_for('/static/images/delete_icon.png')}" rollover="${h.url_for('/static/images/delete_icon_dark.png')}" width='16' height='16' alt='delete' class='deleteButton' border='0'></a>
</div>
</%doc>
<table cellspacing="0" cellpadding="0" border="0" width="100%"><tr>
<td width="*">
<div style="float: right; padding-right: 2px;">
<a href="${h.url_for( controller='root', action='edit', lid=data.id )}">
<img src="${h.url_for('/static/images/pencil_icon.png')}"
rollover="${h.url_for('/static/images/pencil_icon_dark.png')}"
width='16' height='16' alt='view or edit attributes' title='view or edit attributes'
class='editButton' style='vertical-align: middle' border='0'>
</a>
</div>
<input type="checkbox" name="import_ids" value="${data.id}"/>
<span class="historyItemTitle"><b>${data.display_name()}</b></span>
</td>
<td width="100">${data.ext}</td>
<td width="50"><span class="${data.dbkey}">${data.dbkey}</span></td>
<td width="200">${data.info}</td>
</tr></table>
</div>
## Body for history items, extra info and actions, data "peek"
<div id="info${data.id}" class="historyItemBody">
%if not trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_ACCESS, dataset = data.dataset ):
<div>You do not have permission to view this dataset.</div>
%else:
<div>
${data.blurb}
</div>
<div>
%if data.has_data:
<a href="${h.url_for( action='display', id=data.id, tofile='yes', toext='data.ext' )}" target="_blank">save</a>
%for display_app in data.datatype.get_display_types():
<% display_links = data.datatype.get_display_links( data, display_app, app, request.base ) %>
%if len( display_links ) > 0:
| ${data.datatype.get_display_label(display_app)}
%for display_name, display_link in display_links:
<a target="_blank" href="${display_link}">${display_name}</a>
%endfor
%endif
%endfor
%endif
</div>
%if data.peek != "no peek":
<div><pre id="peek${data.id}" class="peek">${data.display_peek()}</pre></div>
%endif
%endif
## Recurse for child datasets
%if len( data.children ) > 0:
## FIXME: This should not be in the template, there should
## be a 'visible_children' method on dataset.
<%
children = []
for child in data.children:
if child.visible:
children.append( child )
%>
%if len( children ) > 0:
<div>
There are ${len( children )} secondary datasets.
%for idx, child in enumerate(children):
${render_dataset( child, idx + 1 )}
%endfor
</div>
%endif
%endif
</div>
</div>
</%def>
+8 -2
View File
@@ -6,7 +6,11 @@
else:
data_state = data.state
%>
<div class="historyItemWrapper historyItem historyItem-${data_state}" id="historyItem-${data.id}">
%if not trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_ACCESS, dataset = data.dataset ):
<div class="historyItemWrapper historyItem historyItem-${data_state} historyItem-noPermission" id="historyItem-${data.id}">
%else:
<div class="historyItemWrapper historyItem historyItem-${data_state}" id="historyItem-${data.id}">
%endif
%if data.deleted:
<div class="warningmessagesmall">
@@ -36,7 +40,9 @@
## Body for history items, extra info and actions, data "peek"
<div id="info${data.id}" class="historyItemBody">
%if data_state == "queued":
%if not trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_ACCESS, dataset = data.dataset ):
<div>You do not have permission to view this dataset.</div>
%elif data_state == "queued":
<div>Job is waiting to run</div>
%elif data_state == "running":
<div>Job is currently running</div>
+8
View File
@@ -1,5 +1,13 @@
<%inherit file="/base_panels.mako"/>
<%def name="init()">
<%
self.has_left_panel=True
self.has_right_panel=True
self.active_view="analysis"
%>
</%def>
<%def name="left_panel()">
<div class="unified-panel-header" unselectable="on">
<div class='unified-panel-header-inner'>Tools</div>
+34 -10
View File
@@ -9,32 +9,56 @@
<link href="${h.url_for('/static/style/masthead.css')}" rel="stylesheet" type="text/css" />
</head>
<body class="mastheadPage">
<body class="mastheadPage" scroll="no">
<div id="tab-bar-bottom"></div>
<table width="100%" cellspacing="0" border="0">
<tr valign="middle"><td width="26px"><a target="_blank" href="${wiki_url}">
<img border="0" src="${h.url_for('/static/images/galaxyIcon_noText.png')}"></a></td>
<td align="left" valign="middle"><div class="pageTitle">Galaxy${brand}</div></td>
<td align="right" valign="middle">
Info: <a href="${bugs_email}">report bugs</a>
| <a target="_blank" href="${wiki_url}">wiki</a>
| <a target="_blank" href="${screencasts_url}">screencasts</a>
| <a target="_blank" href="${blog_url}">blog</a>
View: <span class="link-group">
<span
%if active_view == "analysis":
class="active-link"
%endif
><a target="_parent" href="${h.url_for( controller='root', action='index' )}">analysis</a></span>
| <span
%if active_view == "workflow":
class="active-link"
%endif
><a target="_parent" href="${h.url_for( controller='workflow', action='index' )}">workflow</a></span>
%if admin_user == "true":
| <span
%if active_view == "admin":
class="active-link"
%endif
><a target="_parent" href="${h.url_for( controller='admin', action='index' )}">admin</a></span>
%endif
</span>
&nbsp;&nbsp;&nbsp;
<span class="link-group">
Info: <span><a href="${bugs_email}">report bugs</a></span>
| <span><a target="_blank" href="${wiki_url}">wiki</a></span>
| <span><a target="_blank" href="${screencasts_url}">screencasts</a></span>
</span>
<!-- | <a target="mainframe" href="/static/index_frame_tools.html">tools</a>
| <a target="mainframe" href="/static/index_frame_history.html">history</a> -->
&nbsp;&nbsp;&nbsp;
<span class="link-group">
%if app.config.use_remote_user:
Logged in as ${t.user.email}
%else:
%if t.user:
Logged in as ${t.user.email}: <a target="galaxy_main" href="${h.url_for( controller='user', action='index' )}">manage</a>
| <a target="galaxy_main" href="${h.url_for( controller='user', action='logout' )}">logout</a>
Logged in as ${t.user.email}: <span><a target="galaxy_main" href="${h.url_for( controller='user', action='index' )}">manage</a></span>
| <span><a target="galaxy_main" href="${h.url_for( controller='user', action='logout' )}">logout</a></span>
%else:
Account: <a target="galaxy_main" href="${h.url_for( controller='user', action='create' )}">create</a>
| <a target="galaxy_main" href="${h.url_for( controller='user', action='login' )}">login</a>
Account: <span><a target="galaxy_main" href="${h.url_for( controller='user', action='create' )}">create</a></span>
| <span><a target="galaxy_main" href="${h.url_for( controller='user', action='login' )}">login</a></span>
%endif
%endif
&nbsp;
</span>
</td>
</tr>
</table>
+1 -1
View File
@@ -110,4 +110,4 @@
</body>
</html>
</html>
+2 -1
View File
@@ -8,6 +8,7 @@
<ul>
<li><a href="${h.url_for( action='change_password' )}">Change your password</a></li>
<li><a href="${h.url_for( action='change_email' )}">Update your email address</a></li>
<li><a href="${h.url_for( action='set_default_permissions' )}">Change default permissions</a> for new histories</li>
<li><a href="${h.url_for( action='logout' )}">Logout</a></li>
</ul>
%else:
@@ -16,4 +17,4 @@
<li><a href="${h.url_for( action='login' )}">Login</li>
<li><a href="${h.url_for( action='create' )}">Create new account</a></li>
</ul>
%endif
%endif
+7
View File
@@ -0,0 +1,7 @@
<%inherit file="/base.mako"/>
<%def name="title()">Change Default Permissions on New Histories</%def>
<%namespace file="/dataset/security_common.mako" import="render_permission_form" />
%if trans.user:
${render_permission_form( trans.user, h.url_for(), 'id', None, trans.user.all_roles() )}
%endif
+18 -10
View File
@@ -1,6 +1,18 @@
<%inherit file="/base_panels.mako"/>
<%def name="title()">Galaxy Workflow Editor</%def>
<%def name="init()">
<%
self.active_view="workflow"
self.message_box_visible=True
self.message_box_class="warning"
%>
</%def>
<%def name="message_box_content()">
Workflow support is currently in <b><i>beta</i></b> testing.
Workflows may not work with all tools, may fail unexpectedly, and may
not be compatible with future updates to <b>Galaxy</b>.
</%def>
<%def name="late_javascripts()">
<script type='text/javascript' src="${h.url_for('/static/scripts/galaxy.panels.js')}"> </script>
@@ -229,7 +241,7 @@
}
var close_editor = function() {
<% next_url = h.url_for( controller='root', m_c='workflow' ) %>
<% next_url = h.url_for( controller='workflow', action='index' ) %>
if ( workflow && workflow.has_changes ) {
do_close = function() {
window.onbeforeunload = undefined;
@@ -301,11 +313,12 @@
<%def name="stylesheets()">
${parent.stylesheets()}
## Also include "base.css" for styling tool menu and forms (details)
## Include "base.css" for styling tool menu and forms (details)
<link href="${h.url_for('/static/style/base.css')}" rel="stylesheet" type="text/css" />
## But make sure styles for the layout take precedence
${parent.stylesheets()}
<style type="text/css">
body { margin: 0; padding: 0; overflow: hidden; }
@@ -486,11 +499,6 @@
</td></tr></table>
</div>
<%def name="masthead()">
<div style="float: right; color: black; padding: 3px;"><div class="warningmessagesmall" style="display: inline-block; min-height: 15px;">Workflow support is currently in <b><i>beta</i></b></div></div>
<div class="title"><b>Galaxy workflow editor</b></div>
</%def>
<%def name="left_panel()">
<div class="unified-panel-header" unselectable="on">
<div class="unified-panel-header-inner">
+15 -97
View File
@@ -1,105 +1,23 @@
<%inherit file="/base.mako"/>
<%inherit file="/base_panels.mako"/>
<%def name="title()">Workflow home</%def>
<%def name="init()">
<%
self.has_left_panel=False
self.has_right_panel=False
self.active_view="workflow"
self.message_box_visible=True
self.message_box_class="warning"
%>
</%def>
<div class="warningmessage">
<%def name="message_box_content()">
Workflow support is currently in <b><i>beta</i></b> testing.
Workflows may not work with all tools, may fail unexpectedly, and may
not be compatible with future updates to <b>Galaxy</b>.
</div>
</%def>
%if message:
<%
try:
messagetype
except:
messagetype = "done"
%>
<p />
<div class="${messagetype}message">
${message}
</div>
%endif
<%def name="center_panel()">
<h2>Your workflows</h2>
<iframe name="galaxy_main" id="galaxy_main" frameborder="0" style="position: absolute; width: 100%; height: 100%;" src="${h.url_for( controller="workflow", action="list" )}"> </iframe>
<div style="float: right; margin-top: -2.5em;">
<a class="action-button" href="${h.url_for( action='create' )}">
<img src="${h.url_for('/static/images/silk/add.png')}" />
<span>Add a new workflow</span>
</a>
</div>
%if workflows:
<table class="colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<tr class="header">
<th>Name</th>
<th># of Steps</th>
## <th>Last Updated</th>
<th></th>
</tr>
%for i, workflow in enumerate( workflows ):
<tr>
<td>
<a href="${h.url_for( action='run', id=trans.security.encode_id(workflow.id) )}">${workflow.name}</a>
<a id="wf-${i}-popup" class="popup-arrow" style="display: none;">&#9660;</a>
</td>
<td>${len(workflow.latest_workflow.steps)}</td>
## <td>${str(workflow.update_time)[:19]}</td>
<td>
<div popupmenu="wf-${i}-popup">
<a class="action-button" href="${h.url_for( action='run', id=trans.security.encode_id(workflow.id) )}">Run</a>
<a class="action-button" href="${h.url_for( action='editor', id=trans.security.encode_id(workflow.id) )}" target="_parent">Edit</a>
<a class="action-button" href="${h.url_for( action='rename', id=trans.security.encode_id(workflow.id) )}">Rename</a>
<a class="action-button" href="${h.url_for( action='share', id=trans.security.encode_id(workflow.id) )}">Share</a>
<a class="action-button" confirm="Are you sure you want to delete workflow '${workflow.name}'?" href="${h.url_for( action='delete', id=trans.security.encode_id(workflow.id) )}">Delete</a>
</div>
</td>
</tr>
%endfor
</table>
%else:
You have no workflows.
%endif
<h2>Workflows shared with you by others</h2>
%if shared_by_others:
<table class="colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<tr class="header">
<th>Name</th>
<th>Owner</th>
<th># of Steps</th>
<th></th>
</tr>
%for i, association in enumerate( shared_by_others ):
<% workflow = association.stored_workflow %>
<tr>
<td>
<a href="${h.url_for( action='run', id=trans.security.encode_id(workflow.id) )}">${workflow.name}</a>
<a id="shared-${i}-popup" class="popup-arrow" style="display: none;">&#9660;</a>
</td>
<td>${workflow.user.email}</td>
<td>${len(workflow.latest_workflow.steps)}</td>
<td>
<div popupmenu="shared-${i}-popup">
<a class="action-button" href="${h.url_for( action='run', id=trans.security.encode_id(workflow.id) )}">Run</a>
<a class="action-button" href="${h.url_for( action='clone', id=trans.security.encode_id(workflow.id) )}">Clone</a>
</div>
</td>
</tr>
%endfor
</table>
%else:
No workflows have been shared with you.
%endif
<h2>Other options</h2>
<a class="action-button" href="${h.url_for( action='configure_menu' )}">
<span>Configure your workflow menu</span>
</a>
</%def>
+101
View File
@@ -0,0 +1,101 @@
<%inherit file="/base.mako"/>
<%def name="title()">Workflow home</%def>
%if message:
<%
try:
messagetype
except:
messagetype = "done"
%>
<p />
<div class="${messagetype}message">
${message}
</div>
%endif
<h2>Your workflows</h2>
<ul class="manage-table-actions">
<li>
<a class="action-button" href="${h.url_for( action='create' )}">
<img src="${h.url_for('/static/images/silk/add.png')}" />
<span>Add a new workflow</span>
</a>
</li>
</ul>
%if workflows:
<table class="mange-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<tr class="header">
<th>Name</th>
<th># of Steps</th>
## <th>Last Updated</th>
<th></th>
</tr>
%for i, workflow in enumerate( workflows ):
<tr>
<td>
<a href="${h.url_for( action='run', id=trans.security.encode_id(workflow.id) )}">${workflow.name}</a>
<a id="wf-${i}-popup" class="popup-arrow" style="display: none;">&#9660;</a>
</td>
<td>${len(workflow.latest_workflow.steps)}</td>
## <td>${str(workflow.update_time)[:19]}</td>
<td>
<div popupmenu="wf-${i}-popup">
<a class="action-button" href="${h.url_for( action='run', id=trans.security.encode_id(workflow.id) )}">Run</a>
<a class="action-button" href="${h.url_for( action='editor', id=trans.security.encode_id(workflow.id) )}" target="_parent">Edit</a>
<a class="action-button" href="${h.url_for( action='rename', id=trans.security.encode_id(workflow.id) )}">Rename</a>
<a class="action-button" href="${h.url_for( action='share', id=trans.security.encode_id(workflow.id) )}">Share</a>
<a class="action-button" confirm="Are you sure you want to delete workflow '${workflow.name}'?" href="${h.url_for( action='delete', id=trans.security.encode_id(workflow.id) )}">Delete</a>
</div>
</td>
</tr>
%endfor
</table>
%else:
You have no workflows.
%endif
<h2>Workflows shared with you by others</h2>
%if shared_by_others:
<table class="colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<tr class="header">
<th>Name</th>
<th>Owner</th>
<th># of Steps</th>
<th></th>
</tr>
%for i, association in enumerate( shared_by_others ):
<% workflow = association.stored_workflow %>
<tr>
<td>
<a href="${h.url_for( action='run', id=trans.security.encode_id(workflow.id) )}">${workflow.name}</a>
<a id="shared-${i}-popup" class="popup-arrow" style="display: none;">&#9660;</a>
</td>
<td>${workflow.user.email}</td>
<td>${len(workflow.latest_workflow.steps)}</td>
<td>
<div popupmenu="shared-${i}-popup">
<a class="action-button" href="${h.url_for( action='run', id=trans.security.encode_id(workflow.id) )}">Run</a>
<a class="action-button" href="${h.url_for( action='clone', id=trans.security.encode_id(workflow.id) )}">Clone</a>
</div>
</td>
</tr>
%endfor
</table>
%else:
No workflows have been shared with you.
%endif
<h2>Other options</h2>
<a class="action-button" href="${h.url_for( action='configure_menu' )}">
<span>Configure your workflow menu</span>
</a>
+129 -7
View File
@@ -8,7 +8,7 @@ import twill
import twill.commands as tc
from twill.other_packages._mechanize_dist import ClientForm
from elementtree import ElementTree
buffer = StringIO.StringIO()
#Force twill to log to a buffer -- FIXME: Should this go to stdout and be captured by nose?
@@ -30,7 +30,7 @@ class TwillTestCase( unittest.TestCase ):
self.home()
self.set_history()
"""Functions associated with files"""
# Functions associated with files
def files_diff( self, file1, file2 ):
"""Checks the contents of 2 files for differences"""
if not filecmp.cmp( file1, file2 ):
@@ -82,7 +82,7 @@ class TwillTestCase( unittest.TestCase ):
errmsg += str( err )
raise AssertionError( errmsg )
"""Functions associated with histories"""
# Functions associated with histories
def check_history_for_errors( self ):
"""Raises an exception if there are errors in a history"""
self.visit_page( "history" )
@@ -209,7 +209,7 @@ class TwillTestCase( unittest.TestCase ):
def view_stored_histories( self ):
self.visit_page( "history_available" )
"""Functions associated with datasets (history items) and meta data"""
# Functions associated with datasets (history items) and meta data
def get_job_stderr( self, id ):
self.visit_page( "dataset/stderr?id=%s" % id )
return self.last_page()
@@ -360,7 +360,7 @@ class TwillTestCase( unittest.TestCase ):
genome_build = elem.get('dbkey')
self.assertTrue( genome_build == dbkey )
"""Functions associated with user accounts"""
# Functions associated with user accounts
def create( self, email='test@bx.psu.edu', password='testuser', confirm='testuser' ):
self.visit_page( "user/create?email=%s&password=%s&confirm=%s" %(email, password, confirm) )
try:
@@ -370,6 +370,7 @@ class TwillTestCase( unittest.TestCase ):
self.home() #Reset our URL for future tests
def login( self, email='test@bx.psu.edu', password='testuser'):
# test@bx.psu.edu is configured as an admin user
self.create( email=email, password=password, confirm=password )
self.visit_page( "user/login?email=%s&password=%s" % (email, password) )
self.check_page_for_string( "Now logged in as %s" %email )
@@ -380,7 +381,7 @@ class TwillTestCase( unittest.TestCase ):
self.check_page_for_string( "You are no longer logged in" )
self.home() #Reset our URL for future tests
"""Functions associated with browsers, cookies, HTML forms and page visits"""
# Functions associated with browsers, cookies, HTML forms and page visits
def check_page_for_string( self, patt ):
"""Looks for 'patt' in the current browser page"""
page = self.last_page()
@@ -502,7 +503,7 @@ class TwillTestCase( unittest.TestCase ):
tc.go("%s" % url)
tc.code( 200 )
"""Functions associated with Galaxy tools"""
# Functions associated with Galaxy tools
def run_tool( self, tool_id, **kwd ):
tool_id = tool_id.replace(" ", "+")
"""Runs the tool 'tool_id' and passes it the key/values from the *kwd"""
@@ -525,3 +526,124 @@ class TwillTestCase( unittest.TestCase ):
else:
break
self.assertNotEqual(count, maxiter)
# Dataset Security stuff
def create_role( self, name='New Test Role', description="Very cool new test role", user_ids=[], group_ids=[] ):
"""Create a new role"""
self.visit_url( "%s/admin/create_role" % self.url )
form = tc.show()
self.check_page_for_string( "Create Role" )
try:
tc.fv( "1", "name", name )
tc.fv( "1", "description", description )
for user_id in user_ids:
tc.fv( "1", "3", user_id ) # form field 3 is the check box named 'users'
for group_id in group_ids:
tc.fv( "1", "4", group_id ) # form field 4 is the check box named 'groups'
tc.submit( "create_role_button" )
except AssertionError, err:
errmsg = 'Exception caught attempting to create role: %s' % str( err )
raise AssertionError( errmsg )
self.home()
self.visit_page( "admin/roles" )
self.check_page_for_string( name )
self.home()
def mark_role_deleted( self, role_id ):
"""Mark a role as deleted"""
self.visit_url( "%s/admin/mark_role_deleted?role_id=%s" % ( self.url, role_id ) )
self.last_page()
self.check_page_for_string( 'The role has been marked as deleted' )
self.home()
def undelete_role( self, role_id ):
"""Undelete an existing role"""
self.visit_url( "%s/admin/undelete_role?role_id=%s" % ( self.url, role_id ) )
self.last_page()
self.check_page_for_string( 'The role has been marked as not deleted' )
self.home()
def purge_role( self, role_id, deleted=False ):
"""Purge an existing role"""
if not deleted:
self.mark_role_deleted( role_id )
self.visit_url( "%s/admin/purge_role?role_id=%s" % ( self.url, role_id ) )
self.last_page()
self.check_page_for_string( 'The role has been purged from the database' )
self.home()
def create_group( self, name='New Test Group', user_ids=[], role_ids=[] ):
"""Create a new group with 2 members and 1 associated role"""
self.visit_url( "%s/admin/create_group" % self.url )
form = tc.show()
self.check_page_for_string( "Create Group" )
try:
tc.fv( "1", "name", name )
for user_id in user_ids:
tc.fv( "1", "2", user_id ) # form field 2 is the check box named 'members'
for role_id in role_ids:
tc.fv( "1", "3", role_id ) # form field 3 is the check box named 'roles'
tc.submit( "create_group_button" )
except AssertionError, err:
errmsg = 'Exception caught attempting to create group: %s' % str( err )
raise AssertionError( errmsg )
self.home()
self.visit_page( "admin/groups" )
self.check_page_for_string( name )
self.home()
def add_group_members( self, group_id, user_ids=[] ):
"""Add a member to an existing group"""
self.visit_url( "%s/admin/group_members_edit?group_id=%s" % ( self.url, group_id ) )
self.check_page_for_string( 'Members of' )
try:
for user_id in user_ids:
tc.fv( "1", "1", user_id ) # form field 1 is the check box named 'members'
tc.submit( "group_members_edit_button" )
except AssertionError, err:
raise AssertionError( 'Exception caught attempting to create group: %s' % str( err ) )
self.home()
def associate_groups_with_role( self, role_id, group_ids=[] ):
"""Add groups to an existing role"""
# NOTE: To get this to work with twill, all select lists must contain at least 1 option value
# or twill throws an exception, which is: ParseError: OPTION outside of SELECT
self.visit_url( "%s/admin/role?role_id=%s" % ( self.url, role_id ) )
self.check_page_for_string( 'Groups associated with' )
# All groups must be in the out_groups form field
try:
for group in groups:
tc.fv( "1", "7", group_id ) # form field 7 is the select list named out_groups, note the buttons...
tc.submit( "groups_add_button" )
tc.submit( "role_button" )
except AssertionError, err:
raise AssertionError( 'Exception caught attempting to associated groups with a role: %s' % str( err ) )
except:
pass
self.home()
def mark_group_deleted( self, group_id ):
"""Mark a group as deleted"""
self.visit_url( "%s/admin/mark_group_deleted?group_id=%s" % ( self.url, group_id ) )
self.last_page()
self.check_page_for_string( 'The group has been marked as deleted' )
self.home()
def undelete_group( self, group_id ):
"""Undelete an existing group"""
self.visit_url( "%s/admin/undelete_group?group_id=%s" % ( self.url, group_id ) )
self.last_page()
self.check_page_for_string( 'The group has been marked as not deleted' )
self.home()
def purge_group( self, group_id, deleted=False ):
"""Purge an existing group"""
if not deleted:
self.mark_group_deleted( group_id )
self.visit_url( "%s/admin/purge_group?group_id=%s" % ( self.url, group_id ) )
self.last_page()
self.check_page_for_string( 'The group has been purged from the database' )
self.home()
# Library stuff
def create_library( self, name='', description='' ):
"""Create a new library"""
name = name.replace( ' ', '+' )
description = description.replace( ' ', '+' )
try:
self.visit_url( "%s/admin/library?name=%s&description=%s&create_library=None" % ( self.url, name, description ) )
except AssertionError, err:
errmsg = 'Exception caught attempting to create library: %s' % str( err )
raise AssertionError( errmsg )
self.home()
+1
View File
@@ -73,6 +73,7 @@ def setup():
test_conf = "test.conf",
log_destination = "stdout",
use_heartbeat=False,
admin_users = 'test@bx.psu.edu',
global_conf= { "__file__": "universe_wsgi.ini.sample" } )
log.info( "Embedded Universe application started" )
+4 -1
View File
@@ -48,7 +48,10 @@ class TestHistory( TwillTestCase ):
"""Testing sharing a history with another user"""
self.upload_file('1.bed', dbkey='hg18')
id, name, email = self.share_history()
self.check_page_for_string( 'History (%s) has been shared with: %s' %(name, email) )
try:
self.check_page_for_string( 'History (%s) has been shared with: %s' %(name, email) )
except TwillAssertionError:
self.check_page_for_string( "The history or histories you've chosen to share contain datasets that the user you're sharing with does not have permission to access." )
self.logout()
self.login( email='test2@bx.psu.edu' )
self.view_stored_histories()
@@ -0,0 +1,173 @@
import galaxy.model
from base.twilltestcase import *
s = 'You must have Galaxy administrator privileges to use this feature.'
class TestHistory( TwillTestCase ):
def test_00_admin_features_when_not_logged_in( self ):
"""Testing admin_features when not logged in"""
self.logout()
self.visit_url( "%s/admin" % self.url )
self.check_page_for_string( s )
self.visit_url( "%s/admin/reload_tool?tool_id=upload1" % self.url )
self.check_page_for_string( s )
self.visit_url( "%s/admin/roles" % self.url )
self.check_page_for_string( s )
self.visit_url( "%s/admin/create_role" % self.url )
self.check_page_for_string( s )
self.visit_url( "%s/admin/new_role" % self.url )
self.check_page_for_string( s )
self.visit_url( "%s/admin/role" % self.url )
self.check_page_for_string( s )
self.visit_url( "%s/admin/groups" % self.url )
self.check_page_for_string( s )
self.visit_url( "%s/admin/create_group" % self.url )
self.check_page_for_string( s )
self.visit_url( "%s/admin/group_members_edit" % self.url )
self.check_page_for_string( s )
self.visit_url( "%s/admin/update_group_members" % self.url )
self.check_page_for_string( s )
self.visit_url( "%s/admin/users" % self.url )
self.check_page_for_string( s )
self.visit_url( "%s/admin/library_browser" % self.url )
self.check_page_for_string( s )
self.visit_url( "%s/admin/libraries" % self.url )
self.check_page_for_string( s )
self.visit_url( "%s/admin/library" % self.url )
self.check_page_for_string( s )
self.visit_url( "%s/admin/folder" % self.url )
self.check_page_for_string( s )
self.visit_url( "%s/admin/dataset" % self.url )
self.check_page_for_string( s )
def test_05_login_as_admin( self ):
"""Testing logging in as an admin user"""
self.login( email='test@bx.psu.edu' ) #This is configured as our admin user
self.visit_page( "admin" )
self.check_page_for_string( 'Administration' )
user = galaxy.model.User.filter( galaxy.model.User.table.c.email=='test@bx.psu.edu' ).first()
# Make sure a private role exists for the user
private_role_found = False
for role in user.all_roles():
if role.name == user.email and role.description == 'Private Role for %s' % user.email:
private_role_found = True
break
if not private_role_found:
raise AssertionError( "Private role not found for user '%s'" % user.email )
self.visit_url( "%s/admin/user?user_id=%s" % ( self.url, user.id ) )
self.check_page_for_string( "test@bx.psu.edu" )
self.home()
self.logout()
# Make sure that we have 3 users
self.login( email='test2@bx.psu.edu' ) # This will not be an admin user
self.visit_page( "admin" )
self.check_page_for_string( s )
self.logout()
self.login( email='test3@bx.psu.edu' ) # This will not be an admin user
self.visit_page( "admin" )
self.check_page_for_string( s )
self.logout()
def test_10_create_role( self ):
"""Testing creating new non-private role with 2 members"""
self.login( email='test@bx.psu.edu' )
user = galaxy.model.User.filter( galaxy.model.User.table.c.email=='test@bx.psu.edu' ).first()
user_id1 = str( user.id )
user = galaxy.model.User.filter( galaxy.model.User.table.c.email=='test2@bx.psu.edu' ).first()
user_id2 = str( user.id )
self.create_role( user_ids=[ user_id1, user_id2 ] )
def test_15_create_group( self ):
"""Testing creating new group with 2 members and 1 associated role"""
user = galaxy.model.User.filter( galaxy.model.User.table.c.email=='test@bx.psu.edu' ).first()
user_id1 = str( user.id )
user = galaxy.model.User.filter( galaxy.model.User.table.c.email=='test2@bx.psu.edu' ).first()
user_id2 = str( user.id )
role = galaxy.model.Role.filter( galaxy.model.Role.table.c.name=='New Test Role' ).first()
role_id = str( role.id )
self.create_group( user_ids=[ user_id1, user_id2 ], role_ids=[ role_id ] )
def test_20_add_group_member( self ):
"""Testing editing membership of an existing group"""
self.create_group( name='Another Test Group' )
group = galaxy.model.Group.filter( galaxy.model.Group.table.c.name == 'Another Test Group' ).first()
group_id = str( group.id )
user = galaxy.model.User.filter( galaxy.model.User.table.c.email=='test3@bx.psu.edu' ).first()
user_id = str( user.id )
self.add_group_members( group_id, [ user_id ] )
self.visit_url( "%s/admin/group_members_edit?group_id=%s" % ( self.url, group_id ) )
self.check_page_for_string( 'test3@bx.psu.edu' )
def test_25_associate_groups_with_role( self ):
"""Testing adding existing groups to an existing role"""
group = galaxy.model.Group.filter( galaxy.model.Group.table.c.name == 'Another Test Group' ).first()
group_id = str( group.id )
user = galaxy.model.User.filter( galaxy.model.User.table.c.email == 'test@bx.psu.edu' ).first()
user_id = str( user.id )
# NOTE: To get this to work with twill, all select lists on the ~/admin/role page must contain at least
# 1 option value or twill throws an exception, which is: ParseError: OPTION outside of SELECT
# Due to this bug in twill, we crreate the role, associating it with at least 1 user and 1 group...
#
# TODO: need to enhance this test to associate DefaultUserPermissions and DefaultHistoryPermissions
# with the role, then add tests in test_55_purge_role to make sure the association records are deleted
# when the role is purged.
self.create_role( name='Another Test Role', user_ids=[ user_id ], group_ids=[ group_id ] )
role = galaxy.model.Role.filter( galaxy.model.Role.table.c.name=='Another Test Role' ).first()
role_id = str( role.id )
group = galaxy.model.Group.filter( galaxy.model.Group.table.c.name == 'New Test Group' ).first()
group_id = str( group.id )
# ...and then we associate the role with a group not yet associated
self.associate_groups_with_role( role_id, group_ids=[ group_id ] )
self.visit_page( 'admin/roles' )
self.check_page_for_string( 'New Test Group' )
def test_30_mark_group_deleted( self ):
"""Testing marking a group as deleted"""
self.visit_page( "admin/groups" )
self.check_page_for_string( "Another Test Group" )
group = galaxy.model.Group.filter( galaxy.model.Group.table.c.name == 'Another Test Group' ).first()
group_id = str( group.id )
self.mark_group_deleted( group_id )
def test_35_undelete_group( self ):
"""Testing undeleting a deleted group"""
group = galaxy.model.Group.filter( galaxy.model.Group.table.c.name == 'Another Test Group' ).first()
group_id = str( group.id )
self.undelete_group( group_id )
def test_40_mark_role_deleted( self ):
"""Testing marking a role as deleted"""
self.visit_page( "admin/roles" )
self.check_page_for_string( "Another Test Role" )
role = galaxy.model.Role.filter( galaxy.model.Role.table.c.name == 'Another Test Role' ).first()
role_id = str( role.id )
self.mark_role_deleted( role_id )
def test_45_undelete_role( self ):
"""Testing undeleting a deleted role"""
role = galaxy.model.Role.filter( galaxy.model.Role.table.c.name == 'Another Test Role' ).first()
role_id = str( role.id )
self.undelete_role( role_id )
def test_50_purge_group( self ):
"""Testing purging a group"""
group = galaxy.model.Group.filter( galaxy.model.Group.table.c.name == 'Another Test Group' ).first()
group_id = str( group.id )
self.purge_group( group_id )
# Make sure there are no UserGroupAssociations
uga = galaxy.model.UserGroupAssociation.filter( galaxy.model.UserGroupAssociation.table.c.group_id == group_id ).all()
if uga:
raise AssertionError( "Purging the group did not delete the UserGroupAssociations for group_id '%s'" % group_id )
# Make sure there are no GroupRoleAssociations
gra = galaxy.model.GroupRoleAssociation.filter( galaxy.model.GroupRoleAssociation.table.c.group_id == group_id ).all()
if gra:
raise AssertionError( "Purging the group did not delete the GroupRoleAssociations for group_id '%s'" % group_id )
def test_55_purge_role( self ):
"""Testing purging a role"""
role = galaxy.model.Role.filter( galaxy.model.Role.table.c.name == 'Another Test Role' ).first()
role_id = str( role.id )
self.purge_role( role_id )
# Make sure there are no GroupRoleAssociations
gra = galaxy.model.GroupRoleAssociation.filter( galaxy.model.GroupRoleAssociation.table.c.role_id == role_id ).all()
if gra:
raise AssertionError( "Purging the role did not delete the GroupRoleAssociations for role_id '%s'" % role_id )
# Make sure there are no ActionDatasetRoleAssociations
adra = galaxy.model.ActionDatasetRoleAssociation.filter( galaxy.model.ActionDatasetRoleAssociation.table.c.role_id == role_id ).all()
if adra:
raise AssertionError( "Purging the role did not delete the ActionDatasetRoleAssociations for role_id '%s'" % role_id )
#def test_20_create_library( self ):
# """Testing creating new library"""
# self.create_library( name='New Test Library', description='New Test Library Description' )
# self.visit_page( 'admin/libraries' )
# self.check_page_for_string( "New Test Library" )
+1
View File
@@ -38,3 +38,4 @@ The EpiGRAPH_ web service enables biologists to uncover hidden associations in v
</help>
</tool>
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0"?>
<tool name="Access Libraries" id="library_access1">
<description>stored locally</description>
<inputs action="library/browse" method="get">
<param name="bogus_param" type="hidden" value="needed" />
</inputs>
</tool>
+3 -1
View File
@@ -5,7 +5,8 @@ from shutil import copyfile
#post processing, set build for data and add additional data to history
def exec_after_process(app, inp_data, out_data, param_dict, tool, stdout, stderr):
history = out_data.items()[0][1].history
base_dataset = out_data.items()[0][1]
history = base_dataset.history
if history == None:
print "unknown history!"
return
@@ -37,6 +38,7 @@ def exec_after_process(app, inp_data, out_data, param_dict, tool, stdout, stderr
newdata.extension = file_type
newdata.name = basic_name + " (" + description + ")"
history.add_dataset( newdata )
app.security_agent.copy_dataset_permissions( base_dataset.dataset, newdata.dataset )
app.model.flush()
try:
copyfile(filepath,newdata.file_name)
+3 -1
View File
@@ -84,7 +84,8 @@ from galaxy import datatypes, config, jobs
from shutil import copyfile
def exec_after_process(app, inp_data, out_data, param_dict, tool, stdout, stderr):
history = out_data.items()[0][1].history
base_dataset = out_data.items()[0][1]
history = base_dataset.history
if history == None:
print "unknown history!"
return
@@ -128,6 +129,7 @@ def exec_after_process(app, inp_data, out_data, param_dict, tool, stdout, stderr
newdata.extension = file_type
newdata.name = basic_name + " (" + microbe_info[kingdom][org]['chrs'][chr]['data'][description]['feature'] +" for "+microbe_info[kingdom][org]['name']+":"+chr + ")"
newdata.flush()
app.security_agent.copy_dataset_permissions( base_dataset.dataset, newdata.dataset )
history.add_dataset( newdata )
app.model.flush()
try:

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