Merge changeset to security
@@ -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/
|
||||
|
||||
@@ -39,7 +39,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
|
||||
@@ -84,7 +84,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,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 )
|
||||
|
||||
@@ -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 )
|
||||
@@ -63,6 +63,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()
|
||||
|
||||
@@ -47,6 +47,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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,14 @@ 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()
|
||||
|
||||
|
||||
class Job( object ):
|
||||
"""
|
||||
A job represents a request to run a tool given input datasets, tool
|
||||
@@ -101,187 +103,70 @@ 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()
|
||||
class GroupDatasetAssociation( object ):
|
||||
def __init__( self, group, dataset, permitted_actions=[] ):
|
||||
if isinstance( group, GroupDatasetAssociation ) or \
|
||||
isinstance( group, DefaultUserGroupAssociation ) or \
|
||||
isinstance( group, DefaultHistoryGroupAssociation ):
|
||||
group = group.group
|
||||
self.group = group
|
||||
if isinstance( dataset, HistoryDatasetAssociation ):
|
||||
dataset = dataset.dataset
|
||||
self.dataset = dataset
|
||||
self.parent_id = parent_id
|
||||
self.validation_errors = validation_errors
|
||||
self.copied_from_history_dataset_association = copied_from_history_dataset_association
|
||||
self.permitted_actions = permitted_actions
|
||||
def add_permitted_action( self, action ):
|
||||
if action not in self.permitted_actions:
|
||||
return self.permitted_actions.append( action )
|
||||
raise 'action (%s) already exists in permitted actions list (%s: %s).' % ( action, str( self.id ), str( self.permitted_actions ) )
|
||||
def remove_permitted_action( self, action ):
|
||||
return self.permitted_actions.remove( action )
|
||||
|
||||
class Group( object ):
|
||||
public_id = None
|
||||
permitted_actions = galaxy.security.get_permitted_actions( 'GROUP' )
|
||||
def __init__( self, name = None, priority = 0 ):
|
||||
self.name = name
|
||||
self.priority = priority
|
||||
@classmethod
|
||||
def get_public_group( cls ):
|
||||
return Group.get( cls.public_id )
|
||||
@classmethod
|
||||
def set_public_group( cls, group ):
|
||||
# We store the id instead of the object, because of alchemy sessions
|
||||
if isinstance( group, Group ):
|
||||
group = group.id
|
||||
cls.public_id = group
|
||||
@classmethod
|
||||
def guess_public_group( cls ):
|
||||
# Retrieve from database and store public group id
|
||||
group = Group.filter_by( name='public' ).first()
|
||||
cls.set_public_group( group )
|
||||
|
||||
class UserGroupAssociation( object ):
|
||||
def __init__( self, user, group ):
|
||||
self.user = user
|
||||
self.group = group
|
||||
|
||||
class DefaultUserGroupAssociation( object ):
|
||||
def __init__( self, user, group, permitted_actions ):
|
||||
if isinstance( group, GroupDatasetAssociation ) or \
|
||||
isinstance( group, DefaultUserGroupAssociation ) or \
|
||||
isinstance( group, DefaultHistoryGroupAssociation ):
|
||||
group = group.group
|
||||
self.user = user
|
||||
self.group = group
|
||||
self.permitted_actions = permitted_actions
|
||||
|
||||
class DefaultHistoryGroupAssociation( object ):
|
||||
def __init__( self, history, group, permitted_actions ):
|
||||
if isinstance( group, GroupDatasetAssociation ) or \
|
||||
isinstance( group, DefaultUserGroupAssociation ) or \
|
||||
isinstance( group, DefaultHistoryGroupAssociation ):
|
||||
group = group.group
|
||||
self.history = history
|
||||
self.group = group
|
||||
self.permitted_actions = permitted_actions
|
||||
|
||||
@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 )
|
||||
|
||||
def get_metadata( self ):
|
||||
if not hasattr( self, '_metadata_collection' ):
|
||||
self._metadata_collection = MetadataCollection( self, self.datatype.metadata_spec )
|
||||
return self._metadata_collection
|
||||
def set_metadata( self, bunch ):
|
||||
# Needs to accept a MetadataCollection, a bunch, or a dict
|
||||
self._metadata = dict( [ ( key, copy.deepcopy( value ) ) for key, value in bunch.items() ] )
|
||||
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()
|
||||
if hasattr( self, '_metadata_collection' ):
|
||||
del self._metadata_collection
|
||||
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 ):
|
||||
return self.datatype.missing_meta( self )
|
||||
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, metadata=self._metadata, dataset = self.dataset, visible=self.visible, deleted=self.deleted, parent_id=parent_id, copied_from_history_dataset_association = self )
|
||||
des.flush()
|
||||
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()
|
||||
|
||||
|
||||
|
||||
class History( object ):
|
||||
def __init__( self, id=None, name=None, user=None ):
|
||||
@@ -363,6 +248,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 ):
|
||||
@@ -447,8 +333,367 @@ 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, self.datatype.metadata_spec )
|
||||
return self._metadata_collection
|
||||
def set_metadata( self, bunch ):
|
||||
# Needs to accept a MetadataCollection, a bunch, or a dict
|
||||
self._metadata = dict( [ ( key, copy.deepcopy( value ) ) for key, value in bunch.items() ] )
|
||||
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()
|
||||
if hasattr( self, '_metadata_collection' ):
|
||||
del self._metadata_collection
|
||||
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 ):
|
||||
return self.datatype.missing_meta( self )
|
||||
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()
|
||||
|
||||
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,
|
||||
metadata=self._metadata,
|
||||
dataset = self.dataset,
|
||||
visible=self.visible,
|
||||
deleted=self.deleted,
|
||||
parent_id=parent_id,
|
||||
copied_from_history_dataset_association=self,
|
||||
history = target_history )
|
||||
des.flush()
|
||||
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 ):
|
||||
#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,
|
||||
metadata=self._metadata,
|
||||
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()
|
||||
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,
|
||||
metadata=self._metadata,
|
||||
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()
|
||||
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 ):
|
||||
@@ -490,7 +735,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
|
||||
@@ -537,7 +791,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 ):
|
||||
|
||||
@@ -16,16 +16,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:
|
||||
@@ -58,10 +57,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) )
|
||||
@@ -75,7 +74,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]
|
||||
|
||||
@@ -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,124 @@ 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( "priority", Integer ),
|
||||
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 ) )
|
||||
|
||||
GroupDatasetAssociation.table = Table( "group_dataset_association", metadata,
|
||||
Column( "id", Integer, primary_key=True ),
|
||||
Column( "group_id", Integer, ForeignKey( "galaxy_group.id" ), index=True ),
|
||||
Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ),
|
||||
Column( "create_time", DateTime, default=now ),
|
||||
Column( "update_time", DateTime, default=now, onupdate=now ),
|
||||
Column( "permitted_actions", JSONType(), default=[] ) )
|
||||
|
||||
# The following table stores the permissions that are considered the defaults for new histories when they are created by a user
|
||||
DefaultUserGroupAssociation.table = Table( "default_user_group_association", metadata,
|
||||
Column( "id", Integer, primary_key=True ),
|
||||
Column( "group_id", Integer, ForeignKey( "galaxy_group.id" ), index=True ),
|
||||
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ),
|
||||
Column( "create_time", DateTime, default=now ),
|
||||
Column( "update_time", DateTime, default=now, onupdate=now ),
|
||||
Column( "permitted_actions", JSONType(), default=[] ) )
|
||||
|
||||
# The following table stores the default permissions assigned to histories for datasets
|
||||
# that need permissions ( dataset permissions that cannot be determined based on ancestor )
|
||||
DefaultHistoryGroupAssociation.table = Table( "default_history_group_association", metadata,
|
||||
Column( "id", Integer, primary_key=True ),
|
||||
Column( "group_id", Integer, ForeignKey( "galaxy_group.id" ), index=True ),
|
||||
Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ),
|
||||
Column( "create_time", DateTime, default=now ),
|
||||
Column( "update_time", DateTime, default=now, onupdate=now ),
|
||||
Column( "permitted_actions", JSONType(), default=[] ) )
|
||||
|
||||
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 +289,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 +298,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 +309,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 +323,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,
|
||||
@@ -248,27 +349,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 ) )
|
||||
) )
|
||||
|
||||
|
||||
@@ -293,11 +399,90 @@ assign_mapper( context, History, History.table,
|
||||
assign_mapper( context, User, User.table,
|
||||
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 ),
|
||||
datasets=relation( GroupDatasetAssociation ) ) )
|
||||
|
||||
assign_mapper( context, UserGroupAssociation, UserGroupAssociation.table,
|
||||
properties=dict( user=relation( User, backref = "groups" ),
|
||||
group=relation( Group, backref = "members" ) ) )
|
||||
|
||||
assign_mapper( context, GroupDatasetAssociation, GroupDatasetAssociation.table,
|
||||
properties=dict( dataset=relation( Dataset, backref = "groups" ),
|
||||
group=relation( Group, backref = "group_datasets" ) ) )
|
||||
|
||||
assign_mapper( context, DefaultUserGroupAssociation, DefaultUserGroupAssociation.table,
|
||||
properties=dict( user=relation( User, backref = "default_groups" ),
|
||||
group=relation( Group ) ) )
|
||||
|
||||
assign_mapper( context, DefaultHistoryGroupAssociation, DefaultHistoryGroupAssociation.table,
|
||||
properties=dict( history=relation( History, backref = "default_groups" ),
|
||||
group=relation( Group ) ) )
|
||||
|
||||
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 ) ) )
|
||||
|
||||
@@ -366,11 +551,12 @@ 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:
|
||||
@@ -399,18 +585,31 @@ 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 )
|
||||
# Ensure group named 'public' exists
|
||||
public_group = result.Group.filter_by( name='public' ).first()
|
||||
if not public_group:
|
||||
public_group = result.security_agent.create_group( name = 'public' )
|
||||
# Store public group id
|
||||
result.security_agent.set_public_group( public_group )
|
||||
log.debug( "Public Group identified as id = %s." % ( public_group.id ) )
|
||||
return result
|
||||
|
||||
def get_suite():
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Galaxy specific SQLAlchemy extensions.
|
||||
"""
|
||||
@@ -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
|
||||
@@ -0,0 +1,300 @@
|
||||
"""
|
||||
Galaxy Security
|
||||
|
||||
"""
|
||||
import logging
|
||||
from galaxy.util.bunch import Bunch
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
class RBACAgent:
|
||||
"""Class that handles galaxy security"""
|
||||
permitted_actions = Bunch(
|
||||
# The ability to edit the metadata of the associated dataset
|
||||
DATASET_EDIT_METADATA = 'edit metadata',
|
||||
# The ability to change the permissions of a dataset (so specifically, to add and modify
|
||||
# group_dataset_association rows where the dataset is the dataset for which the permission is set).
|
||||
DATASET_MANAGE_PERMISSIONS = 'manage permissions',
|
||||
# The ability to perform any read only operation on the dataset (view, display at external site,
|
||||
# use in a job, etc).
|
||||
DATASET_ACCESS = 'access'
|
||||
)
|
||||
permitted_action_descriptions = Bunch(
|
||||
DATASET_EDIT_METADATA = "User can edit this dataset's metadata in the library",
|
||||
DATASET_MANAGE_PERMISSIONS = "User can manage the groups and group permitted actions associated with this dataset",
|
||||
DATASET_ACCESS = "User can import this dataset into their history for analysis"
|
||||
)
|
||||
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 get_group( self, id ):
|
||||
raise 'No valid method of retrieving group %s' % ( id )
|
||||
def create_group( self, **kwd ):
|
||||
raise 'No valid method of creating group with %s' % ( kwd )
|
||||
def create_private_user_group( self, user ):
|
||||
raise "Unimplemented Method"
|
||||
def user_set_default_access( self, user, permissions = None, history = False, dataset = False ):
|
||||
raise "Unimplemented Method"
|
||||
def setup_new_user( self, user ):
|
||||
self.user_set_default_access( user, history = True, dataset = True )
|
||||
self.associate_components( user=user, group=self.get_public_group() )
|
||||
def history_set_default_access( self, history, permissions=None, dataset=False ):
|
||||
raise "Unimplemented Method"
|
||||
def set_public_group( self, group ):
|
||||
raise "Unimplemented Method"
|
||||
def get_public_group( self ):
|
||||
raise "Unimplemented Method"
|
||||
def guess_public_group( self ):
|
||||
raise "Unimplemented Method"
|
||||
def set_dataset_permissions( self, dataset, permissions ):
|
||||
raise "Unimplemented Method"
|
||||
def set_dataset_permitted_actions( self, dataset ):
|
||||
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 ] )
|
||||
def get_permitted_action_description( self, permitted_action ):
|
||||
"""
|
||||
Return the description of a permitted_action, regardless of
|
||||
whether permitted_action is a key or value.
|
||||
"""
|
||||
if self.permitted_action_descriptions.get( permitted_action ) is not None:
|
||||
return self.permitted_action_descriptions.get( permitted_action )
|
||||
else:
|
||||
for k, v in self.permitted_actions.items():
|
||||
if v == permitted_action:
|
||||
return self.permitted_action_descriptions.get( k )
|
||||
return permitted_action # so at least something useful is printable
|
||||
|
||||
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 user is None:
|
||||
try:
|
||||
public_assoc = [ gda for gda in dataset.groups if gda.group == self.get_public_group() ][0]
|
||||
except:
|
||||
# There'll be an IndexError if the dataset doesn't have a gda with public
|
||||
return False
|
||||
if action in public_assoc.permitted_actions:
|
||||
return True
|
||||
elif user is not None:
|
||||
# Loop through permitted_actions and if allowed return true:
|
||||
# Check permitted_actions associated with dataset through groups
|
||||
for group_dataset_assoc in dataset.groups:
|
||||
if self.components_are_associated( user = user, group = group_dataset_assoc.group ):
|
||||
if action in group_dataset_assoc.permitted_actions:
|
||||
return True
|
||||
return False # No user and dataset not in public group, or user lacks permission
|
||||
def guess_derived_permissions_for_datasets( self, datasets=[] ):
|
||||
"""Returns a list of group/action tuples for the output dataset based upon provided datasets"""
|
||||
intersect = None
|
||||
priority_access_perms = None
|
||||
for dataset in datasets:
|
||||
# Determine access groups for output datasets - these groups are the
|
||||
# intersection across all inputs. If we end up with no intersection
|
||||
# between inputs, then we rely on priorities
|
||||
if isinstance( dataset, self.model.HistoryDatasetAssociation ):
|
||||
dataset = dataset.dataset
|
||||
assocs = [ assoc for assoc in dataset.groups ]
|
||||
for assoc in assocs:
|
||||
if priority_access_perms is None or priority_access_perms[0].priority < assoc.group.priority:
|
||||
priority_access_perms = ( assoc.group, assoc.permitted_actions )
|
||||
if intersect is None:
|
||||
intersect = [ (a.group, a.permitted_actions) for a in assocs ]
|
||||
else:
|
||||
# Intersect existing perms with new perms
|
||||
#access_assocs = filter( lambda x: x.group in [ a.group for a in access_assocs ], assocs )
|
||||
new_intersect = []
|
||||
for group, actions in intersect:
|
||||
matches = filter( lambda x: x.group == group, assocs )
|
||||
# Could a dataset ever have more than one GDA with the same group id?
|
||||
if len( matches ) > 1:
|
||||
log.error( "Unable to derive permissions, duplicate group_dataset_association rows exist for group %d, dataset %d" % (group.id, dataset.id) )
|
||||
elif len( matches ) == 1:
|
||||
# compare permitted_actions
|
||||
pa_intersect = filter( lambda x: x in actions, matches[0].permitted_actions )
|
||||
new_intersect.append( ( group, pa_intersect ) )
|
||||
intersect = new_intersect
|
||||
# If we have no groups left after intersection, take the highest priority group
|
||||
if not intersect:
|
||||
if priority_access_perms:
|
||||
intersect = [ priority_access_perms ]
|
||||
return intersect
|
||||
def get_group( self, id ):
|
||||
return self.model.Group.get( id )
|
||||
raise 'No valid method of retrieving requested group by id %s' % ( str( id ) )
|
||||
def get_group_by_name( self, name ):
|
||||
return self.model.Group.filter_by( name=name ).first()
|
||||
raise 'No valid method of retrieving requested group by name %s' % ( str( name ) )
|
||||
def create_group( self, **kwd ):
|
||||
rval = self.model.Group( **kwd )
|
||||
rval.flush()
|
||||
return rval
|
||||
raise 'No valid method of creating group with %s' % ( kwd )
|
||||
def associate_components( self, **kwd ):
|
||||
assert len( kwd ) == 2, 'You must specify exactly 2 Galaxy security components to associate.'
|
||||
if 'dataset' in kwd:
|
||||
if 'group' in kwd:
|
||||
return self.associate_group_dataset( kwd['group'], kwd['dataset'] )
|
||||
elif 'permissions' in kwd:
|
||||
return self.associate_group_dataset( kwd['permissions'][0], kwd['dataset'], kwd['permissions'][1] )
|
||||
elif 'user' in kwd:
|
||||
if 'group' in kwd:
|
||||
return self.associate_user_group( kwd['user'], kwd['group'] )
|
||||
raise 'No valid method of associating provided components: %s' % kwd
|
||||
def associate_group_dataset( self, group, dataset, permitted_actions=[ RBACAgent.permitted_actions.DATASET_ACCESS ] ):
|
||||
# HACK: The default permitted_actions should really not be used... need to find cases where this is done and correct it
|
||||
assoc = self.model.GroupDatasetAssociation( group, dataset, permitted_actions )
|
||||
assoc.flush()
|
||||
return assoc
|
||||
def associate_user_group( self, user, group ):
|
||||
assoc = self.model.UserGroupAssociation( user, group )
|
||||
assoc.flush()
|
||||
return assoc
|
||||
def create_private_user_group( self, user ):
|
||||
# Create private group
|
||||
group_name = "%s private group" % user.email
|
||||
group = self.model.Group( name=group_name, priority=10 )
|
||||
group.flush()
|
||||
# Add user to group
|
||||
self.associate_components( group=group, user=user )
|
||||
group.flush()
|
||||
return group
|
||||
def user_set_default_access( self, user, permissions = None, history = False, dataset = False ):
|
||||
if permissions is None:
|
||||
permissions = [ ( self.create_private_user_group( user ), self.permitted_actions.__dict__.values() ) ]
|
||||
if permissions is not None:
|
||||
# Delete all of the previous DefaultUserGroupAssociation
|
||||
for duga in user.default_groups: #this is the association not the actual group
|
||||
duga.delete()
|
||||
duga.flush()
|
||||
# Add the new DefaultUserGroupAssociation
|
||||
for group, permitted_actions in permissions:
|
||||
duga = self.model.DefaultUserGroupAssociation( user, group, permitted_actions )
|
||||
duga.flush()
|
||||
if history:
|
||||
for history in user.active_histories:
|
||||
self.history_set_default_access( history, permissions=permissions, dataset=dataset )
|
||||
def user_get_default_access( self, user ):
|
||||
return [ ( duga.group, duga.permitted_actions ) for duga in user.default_groups ]
|
||||
def history_set_default_access( self, history, permissions=None, dataset=False ):
|
||||
if permissions is None:
|
||||
if history.user:
|
||||
permissions = self.user_get_default_access( history.user )
|
||||
else:
|
||||
permissions = [ ( self.get_public_group(), self.permitted_actions.__dict__.values() ) ]
|
||||
if permissions is not None:
|
||||
# Delete all of the previous DefaultHistoryGroupAssociations
|
||||
for dhga in history.default_groups: #this is the association not the actual group
|
||||
dhga.delete()
|
||||
dhga.flush()
|
||||
# Add the new DefaultHistoryGroupAssociations
|
||||
for group, permitted_actions in permissions:
|
||||
dhga = self.model.DefaultHistoryGroupAssociation( history, group, permitted_actions )
|
||||
dhga.flush()
|
||||
if dataset:
|
||||
for hda in history.datasets:
|
||||
for hda2 in hda.dataset.history_associations:
|
||||
if history.user and hda2.history not in history.user.active_histories:
|
||||
# This will occur when a user logs in and has datasets in their previously-public history.
|
||||
self.set_dataset_permissions( hda.dataset, [ ( self.get_public_group(), [ self.permitted_actions.DATASET_ACCESS ] ) ] )
|
||||
#break
|
||||
else:
|
||||
# At this point, we will have already created a gda for the public group and delete_assocs
|
||||
# is set to False in order to preserve it.
|
||||
if self.allow_action( history.user, self.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset=hda.dataset ):
|
||||
self.set_dataset_permissions( hda.dataset, permissions, delete_existing_assocs=False )
|
||||
def history_get_default_access( self, history ):
|
||||
return [ ( dhga.group, dhga.permitted_actions ) for dhga in history.default_groups ]
|
||||
def get_public_group( self ):
|
||||
return self.model.Group.get_public_group()
|
||||
def set_public_group( self, group ):
|
||||
return self.model.Group.set_public_group( group )
|
||||
def guess_public_group( self ):
|
||||
return self.model.Group.guess_public_group()
|
||||
def set_dataset_permissions( self, dataset, permissions, delete_existing_assocs=True ):
|
||||
"""
|
||||
Apply permissions (a list of (group, permitted_action) tuples)
|
||||
to a dataset, removing any existing permissions. permissions can
|
||||
also be a list of GroupDatasetAssociations (for simplicity).
|
||||
"""
|
||||
if isinstance( dataset, self.model.HistoryDatasetAssociation ):
|
||||
dataset = dataset.dataset
|
||||
if delete_existing_assocs:
|
||||
for gda in dataset.groups:
|
||||
gda.delete()
|
||||
gda.flush()
|
||||
if permissions and isinstance( permissions[0], self.model.GroupDatasetAssociation ):
|
||||
permissions = [ ( gda.group, gda.permitted_actions ) for gda in permissions ]
|
||||
for ptuple in permissions:
|
||||
self.associate_components( dataset=dataset, permissions=ptuple )
|
||||
def get_dataset_permissions( self, dataset, group_id=None ):
|
||||
if not isinstance( dataset, self.model.Dataset ):
|
||||
dataset = dataset.dataset
|
||||
if group_id is not None:
|
||||
return [ ( gda.group, gda.permitted_actions ) for gda in dataset.groups if gda.group_id == int(group_id) ][0]
|
||||
else:
|
||||
return [ ( gda.group, gda.permitted_actions ) for gda in dataset.groups ]
|
||||
def get_component_associations( self, **kwd ):
|
||||
# TODO, Nate: Make sure this method is functionally correct.
|
||||
assert len( kwd ) == 2, 'You must specify exactly 2 Galaxy security components to check for associations.'
|
||||
if 'dataset' in kwd:
|
||||
if 'group' in kwd:
|
||||
return self.model.GroupDatasetAssociation.filter_by( group_id=kwd['group'].id, 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()
|
||||
raise 'No valid method of associating provided components: %s' % kwd
|
||||
def dataset_has_group( self, dataset_id, group_id ):
|
||||
return bool( self.model.GroupDatasetAssociation.filter_by( group_id=group_id, dataset_id=dataset_id ).first() )
|
||||
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
|
||||
@@ -1067,7 +1067,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:
|
||||
@@ -1087,9 +1087,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
|
||||
@@ -1142,6 +1139,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.set_dataset_permissions( child_dataset.dataset, outdata.dataset.groups )
|
||||
# Move data from temp location to dataset location
|
||||
shutil.move( filename, child_dataset.file_name )
|
||||
child_dataset.flush()
|
||||
@@ -1178,6 +1176,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.set_dataset_permissions( primary_data.dataset, outdata.dataset.groups )
|
||||
primary_data.flush()
|
||||
# Move data from temp location to dataset location
|
||||
shutil.move( filename, primary_data.file_name )
|
||||
|
||||
@@ -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 permitted_actions 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_access( trans.history )
|
||||
# Build name for output datasets based on tool name and input names
|
||||
if len( input_names ) == 1:
|
||||
on_text = input_names[0]
|
||||
@@ -118,6 +129,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
|
||||
@@ -177,6 +189,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 )
|
||||
@@ -188,7 +203,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
|
||||
|
||||
@@ -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_access( 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_access( trans.history ) )
|
||||
data.name = file_name
|
||||
data.dbkey = dbkey
|
||||
data.info = info
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -977,29 +974,47 @@ 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
|
||||
|
||||
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.
|
||||
|
||||
>>> # Mock up a history (not connected to database)
|
||||
>>> from galaxy.model import History, HistoryDatasetAssociation
|
||||
>>> from galaxy.model import History, HistoryDatasetAssociation, User, Group
|
||||
>>> from galaxy.util.bunch import Bunch
|
||||
>>> from galaxy.security import GalaxyRBACAgent
|
||||
>>> import galaxy.model
|
||||
>>> security_agent = GalaxyRBACAgent( galaxy.model )
|
||||
>>> 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"/>' ) )
|
||||
>>> group = Group( 'test' )
|
||||
>>> group.flush()
|
||||
>>> Group.public_id = group.id
|
||||
>>> dataset1 = HistoryDatasetAssociation( id=1, extension='txt', create_dataset=True )
|
||||
>>> dataset2 = HistoryDatasetAssociation( id=2, extension='bed', create_dataset=True )
|
||||
>>> security_agent.set_dataset_permissions( dataset2, [ ( group, security_agent.permitted_actions.__dict__.values() ) ] )
|
||||
>>> dataset3 = HistoryDatasetAssociation( id=3, extension='fasta', create_dataset=True )
|
||||
>>> dataset4 = HistoryDatasetAssociation( id=4, extension='png', create_dataset=True )
|
||||
>>> dataset5 = HistoryDatasetAssociation( id=5, extension='interval', create_dataset=True )
|
||||
>>> security_agent.set_dataset_permissions( dataset5, [ ( group, security_agent.permitted_actions.__dict__.values() ) ] )
|
||||
>>> hist.add_dataset( dataset1 )
|
||||
>>> hist.add_dataset( dataset2 )
|
||||
>>> hist.add_dataset( dataset3 )
|
||||
>>> hist.add_dataset( dataset4 )
|
||||
>>> hist.add_dataset( dataset5 )
|
||||
>>> p = DataToolParameter( None, XML( '<param name="blah" type="data" format="txt"><security_check group="test" action="access"/></param>' ) )
|
||||
>>> print p.name
|
||||
blah
|
||||
>>> print p.get_html( trans=Bunch( history=hist ) )
|
||||
>>> print p.security_dict
|
||||
{'test': ['access']}
|
||||
>>> print p.get_html( trans=Bunch( history=hist, user=None, app=Bunch( security_agent = security_agent ) ) )
|
||||
<select name="blah">
|
||||
<option value="2">2: Unnamed dataset</option>
|
||||
<option value="5" selected>5: Unnamed dataset</option>
|
||||
</select>
|
||||
"""
|
||||
|
||||
@@ -1030,6 +1045,16 @@ class DataToolParameter( ToolParameter ):
|
||||
else:
|
||||
self.options = dynamic_options.DynamicOptions( options, self )
|
||||
self.is_dynamic = self.options is not None
|
||||
self.security_dict = {}
|
||||
for security_check in elem.findall( "security_check" ):
|
||||
group = security_check.get( 'group', None )
|
||||
assert group is not None, "security_check elements require a group attribute"
|
||||
action = security_check.get( 'action', None )
|
||||
assert action is not None, "security_check elements require an action attribute"
|
||||
try:
|
||||
self.security_dict[ group ].append( action )
|
||||
except:
|
||||
self.security_dict[ group ] = [ action ]
|
||||
|
||||
def get_html_field( self, trans=None, value=None, other_values={} ):
|
||||
filter_value = None
|
||||
@@ -1046,28 +1071,50 @@ 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] and data.visible:
|
||||
if self.options and data.get_dbkey() != filter_value:
|
||||
hid = str( hda.hid )
|
||||
if not hda.dataset.state == galaxy.model.Dataset.states.ERROR and hda.visible and trans.app.security_agent.allow_action( trans.user, hda.permitted_actions.DATASET_ACCESS, dataset=hda ):
|
||||
if self.security_dict:
|
||||
passed_security_check = True
|
||||
for group_name, permitted_actions in self.security_dict.items():
|
||||
# Make sure the dataset belongs to the group
|
||||
group = trans.app.security_agent.get_group_by_name( group_name )
|
||||
if not trans.app.security_agent.dataset_has_group( hda.dataset_id, group.id ):
|
||||
passed_security_check = False
|
||||
break
|
||||
# Make sure the permitted_actions are available on the dataset for the group
|
||||
group_dataset_permitted_actions = trans.app.security_agent.get_dataset_permissions( hda.dataset, group_id=group.id )[1]
|
||||
actions_allowed = True
|
||||
for action in permitted_actions:
|
||||
if not action in group_dataset_permitted_actions:
|
||||
actions_allowed = False
|
||||
break
|
||||
if not actions_allowed:
|
||||
passed_security_check = False
|
||||
break
|
||||
if not passed_security_check:
|
||||
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:
|
||||
|
||||
@@ -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_access( trans.history ) )
|
||||
data.name = GALAXY_NAME
|
||||
data.dbkey = GALAXY_BUILD
|
||||
data.info = GALAXY_INFO
|
||||
|
||||
@@ -103,28 +103,27 @@ 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." )
|
||||
|
||||
@@ -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'] )
|
||||
@@ -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,39 @@ 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.expose
|
||||
def dataset_import( self, trans, **kwd ):
|
||||
"""
|
||||
External applications (e.g., EpiGRAPH) can import data to Galaxy by passing the following:
|
||||
1. DATA_URL - the url to which Galaxy should post a request to retrieve the data
|
||||
2. GENOME - the name of the UCSC genome assembly (e.g. hg18), dbkey in Galaxy
|
||||
3. NAME - data.name in Galaxy
|
||||
4. INFO - data.info in Galaxy
|
||||
This method will create the tool parameters expected by the upload tool so that it
|
||||
can be executed to retrieve the data from the external application.
|
||||
"""
|
||||
params_dict = {}
|
||||
params = util.Params( kwd )
|
||||
DATA_URL = params.get( 'DATA_URL', None )
|
||||
assert DATA_URL is not None, "Required DATA_URL parameter missing from request"
|
||||
params_dict[ 'url_paste' ] = DATA_URL
|
||||
params_dict[ 'dbkey' ] = params.get( 'GENOME', '?' )
|
||||
NAME = params.get( 'NAME', None )
|
||||
assert NAME is not None, "Required NAME parameter missing from request"
|
||||
params_dict[ 'NAME' ] = NAME
|
||||
INFO = params.get( 'INFO', None )
|
||||
assert INFO is not None, "Required INFO parameter missing from request"
|
||||
params_dict[ 'INFO' ] = INFO
|
||||
params_dict[ 'runtool_btn' ] = 'Execute'
|
||||
tool_id = 'upload1'
|
||||
history = trans.get_history()
|
||||
trans.ensure_valid_galaxy_session()
|
||||
tool = trans.get_toolbox().tools_by_id.get( tool_id )
|
||||
template, vars = tool.handle_input( trans, params_dict )
|
||||
trans.log_event( "/root/dataset_import tool params: %s" % ( str( params_dict ) ), tool_id=tool_id )
|
||||
trans.response.send_redirect( url_for( "/index" ) )
|
||||
|
||||
@web.json
|
||||
def history_item_updates( self, trans, ids=None, states=None ):
|
||||
# Avoid caching
|
||||
@@ -137,22 +165,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 +195,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 +210,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,81 +231,117 @@ 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.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), p))
|
||||
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), p))
|
||||
|
||||
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
|
||||
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.datatype.metadata_spec.items():
|
||||
# We need to be careful about the attributes we are resetting
|
||||
if name != 'name' and name != 'info' and name != 'dbkey':
|
||||
if spec.get( 'default' ):
|
||||
setattr( data.metadata,name,spec.unwrap( spec.get( 'default' ), spec ))
|
||||
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.change_permission:
|
||||
"""The user clicked the change_permission button on the 'Change permissions' form"""
|
||||
if not trans.user:
|
||||
return trans.show_error_message( "You must be logged in if you want to change dataset permitted actions." )
|
||||
if trans.app.security_agent.allow_action( trans.user, data.dataset.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset = data.dataset ):
|
||||
group_args = [ k.replace('group_', '', 1) for k in kwd if k.startswith('group_') ]
|
||||
group_ids_checked = filter( lambda x: not x.count('_'), group_args )
|
||||
permissions = []
|
||||
for group_id in group_ids_checked:
|
||||
action_strings = [ action.replace(group_id + '_', '', 1) for action in group_args if action.startswith(group_id + '_') ]
|
||||
actions = trans.app.security_agent.convert_permitted_action_strings( action_strings )
|
||||
permissions.append( ( trans.app.security_agent.get_group( group_id ), actions ) )
|
||||
trans.app.security_agent.set_dataset_permissions( data.dataset, permissions )
|
||||
return trans.show_ok_message( "Dataset permissions have been set.", refresh_frames=['history'] )
|
||||
else:
|
||||
return trans.show_error_message( "You are not authorized to change this dataset's permitted actions." )
|
||||
|
||||
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
|
||||
metadata = list()
|
||||
# a list of MetadataParemeters
|
||||
for name, spec in data.datatype.metadata_spec.items():
|
||||
# We need to be careful about the attributes we are resetting
|
||||
if name != 'name' and name != 'info' and name != 'dbkey':
|
||||
if spec.get( 'default' ):
|
||||
setattr( data.metadata,name,spec.unwrap( spec.get( 'default' ), spec ))
|
||||
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
|
||||
metadata = list()
|
||||
# a list of MetadataParemeters
|
||||
for name, spec in data.datatype.metadata_spec.items():
|
||||
if spec.visible:
|
||||
metadata.append( spec.wrap( data.metadata.get(name), data ) )
|
||||
# let's not overwrite the imported datatypes module with the variable datatypes?
|
||||
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, metadata=metadata,
|
||||
datatypes=ldatatypes, err=None )
|
||||
if spec.visible:
|
||||
metadata.append( spec.wrap( data.metadata.get(name), data ) )
|
||||
# let's not overwrite the imported datatypes module with the variable datatypes?
|
||||
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, metadata=metadata,
|
||||
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, **kwd):
|
||||
@@ -327,6 +400,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 -----------------------------------------------
|
||||
@@ -356,8 +431,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 DefaultHistoryGroupAssociations
|
||||
for default_history_group_association in history.default_groups:
|
||||
default_history_group_association.delete()
|
||||
default_history_group_association.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()
|
||||
@@ -397,14 +477,48 @@ 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:
|
||||
if p.action == "no_share":
|
||||
trans.response.send_redirect( url_for( action='history_options' ) )
|
||||
try:
|
||||
send_to_group = trans.app.model.Group.filter_by( name=send_to_user.email+' private group' ).first()
|
||||
except:
|
||||
send_to_group = None
|
||||
if not send_to_group:
|
||||
return trans.show_error_message( "Couldn't locate %s's private group, please report this error." % user.email )
|
||||
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 dataset in history.active_datasets:
|
||||
if not trans.app.security_agent.allow_action( send_to_user, trans.app.security_agent.permitted_actions.DATASET_ACCESS, dataset=dataset ):
|
||||
if trans.app.security_agent.allow_action( user, trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset=dataset ):
|
||||
if p.action and p.action == "update":
|
||||
trans.app.security_agent.associate_components( dataset=dataset, permissions=( send_to_group, [ trans.app.security_agent.permitted_actions.DATASET_ACCESS ] ) )
|
||||
elif history not in can_change:
|
||||
can_change[history] = [ dataset ]
|
||||
else:
|
||||
can_change[history].append( dataset )
|
||||
else:
|
||||
if p.action and p.action == "update":
|
||||
pass # don't change stuff that the user doesn't have permission to change
|
||||
elif history not in cannot_change:
|
||||
cannot_change[history] = [ dataset ]
|
||||
else:
|
||||
cannot_change[history].append( dataset )
|
||||
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)) )
|
||||
@@ -441,12 +555,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 )
|
||||
@@ -463,7 +577,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 )
|
||||
@@ -488,7 +602,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 )
|
||||
@@ -548,11 +662,16 @@ 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 )
|
||||
groups = trans.app.security_agent.history_get_default_access( history )
|
||||
if copy_access_from:
|
||||
copy_access_from = trans.app.model.HistoryDatasetAssociation.get( copy_access_from )
|
||||
group_dataset_associations = copy_access_from.dataset.groups
|
||||
data = trans.app.model.HistoryDatasetAssociation( name = name, info = info, extension = ext, dbkey = dbkey, create_dataset = True )
|
||||
trans.app.security_agent.set_dataset_permissions( data.dataset, group_dataset_associations )
|
||||
data.flush()
|
||||
data_file = open( data.file_name, "wb" )
|
||||
file_data.file.seek( 0 )
|
||||
@@ -569,9 +688,35 @@ 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_permitted_actions( self, trans, **kwd ):
|
||||
"""Sets the user's default permitted_actions for the current history"""
|
||||
if trans.user:
|
||||
if 'set_permitted_actions' in kwd:
|
||||
"""The user clicked the set_permitted_actions button on the set_permitted_actions form"""
|
||||
history = trans.get_history()
|
||||
group_args = [ k.replace('group_', '', 1) for k in kwd if k.startswith('group_') ]
|
||||
group_ids_checked = filter( lambda x: not x.count('_'), group_args )
|
||||
if not group_ids_checked:
|
||||
return trans.show_error_message( "You must specify at least one default group." )
|
||||
permissions = []
|
||||
for group_id in group_ids_checked:
|
||||
group = trans.app.security_agent.get_group( group_id )
|
||||
if not group:
|
||||
return trans.show_error_message( 'You have specified an invalid group.' )
|
||||
action_strings = [ action.replace(group_id + '_', '', 1) for action in group_args if action.startswith(group_id + '_') ]
|
||||
permissions.append( ( group, trans.app.security_agent.convert_permitted_action_strings( action_strings ) ) )
|
||||
trans.app.security_agent.history_set_default_access( history, permissions = permissions )
|
||||
return trans.show_ok_message( 'Default history permitted actions 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 permitted actions." )
|
||||
|
||||
@web.expose
|
||||
def dataset_make_primary( self, trans, id=None):
|
||||
"""Copies a dataset and makes primary"""
|
||||
@@ -588,7 +733,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
|
||||
@@ -596,8 +741,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 ):
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
"""
|
||||
Contains the user interface in the Universe class
|
||||
"""
|
||||
|
||||
from galaxy.web.base.controller import *
|
||||
|
||||
from galaxy.model.orm import *
|
||||
import logging, os, string
|
||||
from random import choice
|
||||
|
||||
@@ -53,7 +52,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 +72,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 +107,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 +117,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 +143,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 +166,27 @@ 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_permitted_actions( self, trans, **kwd ):
|
||||
"""Sets the user's default permitted actions for the new histories"""
|
||||
if trans.user:
|
||||
if 'set_permitted_actions' in kwd:
|
||||
"""The user clicked the set_permitted_actions button on the set_permitted_actions form"""
|
||||
group_args = [ k.replace('group_', '', 1) for k in kwd if k.startswith('group_') ]
|
||||
group_ids_checked = filter( lambda x: not x.count('_'), group_args )
|
||||
if not group_ids_checked:
|
||||
return trans.show_error_message( "You must specify at least one default group." )
|
||||
permissions = []
|
||||
for group_id in group_ids_checked:
|
||||
group = trans.app.security_agent.get_group( group_id )
|
||||
if not group:
|
||||
return trans.show_error_message( 'You have specified an invalid group.' )
|
||||
action_strings = [ action.replace(group_id + '_', '', 1) for action in group_args if action.startswith(group_id + '_') ]
|
||||
permissions.append( ( group, trans.app.security_agent.convert_permitted_action_strings( action_strings ) ) )
|
||||
trans.app.security_agent.user_set_default_access( trans.user, permissions )
|
||||
return trans.show_ok_message( 'Default new history permitted actions 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." )
|
||||
|
||||
@@ -15,8 +15,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)
|
||||
"""
|
||||
@@ -31,7 +35,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 )
|
||||
|
||||
@@ -42,7 +46,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 )
|
||||
@@ -59,10 +63,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,
|
||||
@@ -77,7 +81,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 )
|
||||
@@ -102,11 +106,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" )
|
||||
@@ -127,11 +131,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" )
|
||||
@@ -148,7 +152,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" )
|
||||
@@ -304,7 +308,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 = []
|
||||
@@ -429,11 +433,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) ) ) )
|
||||
|
||||
@@ -736,4 +739,4 @@ def cleanup_param_values( inputs, values ):
|
||||
prefix = "%s|" % ( key )
|
||||
cleanup( prefix, input.cases[current_case].inputs, group_values )
|
||||
cleanup( "", inputs, values )
|
||||
return associations
|
||||
return associations
|
||||
|
||||
@@ -24,7 +24,7 @@ import mako.lookup
|
||||
pkg_resources.require( "simplejson" )
|
||||
import simplejson
|
||||
|
||||
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_access( 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,13 @@ 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()
|
||||
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 +283,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 +323,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 +384,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 +433,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_access( history, dataset=True )
|
||||
history.user_id = user.id
|
||||
history.flush()
|
||||
self.__history = history
|
||||
@@ -522,8 +528,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 +540,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 ):
|
||||
|
||||
@@ -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 )
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 593 B |
|
After Width: | Height: | Size: 622 B |
|
After Width: | Height: | Size: 537 B |
|
After Width: | Height: | Size: 688 B |
|
After Width: | Height: | Size: 446 B |
|
After Width: | Height: | Size: 395 B |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 31 KiB |
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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() )
|
||||
"""
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
|
||||
<%
|
||||
from galaxy.web.controllers.admin import entities, unentities
|
||||
from xml.sax.saxutils import escape, unescape
|
||||
%>
|
||||
|
||||
## Render a row
|
||||
<%def name="render_row( group_name, group )">
|
||||
<td><a href="${h.url_for( controller='admin', action='undelete_group', group_id=group[0] )}">Undelete</a></td>
|
||||
<td>${group_name}</td>
|
||||
<td>${group[2]}</td>
|
||||
<td><a href="${h.url_for( controller='admin', action='group_members', group_id=group[0], group_name=group[1] )}">${group[3]}</a></td>
|
||||
%if group[4] > 0:
|
||||
<td><a href="${h.url_for( controller='admin', action='group_dataset_permitted_actions', group_id=group[0], group_name=group[1] )}">${group[4]}</a></td>
|
||||
%else:
|
||||
<td>${group[4]}</td>
|
||||
%endif
|
||||
<td>
|
||||
%if len( group[5] ) == 1:
|
||||
${group[5][0]}
|
||||
%elif len( group[5] ) > 1:
|
||||
%for da in group[5]:
|
||||
${da}<br/>
|
||||
%endfor
|
||||
%endif
|
||||
</td>
|
||||
<td><a href="${h.url_for( controller='admin', action='purge_group', group_id=group[0] )}">Purge</a></td>
|
||||
</%def>
|
||||
|
||||
<%def name="title()">Groups</%def>
|
||||
<div class="toolForm">
|
||||
<div class="form-row">
|
||||
<a href="${h.url_for( controller='admin', action='libraries' )}">Libraries</a> |
|
||||
<a href="${h.url_for( controller='admin', action='groups' )}">Groups</a> |
|
||||
<tr><td><a href="${h.url_for( controller='admin', action='users' )}">Users</a></td></tr>
|
||||
</div>
|
||||
<table align="center" class="colored">
|
||||
%if msg:
|
||||
<tr><td colspan="7"><p class="ok_bgr">${msg}</p></td></tr>
|
||||
%endif
|
||||
%if len( groups ) == 0:
|
||||
<tr><td colspan="7">There are no deleted Galaxy groups</td></tr>
|
||||
%else:
|
||||
<%
|
||||
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 class="header"><td colspan="7"><center><a name="TOP">Deleted Galaxy Groups - Quick Find</a></td></center></tr>
|
||||
<tr>
|
||||
<td colspan="7">
|
||||
<center>
|
||||
|<a href="#A">A</a>|<a href="#B">B</a>|<a href="#C">C</a>|<a href="#D">D</a>|<a href="#E">E</a>|<a href="#F">F</a>
|
||||
|<a href="#G">G</a>|<a href="#H">H</a>|<a href="#I">I</a>|<a href="#J">J</a>|<a href="#K">K</a>|<a href="#L">L</a>
|
||||
|<a href="#M">M</a>|<a href="#N">N</a>|<a href="#O">O</a>|<a href="#P">P</a>|<a href="#Q">Q</a>|<a href="#R">R</a>
|
||||
|<a href="#S">S</a>|<a href="#T">T</a>|<a href="#U">U</a>|<a href="#V">V</a>|<a href="#W">W</a>|<a href="#X">X</a>
|
||||
|<a href="#Y">Y</a>|<a href="#Z">Z</a>
|
||||
</center>
|
||||
</td>
|
||||
</tr>
|
||||
%else:
|
||||
<tr class="header"><td colspan="7"><center>Deleted Galaxy Groups</td></center></tr>
|
||||
%endif
|
||||
<tr class="header">
|
||||
<td> </td>
|
||||
<td>Group</td>
|
||||
<td>Priority</td>
|
||||
<td>Members</td>
|
||||
<td>Datasets</td>
|
||||
<td>Group Permitted Actions on Datasets</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
%for group in groups:
|
||||
<% group_name = unescape( group[1], unentities ) %>
|
||||
%if render_quick_find and not group_name.upper().startswith( curr_anchor ):
|
||||
<% anchored = False %>
|
||||
%endif
|
||||
<tr>
|
||||
%if ctr % 2 == 1:
|
||||
<div class="odd_row">
|
||||
%endif
|
||||
%if render_quick_find and group_name.upper().startswith( curr_anchor ):
|
||||
%if not anchored:
|
||||
<tr>
|
||||
<td colspan="7" class=panel-body">
|
||||
<a name="${curr_anchor}"></a>
|
||||
<div style="float: right;"><a href="#TOP">quick find</a></div>
|
||||
<% anchored = True %>
|
||||
</td>
|
||||
</tr>
|
||||
%endif
|
||||
${render_row( group_name, group )}
|
||||
%elif render_quick_find:
|
||||
%for anchor in anchors[ anchor_loc: ]:
|
||||
%if group_name.upper().startswith( anchor ):
|
||||
%if not anchored:
|
||||
<tr>
|
||||
<td colspan="7" class="panel-body">
|
||||
<a name="${anchor}"></a>
|
||||
<div style="float: right;"><a href="#TOP">quick find</a></div>
|
||||
<%
|
||||
curr_anchor = anchor
|
||||
anchored = True
|
||||
%>
|
||||
</td>
|
||||
</tr>
|
||||
%endif
|
||||
${render_row( group_name, group )}
|
||||
<%
|
||||
anchor_loc = anchors.index( anchor )
|
||||
break
|
||||
%>
|
||||
%endif
|
||||
%endfor
|
||||
%else:
|
||||
${render_row( group_name, group )}
|
||||
%endif
|
||||
%if ctr % 2 == 1:
|
||||
</div>
|
||||
%endif
|
||||
<% ctr += 1 %>
|
||||
</tr>
|
||||
%endfor
|
||||
%endif
|
||||
</table>
|
||||
</div>
|
||||
@@ -0,0 +1,106 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
|
||||
<%
|
||||
from galaxy.web.controllers.admin import entities, unentities
|
||||
from xml.sax.saxutils import escape, unescape
|
||||
%>
|
||||
|
||||
<%def name="title()">Create Group</%def>
|
||||
<div class="toolForm">
|
||||
<div class="form-row">
|
||||
<a href="${h.url_for( controller='admin', action='libraries' )}">Libraries</a> |
|
||||
<a href="${h.url_for( controller='admin', action='groups' )}">Groups</a> |
|
||||
<tr><td><a href="${h.url_for( controller='admin', action='users' )}">Users</a></td></tr>
|
||||
</div>
|
||||
<h3 align="center">Create Group</h3>
|
||||
<table align="center" class="colored">
|
||||
%if msg:
|
||||
<tr><td><p class="ok_bgr">${msg}</p></td></tr>
|
||||
%endif
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0">
|
||||
<form name="group_create" action="${h.url_for( controller='admin', action='new_group' )}" method="post" >
|
||||
<tr>
|
||||
<td>Name: <input name="name" type="textfield" value="" size=40"> Priority: <input name="priority" type="textfield" value="0" size=3"></td>
|
||||
</tr>
|
||||
%if len( users ) == 0:
|
||||
<tr><td>There are no Galaxy users</td></tr>
|
||||
%else:
|
||||
<%
|
||||
render_quick_find = len( users ) > 50
|
||||
%>
|
||||
%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 class="header"><td><a name="TOP">Add Members to Group - Quick Find</a></td></tr>
|
||||
<tr>
|
||||
<td>
|
||||
|<a href="#A">A</a>|<a href="#B">B</a>|<a href="#C">C</a>|<a href="#D">D</a>|<a href="#E">E</a>|<a href="#F">F</a>
|
||||
|<a href="#G">G</a>|<a href="#H">H</a>|<a href="#I">I</a>|<a href="#J">J</a>|<a href="#K">K</a>|<a href="#L">L</a>
|
||||
|<a href="#M">M</a>|<a href="#N">N</a>|<a href="#O">O</a>|<a href="#P">P</a>|<a href="#Q">Q</a>|<a href="#R">R</a>
|
||||
|<a href="#S">S</a>|<a href="#T">T</a>|<a href="#U">U</a>|<a href="#V">V</a>|<a href="#W">W</a>|<a href="#X">X</a>
|
||||
|<a href="#Y">Y</a>|<a href="#Z">Z</a>
|
||||
</td>
|
||||
</tr>
|
||||
%else:
|
||||
<tr class="header"><td>Add Members to Group</td></tr>
|
||||
%endif
|
||||
<tr>
|
||||
<td>
|
||||
%for user in users:
|
||||
<% email = unescape( user[1], unentities ) %>
|
||||
%if render_quick_find and not email.upper().startswith( curr_anchor ):
|
||||
<% anchored = False %>
|
||||
%endif
|
||||
<tr>
|
||||
<td>
|
||||
%if render_quick_find and email.upper().startswith( curr_anchor ):
|
||||
%if not anchored:
|
||||
<a name="${curr_anchor}"></a>
|
||||
<hr/>
|
||||
<div style="float: right;"><a href="#TOP">quick find</a></div>
|
||||
<br/>
|
||||
<% anchored = True %>
|
||||
%endif
|
||||
<input type="checkbox" name="members" value="${user[0]}"/> ${email}
|
||||
%elif render_quick_find:
|
||||
%for anchor in anchors[ anchor_loc: ]:
|
||||
%if email.upper().startswith( anchor ):
|
||||
%if not anchored:
|
||||
<a name="${anchor}"></a>
|
||||
<hr/>
|
||||
<div style="float: right;"><a href="#TOP">quick find</a></div>
|
||||
<br/>
|
||||
<%
|
||||
curr_anchor = anchor
|
||||
anchored = True
|
||||
%>
|
||||
%endif
|
||||
<input type="checkbox" name="members" value="${user[0]}"/> ${email}
|
||||
<%
|
||||
anchor_loc = anchors.index( anchor )
|
||||
break
|
||||
%>
|
||||
%endif
|
||||
%endfor
|
||||
%else:
|
||||
<input type="checkbox" name="members" value="${user[0]}"/> ${email}
|
||||
%endif
|
||||
</td>
|
||||
</tr>
|
||||
%endfor
|
||||
</td>
|
||||
</tr>
|
||||
%endif
|
||||
<tr><td><center><button name="create_group_button" value="group_create">Create</button></center></td></tr>
|
||||
</form>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
@@ -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> |
|
||||
<a href="${h.url_for( controller='admin', action='groups' )}">Groups</a> |
|
||||
<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"> </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,54 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
|
||||
<%
|
||||
from galaxy.web.controllers.admin import entities, unentities
|
||||
from xml.sax.saxutils import escape, unescape
|
||||
%>
|
||||
|
||||
<% gn = unescape( group_name, unentities ) %>
|
||||
|
||||
<%def name="title()">Group Members</%def>
|
||||
|
||||
%if msg:
|
||||
<div class="donemessage">${msg}</div>
|
||||
%endif
|
||||
|
||||
<h2>Members of Group '${gn}'</h2>
|
||||
|
||||
%if not deleted:
|
||||
<ul class="manage-table-actions">
|
||||
<li>
|
||||
<a class="action-button" href="${h.url_for( controller='admin', action='group_members_edit', group_id=group_id, group_name=group_name )}">
|
||||
Manage group membership
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
%endif
|
||||
|
||||
%if len( members ) == 0:
|
||||
|
||||
Group '${gn}' contains no members
|
||||
|
||||
%else:
|
||||
|
||||
<table cellpadding="0" cellspacing="0" width="100%" class="colored">
|
||||
<tr class="header"><td>Username</td></tr>
|
||||
<% ctr = 0 %>
|
||||
%for member in members:
|
||||
<% email = unescape( member[1], unentities ) %>
|
||||
%if ctr % 2 == 1:
|
||||
<tr class="odd_row">
|
||||
%else:
|
||||
<tr class="tr">
|
||||
%endif
|
||||
<td><a href="${h.url_for( controller='admin',
|
||||
action='specified_users_groups',
|
||||
user_id=member[0],
|
||||
user_email=member[1] )}">${email}</a></td>
|
||||
</tr>
|
||||
<% ctr += 1 %>
|
||||
%endfor
|
||||
</table>
|
||||
|
||||
%endif
|
||||
@@ -0,0 +1,112 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
|
||||
<%
|
||||
from galaxy.web.controllers.admin import entities, unentities
|
||||
from xml.sax.saxutils import escape, unescape
|
||||
%>
|
||||
|
||||
<%def name="title()">Manage Group Membership</%def>
|
||||
|
||||
%if msg:
|
||||
<div class="donemessage">${msg}</div>
|
||||
%endif
|
||||
|
||||
<% gn = unescape( group_name, unentities ) %>
|
||||
|
||||
<h2>Members of group '${gn}'</h2>
|
||||
|
||||
<table cellpadding="0" cellspacing="0" width="100%" class="colored">
|
||||
<tr><td> </td></tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0">
|
||||
<form name="group_create" action="${h.url_for( controller='admin', action='update_group_members', group_id=group_id, group_name=group_name )}" method="post" >
|
||||
%if len( users ) == 0:
|
||||
<tr><td>There are no Galaxy users</td></tr>
|
||||
%else:
|
||||
<tr>
|
||||
<td>
|
||||
|<a href="#A">A</a>|<a href="#B">B</a>|<a href="#C">C</a>|<a href="#D">D</a>|<a href="#E">E</a>|<a href="#F">F</a>
|
||||
|<a href="#G">G</a>|<a href="#H">H</a>|<a href="#I">I</a>|<a href="#J">J</a>|<a href="#K">K</a>|<a href="#L">L</a>
|
||||
|<a href="#M">M</a>|<a href="#N">N</a>|<a href="#O">O</a>|<a href="#P">P</a>|<a href="#Q">Q</a>|<a href="#R">R</a>
|
||||
|<a href="#S">S</a>|<a href="#T">T</a>|<a href="#U">U</a>|<a href="#V">V</a>|<a href="#W">W</a>|<a href="#X">X</a>
|
||||
|<a href="#Y">Y</a>|<a href="#Z">Z</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<%
|
||||
ctr = 0
|
||||
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'
|
||||
%>
|
||||
%for user in users:
|
||||
<%
|
||||
email = unescape( user[1], unentities )
|
||||
check = False
|
||||
%>
|
||||
%for member in members:
|
||||
<% member_email = unescape( member[1], unentities ) %>
|
||||
%if email == member_email:
|
||||
<%
|
||||
check = True
|
||||
break
|
||||
%>
|
||||
%endif
|
||||
%endfor
|
||||
%if not email.upper().startswith( curr_anchor ):
|
||||
<% anchored = False %>
|
||||
%endif
|
||||
%if ctr % 2 == 1:
|
||||
<tr class="odd_row">
|
||||
%else:
|
||||
<tr class="tr">
|
||||
%endif
|
||||
<td>
|
||||
%if email.upper().startswith( curr_anchor ):
|
||||
%if not anchored:
|
||||
<a name="${curr_anchor}"><hr/></a><br/>
|
||||
<% anchored = True %>
|
||||
%endif
|
||||
%if check:
|
||||
<input type="checkbox" name="members" value="${user[0]}" checked/> ${email}
|
||||
%else:
|
||||
<input type="checkbox" name="members" value="${user[0]}"/> ${email}
|
||||
%endif
|
||||
%else:
|
||||
%for anchor in anchors[ anchor_loc: ]:
|
||||
%if email.upper().startswith( anchor ):
|
||||
%if not anchored:
|
||||
<a name="${anchor}"><hr/></a><br/>
|
||||
<%
|
||||
curr_anchor = anchor
|
||||
anchored = True
|
||||
%>
|
||||
%endif
|
||||
%if check:
|
||||
<input type="checkbox" name="members" value="${user[0]}" checked/> ${email}
|
||||
%else:
|
||||
<input type="checkbox" name="members" value="${user[0]}"/> ${email}
|
||||
%endif
|
||||
<%
|
||||
anchor_loc = anchors.index( anchor )
|
||||
break
|
||||
%>
|
||||
%endif
|
||||
%endfor
|
||||
%endif
|
||||
</td>
|
||||
<% ctr += 1 %>
|
||||
%endfor
|
||||
</td>
|
||||
</tr>
|
||||
%endif
|
||||
<tr><td><center><button name="group_members_edit_button" value="update_group_members">Update Membership</button></center></td></tr>
|
||||
</form>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
@@ -0,0 +1,137 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
|
||||
<%
|
||||
from galaxy.web.controllers.admin import entities, unentities
|
||||
from xml.sax.saxutils import escape, unescape
|
||||
%>
|
||||
|
||||
## Render a row
|
||||
<%def name="render_row( group_name, group )">
|
||||
<td>${group_name}</td>
|
||||
<td>${group[2]}</td>
|
||||
<td><a href="${h.url_for( controller='admin', action='group_members', group_id=group[0], group_name=group[1] )}">${group[3]}</a></td>
|
||||
%if group[4] > 0:
|
||||
<td><a href="${h.url_for( controller='admin', action='group_dataset_permitted_actions', group_id=group[0], group_name=group[1] )}">${group[4]}</a></td>
|
||||
%else:
|
||||
<td>${group[4]}</td>
|
||||
%endif
|
||||
<td>
|
||||
%if len( group[5] ) == 1:
|
||||
${group[5][0]}
|
||||
%elif len( group[5] ) > 1:
|
||||
%for da in group[5]:
|
||||
${da}<br/>
|
||||
%endfor
|
||||
%endif
|
||||
</td>
|
||||
<td><a href="${h.url_for( controller='admin', action='mark_group_deleted', group_id=group[0] )}">Mark group deleted</a></td>
|
||||
</%def>
|
||||
|
||||
<%def name="title()">Groups</%def>
|
||||
|
||||
%if msg:
|
||||
<div class="donemessage">${msg}</div>
|
||||
%endif
|
||||
|
||||
<h2>Groups</h2>
|
||||
|
||||
<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 ) == 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 ) > 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="6" 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>Priority</td>
|
||||
<td>Members</td>
|
||||
<td>Datasets</td>
|
||||
<td>Group Permitted Actions on Datasets</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
%for group in groups:
|
||||
<% group_name = unescape( group[1], unentities ) %>
|
||||
%if render_quick_find and not group_name.upper().startswith( curr_anchor ):
|
||||
<% anchored = False %>
|
||||
%endif
|
||||
<tr>
|
||||
%if ctr % 2 == 1:
|
||||
<div class="odd_row">
|
||||
%endif
|
||||
%if render_quick_find and group_name.upper().startswith( curr_anchor ):
|
||||
%if not anchored:
|
||||
<tr>
|
||||
<td colspan="6" class=panel-body">
|
||||
<a name="${curr_anchor}"></a>
|
||||
<div style="float: right;"><a href="#TOP">quick find</a></div>
|
||||
<% anchored = True %>
|
||||
</td>
|
||||
</tr>
|
||||
%endif
|
||||
${render_row( group_name, group )}
|
||||
%elif render_quick_find:
|
||||
%for anchor in anchors[ anchor_loc: ]:
|
||||
%if group_name.upper().startswith( anchor ):
|
||||
%if not anchored:
|
||||
<tr>
|
||||
<td colspan="6" class="panel-body">
|
||||
<a name="${anchor}"></a>
|
||||
<div style="float: right;"><a href="#TOP">quick find</a></div>
|
||||
<%
|
||||
curr_anchor = anchor
|
||||
anchored = True
|
||||
%>
|
||||
</td>
|
||||
</tr>
|
||||
%endif
|
||||
${render_row( group_name, group )}
|
||||
<%
|
||||
anchor_loc = anchors.index( anchor )
|
||||
break
|
||||
%>
|
||||
%endif
|
||||
%endfor
|
||||
%else:
|
||||
${render_row( group_name, group )}
|
||||
%endif
|
||||
%if ctr % 2 == 1:
|
||||
</div>
|
||||
%endif
|
||||
<% ctr += 1 %>
|
||||
</tr>
|
||||
%endfor
|
||||
%endif
|
||||
</table>
|
||||
</div>
|
||||
@@ -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>
|
||||
@@ -0,0 +1,72 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
|
||||
<%
|
||||
from galaxy.web.controllers.admin import entities, unentities
|
||||
from xml.sax.saxutils import escape, unescape
|
||||
%>
|
||||
|
||||
<% email = unescape( user_email, unentities ) %>
|
||||
|
||||
<%def name="title()">Group Membership</%def>
|
||||
|
||||
%if msg:
|
||||
<div class="donemessage">${msg}</div>
|
||||
%endif
|
||||
|
||||
<h2>Groups of which '${email}' is a member</h2>
|
||||
|
||||
%if len( groups ) == 0:
|
||||
|
||||
User '${email}' belongs to no groups
|
||||
|
||||
%else:
|
||||
|
||||
<table class="colored" cellpadding="0" cellspacing="0" width="100%">
|
||||
|
||||
<tr class="header">
|
||||
<td>Group</td>
|
||||
<td>Priority</td>
|
||||
<td>Datasets</td>
|
||||
<td>Permitted Actions on Datasets</td>
|
||||
<td>Containing Libraries</td>
|
||||
</tr>
|
||||
<% ctr = 0 %>
|
||||
%for group in groups:
|
||||
<%
|
||||
gn = unescape( group[1], unentities )
|
||||
library_ids = ''
|
||||
for library_id in group[5]:
|
||||
library_ids += "%s," % library_id
|
||||
library_ids = library_ids.rstrip( ',' )
|
||||
%>
|
||||
%if ctr % 2 == 1:
|
||||
<tr class="odd_row">
|
||||
%else:
|
||||
<tr class="tr">
|
||||
%endif
|
||||
<td>${gn}</td>
|
||||
<td>${group[2]}</td>
|
||||
%if group[3] > 0:
|
||||
<td><a href="${h.url_for( controller='admin', action='group_dataset_permitted_actions', group_id=group[0], group_name=group[1] )}">${group[3]}</a></td>
|
||||
%else:
|
||||
<td>${group[3]}</td>
|
||||
%endif
|
||||
<td>
|
||||
%for da in group[4]:
|
||||
${da}<br/>
|
||||
%endfor
|
||||
</td>
|
||||
<td>
|
||||
%if len( group[5] ) > 0:
|
||||
<a href="${h.url_for( controller='admin', action='specified_users_group_libraries', user_email=user_email, group_name=group[1], library_ids=library_ids )}">${len( group[5] )}</a>
|
||||
%else:
|
||||
${len( group[5] )}
|
||||
%endif
|
||||
</td>
|
||||
</tr>
|
||||
<% ctr += 1 %>
|
||||
%endfor
|
||||
|
||||
</table>
|
||||
|
||||
%endif
|
||||
@@ -0,0 +1,85 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
|
||||
<%
|
||||
from galaxy.web.controllers.admin import entities, unentities
|
||||
from xml.sax.saxutils import escape, unescape
|
||||
%>
|
||||
|
||||
<%def name="title()">Users</%def>
|
||||
|
||||
%if msg:
|
||||
<div class="donemessage">${msg}</div>
|
||||
%endif
|
||||
|
||||
<h2>Users</h2>
|
||||
|
||||
%if len( users ) == 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 ) > 50
|
||||
%>
|
||||
%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>Email address</td></tr>
|
||||
%for ctr, user in enumerate( users ):
|
||||
<% email = unescape( user[1], unentities ) %>
|
||||
%if render_quick_find and not email.upper().startswith( curr_anchor ):
|
||||
<% anchored = False %>
|
||||
%endif
|
||||
<tr
|
||||
%if ctr % 2 == 0:
|
||||
class="odd_row"
|
||||
%endif
|
||||
>
|
||||
<td>
|
||||
%if render_quick_find and email.upper().startswith( curr_anchor ):
|
||||
%if not anchored:
|
||||
<a name="${curr_anchor}"></a>
|
||||
<div style="float: right;"><a href="#TOP">top</a></div>
|
||||
<% anchored = True %>
|
||||
%endif
|
||||
<a href="${h.url_for( controller='admin', action='specified_users_groups', user_id=user[0], user_email=user[1] )}">${email}</a>
|
||||
%elif render_quick_find:
|
||||
%for anchor in anchors[ anchor_loc: ]:
|
||||
%if email.upper().startswith( anchor ):
|
||||
%if not anchored:
|
||||
<a name="${anchor}"></a>
|
||||
<div style="float: right;"><a href="#TOP">top</a></div>
|
||||
<%
|
||||
curr_anchor = anchor
|
||||
anchored = True
|
||||
%>
|
||||
%endif
|
||||
<a href="${h.url_for( controller='admin', action='specified_users_groups', user_id=user[0], user_email=user[1] )}">${email}</a>
|
||||
<%
|
||||
anchor_loc = anchors.index( anchor )
|
||||
break
|
||||
%>
|
||||
%endif
|
||||
%endfor
|
||||
%else:
|
||||
<a href="${h.url_for( controller='admin', action='specified_users_groups', user_id=user[0], user_email=user[1] )}">${email}</a>
|
||||
%endif
|
||||
</td>
|
||||
</tr>
|
||||
%endfor
|
||||
</table>
|
||||
|
||||
%endif
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
<%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>
|
||||
</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=""> </iframe>
|
||||
|
||||
</%def>
|
||||
@@ -0,0 +1,179 @@
|
||||
<%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;">▼</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='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>
|
||||
%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;">▼</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>
|
||||
@@ -0,0 +1,253 @@
|
||||
<%def name="render_permissions_forms( data_obj )">
|
||||
<script type="text/javascript">
|
||||
var q = jQuery.noConflict();
|
||||
q( document ).ready( function () {
|
||||
// initialize state
|
||||
q("input.groupCheckbox").each( function() {
|
||||
if ( ! q(this).is(":checked") ) q("div#group_" + this.value).hide();
|
||||
});
|
||||
// handle events
|
||||
q("input.groupCheckbox").click( function() {
|
||||
if ( q(this).is(":checked") ) {
|
||||
q("div#group_" + this.value).slideDown("fast");
|
||||
} else {
|
||||
q("div#group_" + this.value).slideUp("fast");
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<%
|
||||
redirect = None
|
||||
id = None
|
||||
if isinstance( data_obj, trans.app.model.LibraryFolderDatasetAssociation ):
|
||||
dataset = data_obj.dataset
|
||||
redirect = ("lid", data_obj.id)
|
||||
elif isinstance( data_obj, list ):
|
||||
dataset = data_obj[0].dataset
|
||||
id = ",".join( [ str(d.dataset.id) for d in data_obj ] )
|
||||
if isinstance( data_obj[0], trans.app.model.LibraryFolderDatasetAssociation ):
|
||||
redirect = ("lid", ",".join( [ str(d.id) for d in data_obj ] ) )
|
||||
else:
|
||||
trans.show_error_message( "Unknown object passed to render_permissions_forms" )
|
||||
if id is None:
|
||||
id = dataset.id
|
||||
%>
|
||||
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">Change Existing Permissions</div>
|
||||
<div class="toolFormBody">
|
||||
<form name="edit_group_associations" action="${h.url_for( controller='admin', action='dataset_permissions' )}" method="post">
|
||||
<input type="hidden" name="id" value="${id}">
|
||||
%if redirect:
|
||||
<input type="hidden" name="${redirect[0]}" value="${redirect[1]}">
|
||||
%endif
|
||||
<div class="form-row">
|
||||
<%
|
||||
user_permissions = [ p for p in trans.app.security_agent.get_dataset_permissions( dataset ) if p[0].name.endswith( ' private group' ) ]
|
||||
real_permissions = [ p for p in trans.app.security_agent.get_dataset_permissions( dataset ) if not p[0].name.endswith( ' private group' ) and p[0].name != 'public' ]
|
||||
%>
|
||||
<div class="toolParamHelp" style="clear: both;">
|
||||
Update the permissions for the users and groups currently associated with this dataset.
|
||||
</div>
|
||||
<br/>
|
||||
%if trans.app.security_agent.dataset_has_group( dataset.id, trans.app.security_agent.get_public_group().id ):
|
||||
<label>Allow public access:</label><br/>
|
||||
<input type="checkbox" name="public" value="Yes" checked> This dataset can be accessed by anyone (it is public).<br/>
|
||||
%endif
|
||||
<br/>
|
||||
<label>Users with permissions on this dataset:</label><br/>
|
||||
%if not len( user_permissions ):
|
||||
None<br/>
|
||||
%endif
|
||||
%for p in user_permissions:
|
||||
<input type="checkbox" name="users" class="groupCheckbox" value="${p[0].id}" checked/> ${p[0].name.replace( ' private group', '' )} <br/>
|
||||
<div class="permissionContainer" id="group_${p[0].id}">
|
||||
%for k, v in trans.app.security_agent.permitted_actions.items():
|
||||
<input type="checkbox" name="actions" value="${p[0].id},${k}"
|
||||
%if v in p[1]:
|
||||
checked
|
||||
%endif
|
||||
/> ${trans.app.security_agent.get_permitted_action_description(k)} <br/>
|
||||
%endfor
|
||||
</div>
|
||||
%endfor
|
||||
<br/>
|
||||
<label>Groups with permissions on this dataset:</label><br/>
|
||||
%if not len( real_permissions ):
|
||||
None<br/>
|
||||
%endif
|
||||
%for p in real_permissions:
|
||||
<input type="checkbox" name="groups" class="groupCheckbox" value="${p[0].id}" checked/> ${p[0].name} <br/>
|
||||
<div class="permissionContainer" id="group_${p[0].id}">
|
||||
%for k, v in trans.app.security_agent.permitted_actions.items():
|
||||
<input type="checkbox" name="actions" value="${p[0].id},${k}"
|
||||
%if v in p[1]:
|
||||
checked
|
||||
%endif
|
||||
/> ${trans.app.security_agent.get_permitted_action_description(k)} <br/>
|
||||
%endfor
|
||||
</div>
|
||||
%endfor
|
||||
<br/>
|
||||
</div>
|
||||
<div class="form-row"><input type="submit" name="change_permitted_actions" value="Save"></div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<p/>
|
||||
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">Add New Users and Groups</div>
|
||||
<div class="toolFormBody">
|
||||
<form name="make_new_group_associations" action="${h.url_for( controller='admin', action='dataset_permissions' )}" method="post">
|
||||
<input type="hidden" name="id" value="${id}">
|
||||
%if redirect:
|
||||
<input type="hidden" name="${redirect[0]}" value="${redirect[1]}">
|
||||
%endif
|
||||
<div class="form-row">
|
||||
<div class="toolParamHelp" style="clear: both;">
|
||||
Select new users and groups to associate with this dataset (you can assign permissions after creating the association).
|
||||
</div>
|
||||
## Don't display the public access checkbox if it's already public (it's shown above instead)
|
||||
%if not trans.app.security_agent.dataset_has_group( dataset.id, trans.app.security_agent.get_public_group().id ):
|
||||
<label>Allow public access:</label><br/>
|
||||
<input type="checkbox" name="public" value="Yes"> This dataset can be accessed by anyone (make it public).<br/>
|
||||
%endif
|
||||
<br/>
|
||||
<%
|
||||
all_groups = trans.app.model.Group.select()
|
||||
user_groups = [ g for g in all_groups if g.name.endswith( ' private group' ) ]
|
||||
real_groups = [ g for g in all_groups if not g.name.endswith( ' private group' ) and g.name != 'public' ]
|
||||
%>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<label>Associate with users:</label>
|
||||
<select name="users" multiple="true" size="5">
|
||||
%for group in user_groups:
|
||||
%if not trans.app.security_agent.dataset_has_group( dataset.id, group.id ):
|
||||
<option value="${group.id}">${group.name.replace( ' private group', '' )}</option>
|
||||
%endif
|
||||
%endfor
|
||||
</select>
|
||||
<p/>
|
||||
<label>Associate with groups:</label>
|
||||
<select name="groups" multiple="true" size="5">
|
||||
%for group in real_groups:
|
||||
%if dataset and not trans.app.security_agent.dataset_has_group( dataset.id, group.id ):
|
||||
<option value="${group.id}">${group.name}</option>
|
||||
%endif
|
||||
%endfor
|
||||
</select>
|
||||
</div>
|
||||
<div class="toolParamHelp" style="clear: both;">
|
||||
To select multiple users or groups, hold ctrl or command while clicking.
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row"><input type="submit" name="create_group_associations" value="Save"></div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<p/>
|
||||
|
||||
</%def>
|
||||
|
||||
<%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;">▼</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>
|
||||
@@ -0,0 +1,92 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="common.mako" import="render_permissions_forms" />
|
||||
|
||||
<%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>
|
||||
|
||||
${render_permissions_forms( dataset )}
|
||||
|
||||
%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 element in metadata:
|
||||
<div class="form-row">
|
||||
<label>${element.spec.desc}:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
${element.get_html()}
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
%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>
|
||||
@@ -0,0 +1,115 @@
|
||||
<%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 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;">
|
||||
Here you may 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;">
|
||||
You may also choose to 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 entering intervals by hand.
|
||||
</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 class="toolParamHelp" style="clear: both;">
|
||||
Which format? See help below
|
||||
</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">
|
||||
<label>Allow public access:</label>
|
||||
<input type="checkbox" name="public" value="Yes"> This dataset can be accessed by anyone (make it public).<br/>
|
||||
<p/>
|
||||
<% user_groups = [ g for g in groups if g[1].endswith( ' private group' ) ] %>
|
||||
<% real_groups = [ g for g in groups if not g[1].endswith( ' private group' ) and g[1] != 'public' ] %>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<label>Associate with users:</label>
|
||||
<select name="users" multiple="true" size="5">
|
||||
%for group in user_groups:
|
||||
<option value="${group[0]}">${group[1].replace( ' private group', '' )}</option>
|
||||
%endfor
|
||||
</select>
|
||||
%if len( real_groups ):
|
||||
<p/>
|
||||
<label>Associate with groups:</label>
|
||||
<select name="groups" multiple="true" size="5">
|
||||
%for group in real_groups:
|
||||
<option value="${group[0]}">${group[1]}</option>
|
||||
%endfor
|
||||
</select>
|
||||
%endif
|
||||
</div>
|
||||
<div class="toolParamHelp" style="clear: both;">
|
||||
To select multiple users or groups, hold ctrl or command while clicking.
|
||||
</div>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
<div class="form-row">
|
||||
<input type="submit" class="primary-button" name="create_dataset" value="Add Dataset to Folder">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,27 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
|
||||
<%
|
||||
from galaxy.web.controllers.admin import entities, unentities
|
||||
from xml.sax.saxutils import escape, unescape
|
||||
%>
|
||||
|
||||
<%def name="title()">Specified Users Group Libraries{</%def>
|
||||
<%
|
||||
gn = unescape( group_name, unentities )
|
||||
email = unescape( user_email, unentities )
|
||||
%>
|
||||
<div class="toolForm">
|
||||
<div class="form-row">
|
||||
<a href="${h.url_for( controller='admin', action='libraries' )}">Libraries</a> |
|
||||
<a href="${h.url_for( controller='admin', action='groups' )}">Groups</a> |
|
||||
<a href="${h.url_for( controller='admin', action='users' )}">Users</a>
|
||||
</div>
|
||||
<div class="toolFormTitle">Libraries containing datasets associated with group '${gn}' that user '${email}' can access</div>
|
||||
<div class="toolFormBody">
|
||||
%for library in libraries:
|
||||
<div class="form-row">
|
||||
<a href="${h.url_for( controller='admin', action='library', id=library.id )}">${library.name}</a>
|
||||
</div>
|
||||
%endfor
|
||||
</div>
|
||||
</div>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%def name="title()">Your saved histories</%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>
|
||||
@@ -70,39 +78,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
|
||||
|
||||
|
||||
@@ -110,7 +120,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:
|
||||
@@ -132,3 +142,104 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p />
|
||||
%else:
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">View Attributes</div>
|
||||
<div class="toolFormBody">
|
||||
<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 />
|
||||
%endif
|
||||
|
||||
%if trans.user and ( trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset = data ) ):
|
||||
<script type="text/javascript">
|
||||
var q = jQuery.noConflict();
|
||||
q( document ).ready( function () {
|
||||
// initialize state
|
||||
q("input.groupCheckbox").each( function() {
|
||||
if ( ! q(this).is(":checked") ) q("div#" + this.name).hide();
|
||||
});
|
||||
// handle events
|
||||
q("input.groupCheckbox").click( function() {
|
||||
if ( q(this).is(":checked") ) {
|
||||
q("div#" + this.name).slideDown("fast");
|
||||
} else {
|
||||
q("div#" + this.name).slideUp("fast");
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">Change Dataset Access Permissions</div>
|
||||
<div class="toolFormBody">
|
||||
<form name="change_permission_form" action="${h.url_for( action='edit' )}" method="post">
|
||||
<input type="hidden" name="${id_name}" value="${data.id}">
|
||||
<div class="form-row">
|
||||
<% user_groups = [ assoc.group for assoc in trans.user.groups ] %>
|
||||
<% dataset_group_ids = [ assoc.group.id for assoc in data.dataset.groups ] %>
|
||||
<div class="toolParamHelp" style="clear: both;">
|
||||
Check each group which should have access to this dataset.
|
||||
</div>
|
||||
%for group in user_groups:
|
||||
%if group.id in dataset_group_ids:
|
||||
<% assoc = filter( lambda x: x.group_id == group.id, data.dataset.groups )[0] %>
|
||||
%else:
|
||||
<% assoc = None %>
|
||||
%endif
|
||||
<input type="checkbox" name="group_${group.id}" class="groupCheckbox"
|
||||
%if assoc is not None:
|
||||
checked
|
||||
%endif
|
||||
/> ${group.name} <br/>
|
||||
<div class="permissionContainer" id="group_${group.id}">
|
||||
%for k, v in trans.app.security_agent.permitted_actions.items():
|
||||
<input type="checkbox" name="group_${group.id}_${k}"
|
||||
%if assoc is not None and v in assoc.permitted_actions:
|
||||
checked
|
||||
%endif
|
||||
/> ${trans.app.security_agent.get_permitted_action_description(k)} <br/>
|
||||
%endfor
|
||||
</div>
|
||||
%endfor
|
||||
<input type="submit" name="change_permission" value="Save">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
%elif trans.user and ( trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_ACCESS, dataset = data ) ):
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">Dataset Access Permissions</div>
|
||||
<div class="toolFormBody">
|
||||
<div class="form-row">
|
||||
<label>The following groups may perform the actions listed on this dataset:</label>
|
||||
<br/>
|
||||
%for assoc in data.dataset.groups:
|
||||
${assoc.group.name}
|
||||
<ul>
|
||||
%for action in assoc.permitted_actions:
|
||||
<li>${trans.app.security_agent.get_permitted_action_description(action)}</li>
|
||||
%endfor
|
||||
</ul>
|
||||
%endfor
|
||||
<div class="toolParamHelp" style="clear: both;">
|
||||
You are not allowed to change the permitted actions on this dataset.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
%endif
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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_permitted_actions' )}">Change default permitted actions</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_delete', id=history.id )}" confirm="Are you sure you want to delete the current history?">Delete</a> current history</div>
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%def name="title()">Change Default History Permitted Actions</%def>
|
||||
|
||||
%if trans.user:
|
||||
<script type="text/javascript">
|
||||
var q = jQuery.noConflict();
|
||||
q( document ).ready( function () {
|
||||
// initialize state
|
||||
q("input.groupCheckbox").each( function() {
|
||||
if ( ! q(this).is(":checked") ) q("div#" + this.name).hide();
|
||||
});
|
||||
// handle events
|
||||
q("input.groupCheckbox").click( function() {
|
||||
if ( q(this).is(":checked") ) {
|
||||
q("div#" + this.name).slideDown("fast");
|
||||
} else {
|
||||
q("div#" + this.name).slideUp("fast");
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">Change Default History Permitted Actions</div>
|
||||
<div class="toolFormBody">
|
||||
<form name="set_permitted_actions" method="post">
|
||||
<div class="form-row">
|
||||
<% user_groups = [ assoc.group for assoc in trans.user.groups ] %>
|
||||
<% cur_groups = [ assoc.group for assoc in trans.get_history().default_groups ] %>
|
||||
%for group in user_groups:
|
||||
<input type="checkbox" name="group_${group.id}" class="groupCheckbox"
|
||||
%if group in cur_groups:
|
||||
<% assoc = filter( lambda x: x.group_id == group.id, trans.get_history().default_groups )[0] %>
|
||||
checked
|
||||
%else:
|
||||
<% assoc = None %>
|
||||
%endif
|
||||
/> ${group.name} <br/>
|
||||
<div class="permissionContainer" id="group_${group.id}">
|
||||
%for k, v in trans.app.security_agent.permitted_actions.items():
|
||||
<input type="checkbox" name="group_${group.id}_${k}"
|
||||
%if assoc is not None and v in assoc.permitted_actions:
|
||||
checked
|
||||
%endif
|
||||
/> ${trans.app.security_agent.get_permitted_action_description(k)} <br/>
|
||||
%endfor
|
||||
</div>
|
||||
%endfor
|
||||
|
||||
<div style="clear: both"></div>
|
||||
|
||||
<div class="toolParamHelp" style="clear: both;">
|
||||
This will change the default permitted actions assigned
|
||||
to new datasets in your current history. You may also
|
||||
specify the defaults for new histories via the
|
||||
<a href="${h.url_for(controller='user')}">user options</a>.
|
||||
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<input type="submit" name="set_permitted_actions" value="Save">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
%endif
|
||||
@@ -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,85 @@
|
||||
<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="update"> Change permissions
|
||||
%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
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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
|
||||
|
||||
## Header row for history items (name, state, action buttons)
|
||||
|
||||
@@ -32,7 +36,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>
|
||||
@@ -100,4 +106,4 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</%def>
|
||||
</%def>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
<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> -->
|
||||
|
||||
<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
|
||||
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -110,4 +110,4 @@
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -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_permitted_actions' )}">Change default permitted actions</a> for new histories</li>
|
||||
<li><a href="${h.url_for( action='logout' )}">Logout</a></li>
|
||||
</ul>
|
||||
%else:
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%def name="title()">Change Default History Permitted Actions</%def>
|
||||
|
||||
%if trans.user:
|
||||
<script type="text/javascript">
|
||||
var q = jQuery.noConflict();
|
||||
q( document ).ready( function () {
|
||||
// initialize state
|
||||
q("input.groupCheckbox").each( function() {
|
||||
if ( ! q(this).is(":checked") ) q("div#" + this.name).hide();
|
||||
});
|
||||
// handle events
|
||||
q("input.groupCheckbox").click( function() {
|
||||
if ( q(this).is(":checked") ) {
|
||||
q("div#" + this.name).slideDown("fast");
|
||||
} else {
|
||||
q("div#" + this.name).slideUp("fast");
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">Change Default Permitted Actions for new Histories </div>
|
||||
<div class="toolFormBody">
|
||||
<form name="set_permitted_actions" method="post">
|
||||
<div class="form-row">
|
||||
<% user_groups = [ assoc.group for assoc in trans.user.groups ] %>
|
||||
<% cur_groups = [ assoc.group for assoc in trans.user.default_groups ] %>
|
||||
%for group in user_groups:
|
||||
<input type="checkbox" name="group_${group.id}" class="groupCheckbox"
|
||||
%if group in cur_groups:
|
||||
<% assoc = filter( lambda x: x.group_id == group.id, trans.user.default_groups )[0] %>
|
||||
checked
|
||||
%else:
|
||||
<% assoc = None %>
|
||||
%endif
|
||||
/> ${group.name} <br/>
|
||||
<div class="permissionContainer" id="group_${group.id}">
|
||||
%for k, v in trans.app.security_agent.permitted_actions.items():
|
||||
<input type="checkbox" name="group_${group.id}_${k}"
|
||||
%if assoc is not None and v in assoc.permitted_actions:
|
||||
checked
|
||||
%endif
|
||||
/> ${trans.app.security_agent.get_permitted_action_description(k)} <br/>
|
||||
%endfor
|
||||
</div>
|
||||
%endfor
|
||||
|
||||
<div style="clear: both"></div>
|
||||
|
||||
<div class="toolParamHelp" style="clear: both;">
|
||||
This will change the default permitted actions assigned
|
||||
to new datasets in new histories. You may also specify
|
||||
per-history defaults via the
|
||||
<a href="${h.url_for(controller='root', action='history_options')}">history options</a>.
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<input type="submit" name="set_permitted_actions" value="Save">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
%endif
|
||||
@@ -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="/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">
|
||||
|
||||
@@ -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;">▼</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;">▼</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>
|
||||
@@ -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;">▼</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;">▼</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>
|
||||
@@ -9,6 +9,9 @@ import twill.commands as tc
|
||||
from twill.other_packages._mechanize_dist import ClientForm
|
||||
from elementtree import ElementTree
|
||||
|
||||
from galaxy.web.controllers.admin import entities, unentities
|
||||
from xml.sax.saxutils import escape, unescape
|
||||
|
||||
buffer = StringIO.StringIO()
|
||||
|
||||
#Force twill to log to a buffer -- FIXME: Should this go to stdout and be captured by nose?
|
||||
@@ -30,7 +33,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 +85,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 +212,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 +363,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:
|
||||
@@ -380,7 +383,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 +505,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 +528,73 @@ class TwillTestCase( unittest.TestCase ):
|
||||
else:
|
||||
break
|
||||
self.assertNotEqual(count, maxiter)
|
||||
|
||||
# Dataset Security stuff
|
||||
def create_group( self, name='New Test Group', priority='10' ):
|
||||
"""Create a new group with 1 member"""
|
||||
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 )
|
||||
tc.fv( "1", "priority", priority )
|
||||
# twill version 0.9 still does not allow for easily testing forms that contain
|
||||
# multiple fields with the same name ( e.g., check boxes ). We could attempt to determine
|
||||
# the number of the field on the form and use it instead of the name of the field, but we
|
||||
# would be forced to drop and recreate the database every time we test in order to ensure
|
||||
# the fields will be the same on the form ( since it is dynamically rendered from the database ).
|
||||
tc.fv( "1", "3", "1" ) # 1-based form field 3 is the 1st check box named 'members', user id 1 is test@bx.psu.edu
|
||||
tc.submit( "create_group_button" )
|
||||
except AssertionError, err:
|
||||
errmsg = 'Exception caught attempting to create group: %s' % str( err )
|
||||
raise AssertionError( errmsg )
|
||||
self.home()
|
||||
return form
|
||||
def add_group_member( self, group_id='', group_name='' ):
|
||||
"""Add a member to an existing group"""
|
||||
# twill version 0.9 does not allow for this test
|
||||
self.visit_url( "%s/admin/group_members_edit?group_id=%s&group_name=%s" % ( self.url, group_id, group_name ) )
|
||||
self.check_page_for_string( 'Members of' )
|
||||
try:
|
||||
tc.fv( "1", "", "2" ) # 1-based form field 2 is the 2nd check box named 'members', user id 2 is test2@bx.psu.edu
|
||||
tc.submit( "group_members_edit_button" )
|
||||
except AssertionError, err:
|
||||
errmsg = 'Exception caught attempting to create group: %s' % str( err )
|
||||
raise AssertionError( errmsg )
|
||||
self.home()
|
||||
def mark_group_deleted( self, group_id='' ):
|
||||
"""Delete an existing group"""
|
||||
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=None ):
|
||||
"""Purge an existing group"""
|
||||
if not group.deleted:
|
||||
self.mark_group_deleted( group_id=group.id )
|
||||
group_id = str( group.id )
|
||||
group_name = group.name.replace( ' ', '+' )
|
||||
self.visit_url( "%s/admin/deleted_groups" % self.url )
|
||||
self.check_page_for_string( '%s' % group.name )
|
||||
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()
|
||||
|
||||
@@ -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" )
|
||||
|
||||
@@ -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,91 @@
|
||||
import galaxy.model
|
||||
from base.twilltestcase import TwillTestCase
|
||||
|
||||
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/dataset_security" % 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/group_dataset_permitted_actions_edit" % self.url )
|
||||
self.check_page_for_string( s )
|
||||
self.visit_url( "%s/admin/mark_group_deleted" % self.url )
|
||||
self.check_page_for_string( s )
|
||||
self.visit_url( "%s/admin/purge_group" % 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/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' )
|
||||
self.logout()
|
||||
# Need to ensure that we have 2 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 )
|
||||
def test_10_create_group( self ):
|
||||
"""Testing creating new group with 1 members"""
|
||||
self.login( email='test@bx.psu.edu' )
|
||||
self.visit_page( "admin/groups" )
|
||||
# the following should have been created when account was created.
|
||||
self.check_page_for_string( 'test@bx.psu.edu private group' )
|
||||
self.create_group( name='New Test Group', priority='10' )
|
||||
self.visit_page( "admin/groups" )
|
||||
self.check_page_for_string( "group_name=New+Test+Group" )
|
||||
# twill version 0.9 still does not allow for the following test
|
||||
#def test_15_add_group_member( self ):
|
||||
# """Testing adding a member to an existing group"""
|
||||
# group = galaxy.model.Group.filter_by( name='New Test Group' ).all()[0]
|
||||
# group_id = str( group.id )
|
||||
# group_name = group.name.replace( ' ', '+' )
|
||||
# self.add_group_member( group_id=group_id, group_name=group_name )
|
||||
# self.visit_page( 'admin/group_members?group_id=%s&group_name=%s' % ( group_id, group_name ) )
|
||||
# self.check_page_for_string( 'test@bx.psu.edu' )
|
||||
# self.check_page_for_string( 'test2@bx.psu.edu' )
|
||||
def test_20_delete_group( self ):
|
||||
"""Testing deleting a group"""
|
||||
self.visit_page( "admin/groups" )
|
||||
self.check_page_for_string( "group_name=New+Test+Group" )
|
||||
group = galaxy.model.Group.filter_by( name='New Test Group' ).all()[0]
|
||||
group_id = str( group.id )
|
||||
self.mark_group_deleted( group_id=group_id )
|
||||
def test_25_undelete_group( self ):
|
||||
"""Testing undeleting a deleted group"""
|
||||
group = galaxy.model.Group.filter_by( name='New Test Group' ).all()[0]
|
||||
group_id = str( group.id )
|
||||
self.undelete_group( group_id=group_id )
|
||||
def test_30_purge_group( self ):
|
||||
"""Testing purging a group"""
|
||||
group = galaxy.model.Group.filter_by( name='New Test Group' ).all()[0]
|
||||
self.purge_group( group=group )
|
||||
|
||||
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" )
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<toolbox>
|
||||
<section name="Get Data" id="getext">
|
||||
<tool file="data_source/upload.xml"/>
|
||||
<tool file="data_source/access_libraries.xml" />
|
||||
<tool file="data_source/ucsc_tablebrowser.xml" />
|
||||
<tool file="data_source/ucsc_tablebrowser_test.xml" />
|
||||
<tool file="data_source/ucsc_tablebrowser_archaea.xml" />
|
||||
@@ -122,7 +123,6 @@
|
||||
<tool file="visualization/GMAJ.xml" />
|
||||
<tool file="visualization/LAJ.xml" />
|
||||
<tool file="visualization/build_ucsc_custom_track.xml" />
|
||||
<tool file="visualization/build_gbrowse_custom_track.xml" />
|
||||
</section>
|
||||
<section name="Regional Variation" id="regVar">
|
||||
<tool file="regVariation/windowSplitter.xml" />
|
||||
@@ -155,8 +155,8 @@
|
||||
<tool file="taxonomy/poisson2test.xml" />
|
||||
</section>
|
||||
<section name="Solexa tools" id="solexa_tools">
|
||||
<tool file="solexa/fastq_statistics.xml" />
|
||||
<tool file="solexa/lastz_wrapper.xml" />
|
||||
<tool file="sr_mapping/fastq_statistics.xml" />
|
||||
<tool file="sr_mapping/lastz_wrapper.xml" />
|
||||
</section>
|
||||
<!--
|
||||
TODO: uncomment the following EMBOSS section whenever
|
||||
|
||||
@@ -1,17 +1,25 @@
|
||||
<?xml version="1.0"?>
|
||||
<tool name="Perform EpiGRAPH" id="epigraph">
|
||||
<description> Genome analysis and prediction</description>
|
||||
<tool name="Perform genome" id="epigraph">
|
||||
<description> analysis and prediction with EpiGRAPH</description>
|
||||
<redirect_url_params>GENOME=${input1.dbkey} NAME=${input1.name} INFO=${input1.info}</redirect_url_params>
|
||||
<inputs>
|
||||
<param format="bed" name="input1" type="data" label="Send this dataset to EpiGRAPH">
|
||||
<param name="input1" type="data" format="bed" label="Send this dataset to EpiGRAPH" help="Query missing? See TIP below">
|
||||
<security_check group="public" action="access" />
|
||||
<validator type="unspecified_build" />
|
||||
</param>
|
||||
<param name="REDIRECT_URL" type="hidden" value="http://epigraph.mpi-inf.mpg.de/WebGRAPH_Public_Test/faces/DataImport.jsp" />
|
||||
<param name="DATA_URL" type="baseurl" value="/datasets" />
|
||||
<param name="GALAXY_URL" type="baseurl" value="/tool_runner?tool_id=epigraph_import" />
|
||||
<param name="GALAXY_URL" type="baseurl" value="/dataset_import" />
|
||||
</inputs>
|
||||
<outputs/>
|
||||
<help>
|
||||
|
||||
.. class:: infomark
|
||||
|
||||
**TIP:** Data sent to EpiGRAPH must be "publicly accessible" ( the **public** group must have permission to access the data ). Click the pencil icon in the history item to change the dataset's permissions if you are permitted to do so.
|
||||
|
||||
-----
|
||||
|
||||
**What it does**
|
||||
|
||||
This tool sends the selected dataset to EpiGRAPH for in-depth analysis and prediction.
|
||||
|
||||
@@ -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>
|
||||
@@ -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.set_dataset_permissions( newdata.dataset, base_dataset.dataset.groups )
|
||||
app.model.flush()
|
||||
try:
|
||||
copyfile(filepath,newdata.file_name)
|
||||
|
||||
@@ -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.set_dataset_permissions( newdata.dataset, base_dataset.dataset.groups )
|
||||
history.add_dataset( newdata )
|
||||
app.model.flush()
|
||||
try:
|
||||
|
||||
@@ -27,12 +27,12 @@ def exec_after_process(app, inp_data, out_data, param_dict, tool, stdout, stderr
|
||||
fields = line.split("\t")
|
||||
dbkey = fields[1]
|
||||
filepath = fields[2]
|
||||
|
||||
newdata = app.model.HistoryDatasetAssociation( create_dataset = True )
|
||||
newdata.extension = "bed"
|
||||
newdata.name = basic_name + " (" + dbkey + ")"
|
||||
newdata.flush()
|
||||
history.add_dataset( newdata )
|
||||
app.security_agent.set_dataset_permissions( newdata.dataset, output_data.dataset.groups )
|
||||
newdata.flush()
|
||||
history.flush()
|
||||
app.model.flush()
|
||||
|
||||
@@ -90,7 +90,7 @@ def main():
|
||||
|
||||
for ii, line in enumerate( file( tmpfile.name )):
|
||||
if line and not line.startswith( '#' ):
|
||||
line = line.strip()
|
||||
line = line.rstrip( '\r\n' )
|
||||
try:
|
||||
fields = line.split("\t")
|
||||
item = fields[group_col]
|
||||
|
||||
@@ -77,8 +77,12 @@ use_lint = false
|
||||
# NEVER enable this on a public site (even test or QA)
|
||||
use_interactive = true
|
||||
|
||||
# Admin Password
|
||||
admin_pass = galaxy
|
||||
# Admin Users - this should be a comma-separated list of valid Galaxy users
|
||||
#admin_users = user1@bx.psu.edu,user2@bx.psu.edu
|
||||
|
||||
# Files in directories under this directory can be directly imported through
|
||||
# the library admin's "add dataset" tool
|
||||
## library_import_dir = /var/opt/galaxy/import
|
||||
|
||||
# path to sendmail
|
||||
sendmail_path = /usr/sbin/sendmail
|
||||
|
||||