merging from central
@@ -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/
|
||||
|
||||
@@ -140,6 +140,30 @@
|
||||
<datatype extension="wobble" type="galaxy.datatypes.data:Text"/>
|
||||
<datatype extension="wordcount" type="galaxy.datatypes.data:Text"/>
|
||||
<datatype extension="tagseq" type="galaxy.datatypes.data:Text"/>
|
||||
<!-- Start RGenetics Datatypes -->
|
||||
<!-- genome graphs ucsc file - first col is always marker then numeric values to plot -->
|
||||
<datatype extension="gg" type="galaxy.datatypes.genetics:GenomeGraphs"/>
|
||||
<datatype extension="rgenetics" type="galaxy.datatypes.genetics:Rgenetics"/>
|
||||
<!-- linkage format pedigree (separate .map file) -->
|
||||
<datatype extension="lped" type="galaxy.datatypes.genetics:Lped"/>
|
||||
<!-- plink compressed file - has bed extension unfortunately -->
|
||||
<datatype extension="pbed" type="galaxy.datatypes.genetics:Pbed"/>
|
||||
<!-- eigenstrat pedigree input file -->
|
||||
<datatype extension="eigenstratgeno" type="galaxy.datatypes.genetics:Eigenstratgeno"/>
|
||||
<!-- eigenstrat pca output file for adjusted eigenQTL eg -->
|
||||
<datatype extension="eigenstratpca" type="galaxy.datatypes.genetics:Eigenstratpca"/>
|
||||
<!-- fbat/pbat format pedigree (header row of marker names) -->
|
||||
<datatype extension="fped" type="galaxy.datatypes.genetics:Fped"/>
|
||||
<!-- part of linkage format pedigree -->
|
||||
<datatype extension="lmap" type="galaxy.datatypes.genetics:Lmap"/>
|
||||
<!-- phenotype file - fbat format -->
|
||||
<datatype extension="fphe" type="galaxy.datatypes.genetics:Fphe"/>
|
||||
<!-- phenotype file - plink format -->
|
||||
<datatype extension="pphe" type="galaxy.datatypes.genetics:Pphe"/>
|
||||
<datatype extension="snptest" type="galaxy.datatypes.genetics:Snptest"/>
|
||||
<datatype extension="snpmatrix" type="galaxy.datatypes.genetics:SNPMatrix"/>
|
||||
<datatype extension="xls" type="galaxy.datatypes.tabular:Tabular"/>
|
||||
<!-- End RGenetics Datatypes -->
|
||||
</registration>
|
||||
<sniffers>
|
||||
<!--
|
||||
|
||||
@@ -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"""
|
||||
@@ -25,11 +26,13 @@ class UniverseApplication( object ):
|
||||
self.model = galaxy.model.mapping.init( self.config.file_path,
|
||||
db_url,
|
||||
self.config.database_engine_options,
|
||||
create_tables = True )
|
||||
create_tables = self.config.database_create_tables )
|
||||
# Initialize the tools
|
||||
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
|
||||
# Heartbeat and memdump for thread / heap profiling
|
||||
self.heartbeat = None
|
||||
self.memdump = None
|
||||
|
||||
@@ -27,6 +27,7 @@ class Configuration( object ):
|
||||
self.database = resolve_path( kwargs.get( "database_file", "database/universe.d" ), self.root )
|
||||
self.database_connection = kwargs.get( "database_connection", False )
|
||||
self.database_engine_options = get_database_engine_options( kwargs )
|
||||
self.database_create_tables = string_as_bool( kwargs.get( "database_create_tables", "True" ) )
|
||||
# Where dataset files are stored
|
||||
self.file_path = resolve_path( kwargs.get( "file_path", "database/files" ), self.root )
|
||||
self.new_file_path = resolve_path( kwargs.get( "new_file_path", "database/tmp" ), self.root )
|
||||
@@ -38,14 +39,17 @@ class Configuration( object ):
|
||||
self.id_secret = kwargs.get( "id_secret", "USING THE DEFAULT IS NOT SECURE!" )
|
||||
self.use_remote_user = string_as_bool( kwargs.get( "use_remote_user", "False" ) )
|
||||
self.remote_user_maildomain = kwargs.get( "remote_user_maildomain", None )
|
||||
self.require_login = string_as_bool( kwargs.get( "require_login", "False" ) )
|
||||
self.allow_user_creation = string_as_bool( kwargs.get( "allow_user_creation", "True" ) )
|
||||
self.allow_user_deletion = string_as_bool( kwargs.get( "allow_user_deletion", "False" ) )
|
||||
self.template_path = resolve_path( kwargs.get( "template_path", "templates" ), self.root )
|
||||
self.template_cache = resolve_path( kwargs.get( "template_cache_path", "database/compiled_templates" ), self.root )
|
||||
self.local_job_queue_workers = int( kwargs.get( "local_job_queue_workers", "5" ) )
|
||||
self.cluster_job_queue_workers = int( kwargs.get( "cluster_job_queue_workers", "5" ) )
|
||||
self.cluster_job_queue_workers = int( kwargs.get( "cluster_job_queue_workers", "3" ) )
|
||||
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 )
|
||||
@@ -56,10 +60,10 @@ class Configuration( object ):
|
||||
self.pbs_dataset_server = kwargs.get('pbs_dataset_server', "" )
|
||||
self.pbs_dataset_path = kwargs.get('pbs_dataset_path', "" )
|
||||
self.pbs_stage_path = kwargs.get('pbs_stage_path', "" )
|
||||
self.use_heartbeat = string_as_bool( kwargs.get( 'use_heartbeat', False ) )
|
||||
self.use_memdump = string_as_bool( kwargs.get( 'use_memdump', False ) )
|
||||
self.log_memory_usage = string_as_bool( kwargs.get( 'log_memory_usage', False ) )
|
||||
self.log_events = string_as_bool( kwargs.get( 'log_events', False ) )
|
||||
self.use_heartbeat = string_as_bool( kwargs.get( 'use_heartbeat', 'False' ) )
|
||||
self.use_memdump = string_as_bool( kwargs.get( 'use_memdump', 'False' ) )
|
||||
self.log_memory_usage = string_as_bool( kwargs.get( 'log_memory_usage', 'False' ) )
|
||||
self.log_events = string_as_bool( kwargs.get( 'log_events', 'False' ) )
|
||||
self.ucsc_display_sites = kwargs.get( 'ucsc_display_sites', "main,test,archaea" ).lower().split(",")
|
||||
self.gbrowse_display_sites = kwargs.get( 'gbrowse_display_sites', "wormbase,flybase,elegans" ).lower().split(",")
|
||||
self.brand = kwargs.get( 'brand', None )
|
||||
@@ -67,6 +71,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 )
|
||||
# Configuration options for taking advantage of nginx features
|
||||
self.nginx_x_accel_redirect_base = kwargs.get( 'nginx_x_accel_redirect_base', False )
|
||||
self.nginx_upload_location = kwargs.get( 'nginx_upload_store', False )
|
||||
|
||||
@@ -45,6 +45,9 @@ class Data( object ):
|
||||
"""Stores the set of display applications, and viewing methods, supported by this datatype """
|
||||
supported_display_apps = {}
|
||||
|
||||
"""If False, the peek is regenerated whenever a dataset of this type is copied"""
|
||||
copy_safe_peek = True
|
||||
|
||||
def __init__(self, **kwd):
|
||||
"""Initialize the datatype"""
|
||||
object.__init__(self, **kwd)
|
||||
@@ -180,12 +183,17 @@ class Data( object ):
|
||||
return "This display type (%s) is not implemented for this datatype (%s)." % ( type, dataset.ext)
|
||||
|
||||
def get_display_links(self, dataset, type, app, base_url, **kwd):
|
||||
"""Returns a list of tuples of (name, link) for a particular display type """
|
||||
try:
|
||||
if type in self.get_display_types():
|
||||
return getattr (self, self.supported_display_apps[type]['links_function']) (dataset, type, app, base_url, **kwd)
|
||||
except:
|
||||
log.exception('Function %s is referred to in datatype %s for generating links for type %s, but is not accessible' % (self.supported_display_apps[type]['links_function'], self.__class__.__name__, type) )
|
||||
"""
|
||||
Returns a list of tuples of (name, link) for a particular display type
|
||||
as long as the dataset is not associated with a role restricting its access.
|
||||
We determine this by sending None as the user to the allow_action method.
|
||||
"""
|
||||
if app.security_agent.allow_action( None, dataset.permitted_actions.DATASET_ACCESS, dataset=dataset ):
|
||||
try:
|
||||
if type in self.get_display_types():
|
||||
return getattr (self, self.supported_display_apps[type]['links_function']) (dataset, type, app, base_url, **kwd)
|
||||
except:
|
||||
log.exception('Function %s is referred to in datatype %s for generating links for type %s, but is not accessible' % (self.supported_display_apps[type]['links_function'], self.__class__.__name__, type) )
|
||||
return []
|
||||
|
||||
def get_converter_types(self, original_dataset, datatypes_registry):
|
||||
|
||||
@@ -132,10 +132,10 @@ class SNPMatrix(Rgenetics):
|
||||
def set_peek( self, dataset ):
|
||||
dataset.peek = "Binary RGenetics file"
|
||||
dataset.blurb = data.nice_size( dataset.get_size() )
|
||||
def sniff( self, filename ):
|
||||
"""
|
||||
"""
|
||||
return True
|
||||
#def sniff( self, filename ):
|
||||
# """
|
||||
# """
|
||||
# return True
|
||||
|
||||
class Lped(Rgenetics):
|
||||
"""fake class to distinguish different species of Rgenetics data collections
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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__ )
|
||||
@@ -31,15 +32,25 @@ class User( object ):
|
||||
self.email = email
|
||||
self.password = password
|
||||
self.external = False
|
||||
self.deleted = False
|
||||
self.purged = False
|
||||
# Relationships
|
||||
self.histories = []
|
||||
|
||||
def set_password_cleartext( self, cleartext ):
|
||||
"""Set 'self.password' to the digest of 'cleartext'."""
|
||||
self.password = sha.new( cleartext ).hexdigest()
|
||||
def check_password( self, cleartext ):
|
||||
"""Check if 'cleartext' matches 'self.password' when hashed."""
|
||||
return self.password == sha.new( cleartext ).hexdigest()
|
||||
|
||||
def all_roles( self ):
|
||||
roles = [ ura.role for ura in self.roles ]
|
||||
for group in [ uga.group for uga in self.groups ]:
|
||||
for role in [ gra.role for gra in group.roles ]:
|
||||
if role not in roles:
|
||||
roles.append( role )
|
||||
return roles
|
||||
|
||||
class Job( object ):
|
||||
"""
|
||||
A job represents a request to run a tool given input datasets, tool
|
||||
@@ -130,195 +141,16 @@ class JobToOutputDatasetAssociation( object ):
|
||||
self.name = name
|
||||
self.dataset = dataset
|
||||
|
||||
class HistoryDatasetAssociation( object ):
|
||||
def __init__( self, id=None, hid=None, name=None, info=None, blurb=None, peek=None, extension=None,
|
||||
dbkey=None, metadata=None, history=None, dataset=None, deleted=False, designation=None,
|
||||
parent_id=None, copied_from_history_dataset_association = None, validation_errors=None, visible=True, create_dataset = False ):
|
||||
self.name = name or "Unnamed dataset"
|
||||
self.id = id
|
||||
self.hid = hid
|
||||
self.info = info
|
||||
self.blurb = blurb
|
||||
self.peek = peek
|
||||
self.extension = extension
|
||||
self.designation = designation
|
||||
self.metadata = metadata or dict()
|
||||
if dbkey: #dbkey is stored in metadata, only set if non-zero, or else we could clobber one supplied by input 'metadata'
|
||||
self.dbkey = dbkey
|
||||
self.deleted = deleted
|
||||
self.visible = visible
|
||||
# Relationships
|
||||
self.history = history
|
||||
if not dataset and create_dataset:
|
||||
dataset = Dataset()
|
||||
dataset.flush()
|
||||
self.dataset = dataset
|
||||
self.parent_id = parent_id
|
||||
self.validation_errors = validation_errors
|
||||
self.copied_from_history_dataset_association = copied_from_history_dataset_association
|
||||
|
||||
@property
|
||||
def ext( self ):
|
||||
return self.extension
|
||||
|
||||
@property
|
||||
def states( self ):
|
||||
return self.dataset.states
|
||||
|
||||
def get_dataset_state( self ):
|
||||
return self.dataset.state
|
||||
def set_dataset_state ( self, state ):
|
||||
self.dataset.state = state
|
||||
self.dataset.flush() #flush here, because hda.flush() won't flush the Dataset object
|
||||
state = property( get_dataset_state, set_dataset_state )
|
||||
|
||||
def get_file_name( self ):
|
||||
return self.dataset.get_file_name()
|
||||
|
||||
def set_file_name (self, filename):
|
||||
return self.dataset.set_file_name( filename )
|
||||
|
||||
file_name = property( get_file_name, set_file_name )
|
||||
|
||||
@property
|
||||
def extra_files_path( self ):
|
||||
return self.dataset.extra_files_path
|
||||
|
||||
@property
|
||||
def datatype( self ):
|
||||
return datatypes_registry.get_datatype_by_extension( self.extension )
|
||||
|
||||
def get_metadata( self ):
|
||||
if not hasattr( self, '_metadata_collection' ) or self._metadata_collection.parent != self: #using weakref to store parent (to prevent circ ref), does a Session.clear() cause parent to be invalidated, while still copying over this non-database attribute?
|
||||
self._metadata_collection = MetadataCollection( self )
|
||||
return self._metadata_collection
|
||||
def set_metadata( self, bunch ):
|
||||
# Needs to accept a MetadataCollection, a bunch, or a dict
|
||||
self._metadata = self.metadata.make_dict_copy( bunch )
|
||||
metadata = property( get_metadata, set_metadata )
|
||||
|
||||
"""
|
||||
This provide backwards compatibility with using the old dbkey
|
||||
field in the database. That field now maps to "old_dbkey" (see mapping.py).
|
||||
"""
|
||||
def get_dbkey( self ):
|
||||
dbkey = self.metadata.dbkey
|
||||
if not isinstance(dbkey, list): dbkey = [dbkey]
|
||||
#if dbkey in [["?"], [None], []]: dbkey = [self.old_dbkey]
|
||||
if dbkey in [[None], []]: return "?"
|
||||
return dbkey[0]
|
||||
def set_dbkey( self, value ):
|
||||
if "dbkey" in self.datatype.metadata_spec:
|
||||
if not isinstance(value, list):
|
||||
self.metadata.dbkey = [value]
|
||||
else:
|
||||
self.metadata.dbkey = value
|
||||
#if isinstance(value, list):
|
||||
# self.old_dbkey = value[0]
|
||||
#else:
|
||||
# self.old_dbkey = value
|
||||
dbkey = property( get_dbkey, set_dbkey )
|
||||
|
||||
def change_datatype( self, new_ext ):
|
||||
self.clear_associated_files()
|
||||
datatypes_registry.change_datatype( self, new_ext )
|
||||
def get_size( self ):
|
||||
"""Returns the size of the data on disk"""
|
||||
return self.dataset.get_size()
|
||||
def set_size( self ):
|
||||
"""Returns the size of the data on disk"""
|
||||
return self.dataset.set_size()
|
||||
def has_data( self ):
|
||||
"""Detects whether there is any data"""
|
||||
return self.dataset.has_data()
|
||||
def get_raw_data( self ):
|
||||
"""Returns the full data. To stream it open the file_name and read/write as needed"""
|
||||
return self.datatype.get_raw_data( self )
|
||||
def write_from_stream( self, stream ):
|
||||
"""Writes data from a stream"""
|
||||
self.datatype.write_from_stream(self, stream)
|
||||
def set_raw_data( self, data ):
|
||||
"""Saves the data on the disc"""
|
||||
self.datatype.set_raw_data(self, data)
|
||||
def get_mime( self ):
|
||||
"""Returns the mime type of the data"""
|
||||
return datatypes_registry.get_mimetype_by_extension( self.extension.lower() )
|
||||
def set_peek( self ):
|
||||
return self.datatype.set_peek( self )
|
||||
def init_meta( self, copy_from=None ):
|
||||
return self.datatype.init_meta( self, copy_from=copy_from )
|
||||
def set_meta( self, **kwd ):
|
||||
self.clear_associated_files( metadata_safe = True )
|
||||
return self.datatype.set_meta( self, **kwd )
|
||||
def set_readonly_meta( self, **kwd ):
|
||||
return self.datatype.set_readonly_meta( self, **kwd )
|
||||
def missing_meta( self, **kwd ):
|
||||
return self.datatype.missing_meta( self, **kwd )
|
||||
def as_display_type( self, type, **kwd ):
|
||||
return self.datatype.as_display_type( self, type, **kwd )
|
||||
def display_peek( self ):
|
||||
return self.datatype.display_peek( self )
|
||||
def display_name( self ):
|
||||
return self.datatype.display_name( self )
|
||||
def display_info( self ):
|
||||
return self.datatype.display_info( self )
|
||||
def get_converted_files_by_type( self, file_type ):
|
||||
valid = []
|
||||
for assoc in self.implicitly_converted_datasets:
|
||||
if not assoc.deleted and assoc.type == file_type:
|
||||
valid.append( assoc.dataset )
|
||||
return valid
|
||||
def clear_associated_files( self, metadata_safe = False, purge = False ):
|
||||
#metadata_safe = True means to only clear when assoc.metadata_safe == False
|
||||
for assoc in self.implicitly_converted_datasets:
|
||||
if not metadata_safe or not assoc.metadata_safe:
|
||||
assoc.clear( purge = purge )
|
||||
def get_child_by_designation(self, designation):
|
||||
for child in self.children:
|
||||
if child.designation == designation:
|
||||
return child
|
||||
return None
|
||||
|
||||
def get_converter_types(self):
|
||||
return self.datatype.get_converter_types( self, datatypes_registry)
|
||||
|
||||
def find_conversion_destination( self, accepted_formats, **kwd ):
|
||||
"""Returns ( target_ext, exisiting converted dataset )"""
|
||||
return self.datatype.find_conversion_destination( self, accepted_formats, datatypes_registry, **kwd )
|
||||
|
||||
def copy( self, copy_children = False, parent_id = None ):
|
||||
des = HistoryDatasetAssociation( hid=self.hid, name=self.name, info=self.info, blurb=self.blurb, peek=self.peek, extension=self.extension, dbkey=self.dbkey, dataset = self.dataset, visible=self.visible, deleted=self.deleted, parent_id=parent_id, copied_from_history_dataset_association = self )
|
||||
des.flush()
|
||||
des.set_size()
|
||||
des.metadata = self.metadata #need to set after flushed, as MetadataFiles require dataset.id
|
||||
if copy_children:
|
||||
for child in self.children:
|
||||
child_copy = child.copy( copy_children = copy_children, parent_id = des.id )
|
||||
des.set_peek() #in some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs
|
||||
des.flush()
|
||||
return des
|
||||
|
||||
def add_validation_error( self, validation_error ):
|
||||
self.validation_errors.append( validation_error )
|
||||
|
||||
def extend_validation_errors( self, validation_errors ):
|
||||
self.validation_errors.extend(validation_errors)
|
||||
|
||||
def mark_deleted( self, include_children=True ):
|
||||
self.deleted = True
|
||||
if include_children:
|
||||
for child in self.children:
|
||||
child.mark_deleted()
|
||||
|
||||
def mark_undeleted( self, include_children=True ):
|
||||
class Group( object ):
|
||||
permitted_actions = galaxy.security.get_permitted_actions( 'GROUP' )
|
||||
def __init__( self, name = None ):
|
||||
self.name = name
|
||||
self.deleted = False
|
||||
if include_children:
|
||||
for child in self.children:
|
||||
child.mark_undeleted()
|
||||
def undeletable( self ):
|
||||
if self.purged:
|
||||
return False
|
||||
return True
|
||||
|
||||
class UserGroupAssociation( object ):
|
||||
def __init__( self, user, group ):
|
||||
self.user = user
|
||||
self.group = group
|
||||
|
||||
class History( object ):
|
||||
def __init__( self, id=None, name=None, user=None ):
|
||||
@@ -393,6 +225,49 @@ class History( object ):
|
||||
# self.history = history
|
||||
# self.datasets = []
|
||||
|
||||
class UserRoleAssociation( object ):
|
||||
def __init__( self, user, role ):
|
||||
self.user = user
|
||||
self.role = role
|
||||
|
||||
class GroupRoleAssociation( object ):
|
||||
def __init__( self, group, role ):
|
||||
self.group = group
|
||||
self.role = role
|
||||
|
||||
class Role( object ):
|
||||
private_id = None
|
||||
types = Bunch(
|
||||
PRIVATE = 'private',
|
||||
SYSTEM = 'system',
|
||||
USER = 'user',
|
||||
ADMIN = 'admin',
|
||||
SHARING = 'sharing'
|
||||
)
|
||||
def __init__( self, name="", description="", type="system", deleted=False ):
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.type = type
|
||||
self.deleted = deleted
|
||||
|
||||
class ActionDatasetRoleAssociation( object ):
|
||||
def __init__( self, action, dataset, role ):
|
||||
self.action = action
|
||||
self.dataset = dataset
|
||||
self.role = role
|
||||
|
||||
class DefaultUserPermissions( object ):
|
||||
def __init__( self, user, action, role ):
|
||||
self.user = user
|
||||
self.action = action
|
||||
self.role = role
|
||||
|
||||
class DefaultHistoryPermissions( object ):
|
||||
def __init__( self, history, action, role ):
|
||||
self.history = history
|
||||
self.action = action
|
||||
self.role = role
|
||||
|
||||
class Dataset( object ):
|
||||
states = Bunch( NEW = 'new',
|
||||
QUEUED = 'queued',
|
||||
@@ -401,6 +276,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 ):
|
||||
@@ -486,8 +362,402 @@ 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.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
|
||||
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' ) or self._metadata_collection.parent != self: #using weakref to store parent (to prevent circ ref), does a Session.clear() cause parent to be invalidated, while still copying over this non-database attribute?
|
||||
self._metadata_collection = MetadataCollection( self )
|
||||
return self._metadata_collection
|
||||
def set_metadata( self, bunch ):
|
||||
# Needs to accept a MetadataCollection, a bunch, or a dict
|
||||
self._metadata = self.metadata.make_dict_copy( bunch )
|
||||
metadata = property( get_metadata, set_metadata )
|
||||
# This provide backwards compatibility with using the old dbkey
|
||||
# field in the database. That field now maps to "old_dbkey" (see mapping.py).
|
||||
def get_dbkey( self ):
|
||||
dbkey = self.metadata.dbkey
|
||||
if not isinstance(dbkey, list): dbkey = [dbkey]
|
||||
#if dbkey in [["?"], [None], []]: dbkey = [self.old_dbkey]
|
||||
if dbkey in [[None], []]: return "?"
|
||||
return dbkey[0]
|
||||
def set_dbkey( self, value ):
|
||||
if "dbkey" in self.datatype.metadata_spec:
|
||||
if not isinstance(value, list):
|
||||
self.metadata.dbkey = [value]
|
||||
else:
|
||||
self.metadata.dbkey = value
|
||||
#if isinstance(value, list):
|
||||
# self.old_dbkey = value[0]
|
||||
#else:
|
||||
# self.old_dbkey = value
|
||||
dbkey = property( get_dbkey, set_dbkey )
|
||||
def change_datatype( self, new_ext ):
|
||||
self.clear_associated_files()
|
||||
datatypes_registry.change_datatype( self, new_ext )
|
||||
def get_size( self ):
|
||||
"""Returns the size of the data on disk"""
|
||||
return self.dataset.get_size()
|
||||
def set_size( self ):
|
||||
"""Returns the size of the data on disk"""
|
||||
return self.dataset.set_size()
|
||||
def has_data( self ):
|
||||
"""Detects whether there is any data"""
|
||||
return self.dataset.has_data()
|
||||
def get_raw_data( self ):
|
||||
"""Returns the full data. To stream it open the file_name and read/write as needed"""
|
||||
return self.datatype.get_raw_data( self )
|
||||
def write_from_stream( self, stream ):
|
||||
"""Writes data from a stream"""
|
||||
self.datatype.write_from_stream(self, stream)
|
||||
def set_raw_data( self, data ):
|
||||
"""Saves the data on the disc"""
|
||||
self.datatype.set_raw_data(self, data)
|
||||
def get_mime( self ):
|
||||
"""Returns the mime type of the data"""
|
||||
return datatypes_registry.get_mimetype_by_extension( self.extension.lower() )
|
||||
def set_peek( self ):
|
||||
return self.datatype.set_peek( self )
|
||||
def init_meta( self, copy_from=None ):
|
||||
return self.datatype.init_meta( self, copy_from=copy_from )
|
||||
def set_meta( self, **kwd ):
|
||||
self.clear_associated_files( metadata_safe = True )
|
||||
return self.datatype.set_meta( self, **kwd )
|
||||
def set_readonly_meta( self, **kwd ):
|
||||
return self.datatype.set_readonly_meta( self, **kwd )
|
||||
def missing_meta( self, **kwd ):
|
||||
return self.datatype.missing_meta( self, **kwd )
|
||||
def as_display_type( self, type, **kwd ):
|
||||
return self.datatype.as_display_type( self, type, **kwd )
|
||||
def display_peek( self ):
|
||||
return self.datatype.display_peek( self )
|
||||
def display_name( self ):
|
||||
return self.datatype.display_name( self )
|
||||
def display_info( self ):
|
||||
return self.datatype.display_info( self )
|
||||
def get_converted_files_by_type( self, file_type ):
|
||||
valid = []
|
||||
for assoc in self.implicitly_converted_datasets:
|
||||
if not assoc.deleted and assoc.type == file_type:
|
||||
valid.append( assoc.dataset )
|
||||
return valid
|
||||
def clear_associated_files( self, metadata_safe = False, purge = False ):
|
||||
raise 'Unimplemented'
|
||||
def get_child_by_designation(self, designation):
|
||||
for child in self.children:
|
||||
if child.designation == designation:
|
||||
return child
|
||||
return None
|
||||
def get_converter_types(self):
|
||||
return self.datatype.get_converter_types( self, datatypes_registry)
|
||||
def find_conversion_destination( self, accepted_formats, **kwd ):
|
||||
"""Returns ( target_ext, exisiting converted dataset )"""
|
||||
return self.datatype.find_conversion_destination( self, accepted_formats, datatypes_registry, **kwd )
|
||||
def add_validation_error( self, validation_error ):
|
||||
self.validation_errors.append( validation_error )
|
||||
def extend_validation_errors( self, validation_errors ):
|
||||
self.validation_errors.extend(validation_errors)
|
||||
def mark_deleted( self, include_children=True ):
|
||||
self.deleted = True
|
||||
if include_children:
|
||||
for child in self.children:
|
||||
child.mark_deleted()
|
||||
def mark_undeleted( self, include_children=True ):
|
||||
self.deleted = False
|
||||
if include_children:
|
||||
for child in self.children:
|
||||
child.mark_undeleted()
|
||||
def undeletable( self ):
|
||||
if self.purged:
|
||||
return False
|
||||
return True
|
||||
|
||||
class HistoryDatasetAssociation( DatasetInstance ):
|
||||
def __init__( self,
|
||||
hid = None,
|
||||
history = None,
|
||||
copied_from_history_dataset_association = None,
|
||||
copied_from_library_folder_dataset_association = None,
|
||||
**kwd ):
|
||||
DatasetInstance.__init__( self, **kwd )
|
||||
self.hid = hid
|
||||
# Relationships
|
||||
self.history = history
|
||||
self.copied_from_history_dataset_association = copied_from_history_dataset_association
|
||||
self.copied_from_library_folder_dataset_association = copied_from_library_folder_dataset_association
|
||||
def copy( self, copy_children = False, parent_id = None, target_history = None ):
|
||||
des = HistoryDatasetAssociation( hid=self.hid,
|
||||
name=self.name,
|
||||
info=self.info,
|
||||
blurb=self.blurb,
|
||||
peek=self.peek,
|
||||
extension=self.extension,
|
||||
dbkey=self.dbkey,
|
||||
dataset = self.dataset,
|
||||
visible=self.visible,
|
||||
deleted=self.deleted,
|
||||
parent_id=parent_id,
|
||||
copied_from_history_dataset_association=self,
|
||||
history = target_history )
|
||||
des.flush()
|
||||
des.set_size()
|
||||
des.metadata = self.metadata #need to set after flushed, as MetadataFiles require dataset.id
|
||||
if copy_children:
|
||||
for child in self.children:
|
||||
child_copy = child.copy( copy_children = copy_children, parent_id = des.id )
|
||||
if not self.datatype.copy_safe_peek:
|
||||
des.set_peek() #in some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs
|
||||
des.flush()
|
||||
return des
|
||||
|
||||
def to_library_dataset_folder_association( self, parent_id = None, target_folder = None ):
|
||||
|
||||
des = LibraryFolderDatasetAssociation( name=self.name,
|
||||
info=self.info,
|
||||
blurb=self.blurb,
|
||||
peek=self.peek,
|
||||
extension=self.extension,
|
||||
dbkey=self.dbkey,
|
||||
dataset = self.dataset,
|
||||
visible=self.visible,
|
||||
deleted=self.deleted,
|
||||
parent_id=parent_id,
|
||||
copied_from_history_dataset_association = self,
|
||||
folder = target_folder )
|
||||
des.flush()
|
||||
des.metadata = self.metadata #need to set after flushed, as MetadataFiles require dataset.id
|
||||
if target_folder:
|
||||
target_folder.add_dataset( des )
|
||||
for child in self.children:
|
||||
child_copy = child.to_library_dataset_folder_association( parent_id = des.id )
|
||||
if not self.datatype.copy_safe_peek:
|
||||
des.set_peek() #in some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs
|
||||
des.flush()
|
||||
return des
|
||||
|
||||
def clear_associated_files( self, metadata_safe = False, purge = False ):
|
||||
#metadata_safe = True means to only clear when assoc.metadata_safe == False
|
||||
for assoc in self.implicitly_converted_datasets:
|
||||
if not metadata_safe or not assoc.metadata_safe:
|
||||
assoc.clear( purge = purge )
|
||||
|
||||
class History( object ):
|
||||
def __init__( self, id=None, name=None, user=None ):
|
||||
self.id = id
|
||||
self.name = name or "Unnamed history"
|
||||
self.deleted = False
|
||||
self.purged = False
|
||||
self.genome_build = None
|
||||
# Relationships
|
||||
self.user = user
|
||||
self.datasets = []
|
||||
self.galaxy_sessions = []
|
||||
def _next_hid( self ):
|
||||
# TODO: override this with something in the database that ensures
|
||||
# better integrity
|
||||
if len( self.datasets ) == 0:
|
||||
return 1
|
||||
else:
|
||||
last_hid = 0
|
||||
for dataset in self.datasets:
|
||||
if dataset.hid > last_hid:
|
||||
last_hid = dataset.hid
|
||||
return last_hid + 1
|
||||
def add_galaxy_session( self, galaxy_session, association=None ):
|
||||
if association is None:
|
||||
self.galaxy_sessions.append( GalaxySessionToHistoryAssociation( galaxy_session, self ) )
|
||||
else:
|
||||
self.galaxy_sessions.append( association )
|
||||
def add_dataset( self, dataset, parent_id=None, genome_build=None, set_hid = True ):
|
||||
if isinstance( dataset, Dataset ):
|
||||
dataset = HistoryDatasetAssociation( dataset = dataset )
|
||||
dataset.flush()
|
||||
elif not isinstance( dataset, HistoryDatasetAssociation ):
|
||||
raise TypeError, "You can only add Dataset and HistoryDatasetAssociation instances to a history."
|
||||
if parent_id:
|
||||
for data in self.datasets:
|
||||
if data.id == parent_id:
|
||||
dataset.hid = data.hid
|
||||
break
|
||||
else:
|
||||
if set_hid: dataset.hid = self._next_hid()
|
||||
else:
|
||||
if set_hid: dataset.hid = self._next_hid()
|
||||
dataset.history = self
|
||||
if genome_build not in [None, '?']:
|
||||
self.genome_build = genome_build
|
||||
self.datasets.append( dataset )
|
||||
def copy( self, target_user = None ):
|
||||
if not target_user:
|
||||
target_user = self.user
|
||||
des = History( user = target_user )
|
||||
des.flush()
|
||||
des.name = self.name
|
||||
for data in self.datasets:
|
||||
new_data = data.copy( copy_children = True, target_history = des )
|
||||
des.add_dataset( new_data, set_hid = False )
|
||||
new_data.flush()
|
||||
des.hid_counter = self.hid_counter
|
||||
des.flush()
|
||||
return des
|
||||
|
||||
class Library( object ):
|
||||
def __init__( self, name = None, description = None, root_folder = None ):
|
||||
self.name = name or "Unnamed library"
|
||||
self.description = description
|
||||
self.root_folder = root_folder
|
||||
|
||||
class LibraryFolder( object ):
|
||||
def __init__( self, name = None, description = None, item_count = 0, order_id = None ):
|
||||
self.name = name or "Unnamed folder"
|
||||
self.description = description
|
||||
self.item_count = item_count
|
||||
self.order_id = order_id
|
||||
self.genome_build = None
|
||||
def add_dataset( self, dataset, genome_build=None ):
|
||||
dataset.folder_id = self.id
|
||||
dataset.order_id = self.item_count
|
||||
self.item_count += 1
|
||||
if genome_build not in [None, '?']:
|
||||
self.genome_build = genome_build
|
||||
def add_folder( self, folder ):
|
||||
folder.parent_id = self.id
|
||||
folder.order_id = self.item_count
|
||||
self.item_count += 1
|
||||
|
||||
@property
|
||||
def active_components( self ):
|
||||
return list( self.active_folders ) + list( self.active_datasets )
|
||||
|
||||
class LibraryFolderDatasetAssociation( DatasetInstance ):
|
||||
def __init__( self,
|
||||
folder = None,
|
||||
order_id = None,
|
||||
copied_from_history_dataset_association = None,
|
||||
copied_from_library_folder_dataset_association = None,
|
||||
**kwd ):
|
||||
DatasetInstance.__init__( self, **kwd )
|
||||
self.folder = folder
|
||||
self.order_id = order_id
|
||||
self.copied_from_history_dataset_association = copied_from_history_dataset_association
|
||||
self.copied_from_library_folder_dataset_association = copied_from_library_folder_dataset_association
|
||||
def to_history_dataset_association( self, parent_id = None, target_history = None ):
|
||||
if target_history:
|
||||
hid = target_history._next_hid()
|
||||
else:
|
||||
hid = None
|
||||
des = HistoryDatasetAssociation( name=self.name,
|
||||
info=self.info,
|
||||
blurb=self.blurb,
|
||||
peek=self.peek,
|
||||
extension=self.extension,
|
||||
dbkey=self.dbkey,
|
||||
dataset = self.dataset,
|
||||
visible=self.visible,
|
||||
deleted=self.deleted,
|
||||
parent_id=parent_id,
|
||||
copied_from_library_folder_dataset_association = self,
|
||||
history = target_history,
|
||||
hid = hid )
|
||||
des.flush()
|
||||
des.metadata = self.metadata #need to set after flushed, as MetadataFiles require dataset.id
|
||||
for child in self.children:
|
||||
child_copy = child.to_history_dataset_association( parent_id = des.id )
|
||||
if not self.datatype.copy_safe_peek:
|
||||
des.set_peek() #in some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs
|
||||
des.flush()
|
||||
return des
|
||||
def copy( self, copy_children = False, parent_id = None, target_folder = None ):
|
||||
des = LibraryFolderDatasetAssociation( name=self.name,
|
||||
info=self.info,
|
||||
blurb=self.blurb,
|
||||
peek=self.peek,
|
||||
extension=self.extension,
|
||||
dbkey=self.dbkey,
|
||||
dataset = self.dataset,
|
||||
visible=self.visible,
|
||||
deleted=self.deleted,
|
||||
parent_id=parent_id,
|
||||
copied_from_library_folder_dataset_association = self,
|
||||
folder = target_folder )
|
||||
des.flush()
|
||||
des.metadata = self.metadata #need to set after flushed, as MetadataFiles require dataset.id
|
||||
if copy_children:
|
||||
for child in self.children:
|
||||
child_copy = child.copy( copy_children = copy_children, parent_id = des.id )
|
||||
if not self.datatype.copy_safe_peek:
|
||||
des.set_peek() #in some instances peek relies on dataset_id, i.e. gmaj.zip for viewing MAFs
|
||||
des.flush()
|
||||
return des
|
||||
def clear_associated_files( self, metadata_safe = False, purge = False ):
|
||||
return
|
||||
|
||||
class LibraryTag( object ):
|
||||
def __init__( self, tag ):
|
||||
self.tag = tag
|
||||
|
||||
class LibraryTagFolderAssociation( object ):
|
||||
def __init__( self, tag, folder ):
|
||||
self.tag = tag
|
||||
self.folder = folder
|
||||
|
||||
class LibraryTagDatasetAssociation( object ):
|
||||
def __init__( self, tag, dataset ):
|
||||
self.tag = tag
|
||||
self.dataset = dataset
|
||||
|
||||
# class Query( object ):
|
||||
# def __init__( self, name=None, state=None, tool_parameters=None, history=None ):
|
||||
# self.name = name or "Unnamed query"
|
||||
# self.state = state
|
||||
# self.tool_parameters = tool_parameters
|
||||
# # Relationships
|
||||
# self.history = history
|
||||
# self.datasets = []
|
||||
|
||||
|
||||
class ValidationError( object ):
|
||||
def __init__( self, message=None, err_type=None, attributes=None ):
|
||||
@@ -529,7 +799,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
|
||||
@@ -599,7 +878,10 @@ class StoredWorkflowMenuEntry( object ):
|
||||
|
||||
class MetadataFile( object ):
|
||||
def __init__( self, dataset = None, name = None ):
|
||||
self.dataset = dataset
|
||||
if isinstance( dataset, HistoryDatasetAssociation ):
|
||||
self.history_dataset = dataset
|
||||
elif isinstance( dataset, LibraryFolderDatasetAssociation ):
|
||||
self.library_dataset = dataset
|
||||
self.name = name
|
||||
@property
|
||||
def file_name( self ):
|
||||
@@ -629,3 +911,4 @@ def directory_hash_id( id ):
|
||||
padded = padded[:-3]
|
||||
# Break into chunks of three
|
||||
return [ padded[i*3:(i+1)*3] for i in range( len( padded ) // 3 ) ]
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ 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 = MetaData()
|
||||
context = Session = scoped_session( sessionmaker( autoflush=False, transactional=False ) )
|
||||
@@ -42,7 +43,9 @@ User.table = Table( "galaxy_user", metadata,
|
||||
Column( "update_time", DateTime, default=now, onupdate=now ),
|
||||
Column( "email", TrimmedString( 255 ), nullable=False ),
|
||||
Column( "password", TrimmedString( 40 ), nullable=False ),
|
||||
Column( "external", Boolean, default=False ) )
|
||||
Column( "external", Boolean, default=False ),
|
||||
Column( "deleted", Boolean, index=True, default=False ),
|
||||
Column( "purged", Boolean, index=True, default=False ) )
|
||||
|
||||
History.table = Table( "history", metadata,
|
||||
Column( "id", Integer, primary_key=True),
|
||||
@@ -62,7 +65,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 ),
|
||||
@@ -70,6 +72,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 ) ),
|
||||
@@ -111,6 +114,126 @@ 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", String( 255 ), index=True, unique=True ),
|
||||
Column( "deleted", Boolean, index=True, default=False ) )
|
||||
|
||||
UserGroupAssociation.table = Table( "user_group_association", metadata,
|
||||
Column( "id", Integer, primary_key=True ),
|
||||
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ),
|
||||
Column( "group_id", Integer, ForeignKey( "galaxy_group.id" ), index=True ),
|
||||
Column( "create_time", DateTime, default=now ),
|
||||
Column( "update_time", DateTime, default=now, onupdate=now ) )
|
||||
|
||||
UserRoleAssociation.table = Table( "user_role_association", metadata,
|
||||
Column( "id", Integer, primary_key=True ),
|
||||
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ),
|
||||
Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ),
|
||||
Column( "create_time", DateTime, default=now ),
|
||||
Column( "update_time", DateTime, default=now, onupdate=now ) )
|
||||
|
||||
GroupRoleAssociation.table = Table( "group_role_association", metadata,
|
||||
Column( "id", Integer, primary_key=True ),
|
||||
Column( "group_id", Integer, ForeignKey( "galaxy_group.id" ), index=True ),
|
||||
Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ),
|
||||
Column( "create_time", DateTime, default=now ),
|
||||
Column( "update_time", DateTime, default=now, onupdate=now ) )
|
||||
|
||||
Role.table = Table( "role", metadata,
|
||||
Column( "id", Integer, primary_key=True ),
|
||||
Column( "create_time", DateTime, default=now ),
|
||||
Column( "update_time", DateTime, default=now, onupdate=now ),
|
||||
Column( "name", String( 255 ), index=True, unique=True ),
|
||||
Column( "description", TEXT ),
|
||||
Column( "type", String( 40 ), index=True ),
|
||||
Column( "deleted", Boolean, index=True, default=False ) )
|
||||
|
||||
ActionDatasetRoleAssociation.table = Table( "action_dataset_role_association", metadata,
|
||||
Column( "id", Integer, primary_key=True ),
|
||||
Column( "create_time", DateTime, default=now ),
|
||||
Column( "update_time", DateTime, default=now, onupdate=now ),
|
||||
Column( "action", TEXT ),
|
||||
Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ),
|
||||
Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) )
|
||||
|
||||
DefaultUserPermissions.table = Table( "default_user_permissions", metadata,
|
||||
Column( "id", Integer, primary_key=True ),
|
||||
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ),
|
||||
Column( "action", TEXT ),
|
||||
Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) )
|
||||
|
||||
DefaultHistoryPermissions.table = Table( "default_history_permissions", metadata,
|
||||
Column( "id", Integer, primary_key=True ),
|
||||
Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ),
|
||||
Column( "action", TEXT ),
|
||||
Column( "role_id", Integer, ForeignKey( "role.id" ), index=True ) )
|
||||
|
||||
LibraryFolderDatasetAssociation.table = Table( "library_folder_dataset_association", metadata,
|
||||
Column( "id", Integer, primary_key=True ),
|
||||
Column( "dataset_id", Integer, ForeignKey( "dataset.id" ), index=True ),
|
||||
Column( "folder_id", Integer, ForeignKey( "library_folder.id" ), index=True ),
|
||||
Column( "order_id", Integer ),
|
||||
Column( "create_time", DateTime, default=now ),
|
||||
Column( "update_time", DateTime, default=now, onupdate=now ),
|
||||
Column( "copied_from_history_dataset_association_id", Integer, ForeignKey( "history_dataset_association.id", use_alter=True, name='history_dataset_association_dataset_id_fkey' ), nullable=True ),
|
||||
Column( "copied_from_library_folder_dataset_association_id", Integer, ForeignKey( "library_folder_dataset_association.id", use_alter=True, name='library_folder_dataset_association_id_fkey' ), nullable=True ),
|
||||
Column( "name", TrimmedString( 255 ) ),
|
||||
Column( "info", TrimmedString( 255 ) ),
|
||||
Column( "blurb", TrimmedString( 255 ) ),
|
||||
Column( "peek" , TEXT ),
|
||||
Column( "extension", TrimmedString( 64 ) ),
|
||||
Column( "metadata", MetadataType(), key="_metadata" ),
|
||||
Column( "parent_id", Integer, ForeignKey( "library_folder_dataset_association.id" ), nullable=True ),
|
||||
Column( "designation", TrimmedString( 255 ) ),
|
||||
Column( "deleted", Boolean, index=True, default=False ),
|
||||
Column( "visible", Boolean ) )
|
||||
|
||||
Library.table = Table( "library", metadata,
|
||||
Column( "id", Integer, primary_key=True ),
|
||||
Column( "root_folder_id", Integer, ForeignKey( "library_folder.id" ), index=True ),
|
||||
Column( "create_time", DateTime, default=now ),
|
||||
Column( "update_time", DateTime, default=now, onupdate=now ),
|
||||
Column( "name", String( 255 ), index=True ),
|
||||
Column( "deleted", Boolean, index=True, default=False ),
|
||||
Column( "purged", 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( "purged", 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 ),
|
||||
@@ -239,6 +362,7 @@ MetadataFile.table = Table( "metadata_file", metadata,
|
||||
Column( "id", Integer, primary_key=True ),
|
||||
Column( "name", TEXT ),
|
||||
Column( "hda_id", Integer, ForeignKey( "history_dataset_association.id" ), index=True, nullable=True ),
|
||||
Column( "lda_id", Integer, ForeignKey( "library_folder_dataset_association.id" ), index=True, nullable=True ),
|
||||
Column( "create_time", DateTime, default=now ),
|
||||
Column( "update_time", DateTime, index=True, default=now, onupdate=now ),
|
||||
Column( "deleted", Boolean, index=True, default=False ),
|
||||
@@ -258,21 +382,37 @@ assign_mapper( context, HistoryDatasetAssociation, HistoryDatasetAssociation.tab
|
||||
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 ) ),
|
||||
visible_children=relation(
|
||||
HistoryDatasetAssociation,
|
||||
primaryjoin=( ( HistoryDatasetAssociation.table.c.parent_id == HistoryDatasetAssociation.table.c.id ) & ( HistoryDatasetAssociation.table.c.visible == True ) ) )
|
||||
) )
|
||||
|
||||
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 ) ),
|
||||
active_history_associations=relation(
|
||||
HistoryDatasetAssociation,
|
||||
primaryjoin=( ( Dataset.table.c.id == HistoryDatasetAssociation.table.c.dataset_id ) & ( HistoryDatasetAssociation.table.c.deleted == False ) ) ),
|
||||
library_associations=relation(
|
||||
LibraryFolderDatasetAssociation,
|
||||
primaryjoin=( Dataset.table.c.id == LibraryFolderDatasetAssociation.table.c.dataset_id ) ),
|
||||
active_library_associations=relation(
|
||||
LibraryFolderDatasetAssociation,
|
||||
primaryjoin=( ( Dataset.table.c.id == LibraryFolderDatasetAssociation.table.c.dataset_id ) & ( LibraryFolderDatasetAssociation.table.c.deleted == False ) ) )
|
||||
) )
|
||||
|
||||
|
||||
@@ -297,14 +437,125 @@ assign_mapper( context, History, History.table,
|
||||
) )
|
||||
|
||||
assign_mapper( context, User, User.table,
|
||||
properties=dict( histories=relation( History, backref="user",
|
||||
properties=dict( histories=relation( History, backref="user",
|
||||
order_by=desc(History.table.c.update_time) ),
|
||||
active_histories=relation( History, primaryjoin=( ( History.table.c.user_id == User.table.c.id ) & ( not_( History.table.c.deleted ) ) ), order_by=desc( History.table.c.update_time ) ),
|
||||
galaxy_sessions=relation( GalaxySession, order_by=desc( GalaxySession.table.c.update_time ) ),
|
||||
stored_workflow_menu_entries=relation( StoredWorkflowMenuEntry, backref="user",
|
||||
cascade="all, delete-orphan",
|
||||
collection_class=ordering_list( 'order_index' ) )
|
||||
) )
|
||||
|
||||
assign_mapper( context, Group, Group.table,
|
||||
properties=dict( users=relation( UserGroupAssociation ) ) )
|
||||
|
||||
assign_mapper( context, UserGroupAssociation, UserGroupAssociation.table,
|
||||
properties=dict( user=relation( User, backref = "groups" ),
|
||||
group=relation( Group, backref = "members" ) ) )
|
||||
|
||||
assign_mapper( context, DefaultUserPermissions, DefaultUserPermissions.table,
|
||||
properties=dict( user=relation( User, backref = "default_permissions" ),
|
||||
role=relation( Role ) ) )
|
||||
|
||||
assign_mapper( context, DefaultHistoryPermissions, DefaultHistoryPermissions.table,
|
||||
properties=dict( history=relation( History, backref = "default_permissions" ),
|
||||
role=relation( Role ) ) )
|
||||
|
||||
assign_mapper( context, Role, Role.table,
|
||||
properties=dict(
|
||||
users=relation( UserRoleAssociation ),
|
||||
groups=relation( GroupRoleAssociation )
|
||||
)
|
||||
)
|
||||
|
||||
assign_mapper( context, UserRoleAssociation, UserRoleAssociation.table,
|
||||
properties=dict(
|
||||
user=relation( User, backref="roles" ),
|
||||
non_private_roles=relation( User,
|
||||
backref="non_private_roles",
|
||||
primaryjoin=( ( User.table.c.id == UserRoleAssociation.table.c.user_id ) & ( UserRoleAssociation.table.c.role_id == Role.table.c.id ) & not_( Role.table.c.type == 'private' ) ) ),
|
||||
role=relation( Role )
|
||||
)
|
||||
)
|
||||
|
||||
assign_mapper( context, GroupRoleAssociation, GroupRoleAssociation.table,
|
||||
properties=dict(
|
||||
group=relation( Group, backref="roles" ),
|
||||
role=relation( Role )
|
||||
)
|
||||
)
|
||||
|
||||
assign_mapper( context, ActionDatasetRoleAssociation, ActionDatasetRoleAssociation.table,
|
||||
properties=dict(
|
||||
dataset=relation( Dataset, backref="actions" ),
|
||||
role=relation( Role, backref="actions" )
|
||||
)
|
||||
)
|
||||
|
||||
assign_mapper( context, Library, Library.table,
|
||||
properties=dict(
|
||||
root_folder=relation( LibraryFolder,
|
||||
backref=backref( "library_root" ) )
|
||||
) )
|
||||
|
||||
assign_mapper( context, LibraryFolder, LibraryFolder.table,
|
||||
properties=dict(
|
||||
folders=relation(
|
||||
LibraryFolder,
|
||||
primaryjoin=( LibraryFolder.table.c.parent_id == LibraryFolder.table.c.id ),
|
||||
backref=backref( "parent", primaryjoin=( LibraryFolder.table.c.parent_id == LibraryFolder.table.c.id ), remote_side=[LibraryFolder.table.c.id] ) ),
|
||||
active_folders=relation( LibraryFolder,
|
||||
primaryjoin=( ( LibraryFolder.table.c.parent_id == LibraryFolder.table.c.id ) & ( not_( LibraryFolder.table.c.deleted ) ) ),
|
||||
order_by=asc( LibraryFolder.table.c.order_id ),
|
||||
lazy=True, #"""sqlalchemy.exceptions.ArgumentError: Error creating eager relationship 'active_folders' on parent class '<class 'galaxy.model.LibraryFolder'>' to child class '<class 'galaxy.model.LibraryFolder'>': Cant use eager loading on a self referential relationship."""
|
||||
viewonly=True ),
|
||||
datasets=relation( LibraryFolderDatasetAssociation,
|
||||
primaryjoin=( ( LibraryFolderDatasetAssociation.table.c.folder_id == LibraryFolder.table.c.id ) ),
|
||||
order_by=asc( LibraryFolderDatasetAssociation.table.c.order_id ),
|
||||
lazy=False,
|
||||
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 ),
|
||||
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] ) ),
|
||||
visible_children=relation(
|
||||
LibraryFolderDatasetAssociation,
|
||||
primaryjoin=( ( LibraryFolderDatasetAssociation.table.c.parent_id == LibraryFolderDatasetAssociation.table.c.id ) & ( LibraryFolderDatasetAssociation.table.c.visible == True ) ) ),
|
||||
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, lazy=False ) ) )
|
||||
|
||||
@@ -370,7 +621,7 @@ assign_mapper( context, StoredWorkflowMenuEntry, StoredWorkflowMenuEntry.table,
|
||||
properties=dict( stored_workflow=relation( StoredWorkflow ) ) )
|
||||
|
||||
assign_mapper( context, MetadataFile, MetadataFile.table,
|
||||
properties=dict( dataset=relation( HistoryDatasetAssociation ) ) )
|
||||
properties=dict( history_dataset=relation( HistoryDatasetAssociation ), library_dataset=relation( LibraryFolderDatasetAssociation ) ) )
|
||||
|
||||
def db_next_hid( self ):
|
||||
"""
|
||||
@@ -390,13 +641,13 @@ def db_next_hid( self ):
|
||||
raise
|
||||
|
||||
History._next_hid = db_next_hid
|
||||
|
||||
def init( file_path, url, engine_options={}, create_tables=False ):
|
||||
"""Connect mappings to the database"""
|
||||
# Connect dataset to the file path
|
||||
Dataset.file_path = file_path
|
||||
|
||||
def guess_dialect_for_url( url ):
|
||||
return (url.split(':', 1))[0]
|
||||
|
||||
def load_egg_for_url( url ):
|
||||
# Load the appropriate db module
|
||||
dialect = (url.split(':', 1))[0]
|
||||
dialect = guess_dialect_for_url( url )
|
||||
try:
|
||||
egg = dialect_to_egg[dialect]
|
||||
try:
|
||||
@@ -408,6 +659,13 @@ def init( file_path, url, engine_options={}, create_tables=False ):
|
||||
except KeyError:
|
||||
# Let this go, it could possibly work with db's we don't support
|
||||
log.error( "database_connection contains an unknown SQLAlchemy database dialect: %s" % dialect )
|
||||
|
||||
def init( file_path, url, engine_options={}, create_tables=False ):
|
||||
"""Connect mappings to the database"""
|
||||
# Connect dataset to the file path
|
||||
Dataset.file_path = file_path
|
||||
# Load the appropriate db module
|
||||
load_egg_for_url( url )
|
||||
# Create the database engine
|
||||
engine = create_engine( url, **engine_options )
|
||||
# Connect the metadata to the database.
|
||||
@@ -427,9 +685,12 @@ def init( file_path, url, engine_options={}, create_tables=False ):
|
||||
# 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 )
|
||||
return result
|
||||
|
||||
def get_suite():
|
||||
"""Get unittest suite for this module"""
|
||||
import unittest, mapping_tests
|
||||
return unittest.makeSuite( mapping_tests.MappingTests )
|
||||
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
"""
|
||||
Galaxy Security
|
||||
|
||||
"""
|
||||
import logging
|
||||
from galaxy.util.bunch import Bunch
|
||||
from galaxy.model.orm import *
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
class Action( object ):
|
||||
def __init__( self, action, description, model ):
|
||||
self.action = action
|
||||
self.description = description
|
||||
self.model = model
|
||||
|
||||
class RBACAgent:
|
||||
"""Class that handles galaxy security"""
|
||||
permitted_actions = Bunch(
|
||||
DATASET_EDIT_METADATA = Action(
|
||||
"edit metadata", "Role members can edit this dataset's metadata in the library", "grant" ),
|
||||
DATASET_MANAGE_PERMISSIONS = Action(
|
||||
"manage permissions", "Role members can manage the roles associated with this dataset", "grant" ),
|
||||
DATASET_ACCESS = Action(
|
||||
"access", "Role members can import this dataset into their history for analysis", "restrict" )
|
||||
)
|
||||
def get_action( self, name, default=None ):
|
||||
"""
|
||||
Get a permitted action by its dict key or action name
|
||||
"""
|
||||
for k, v in self.permitted_actions.items():
|
||||
if k == name or v.action == name:
|
||||
return v
|
||||
return default
|
||||
def get_actions( self ):
|
||||
"""
|
||||
Get all permitted actions as a list
|
||||
"""
|
||||
return self.permitted_actions.__dict__.values()
|
||||
def allow_action( self, user, action, **kwd ):
|
||||
raise 'No valid method of checking action (%s) on %s for user %s.' % ( action, kwd, user )
|
||||
def guess_derived_permissions_for_datasets( self, datasets = [] ):
|
||||
raise "Unimplemented Method"
|
||||
def associate_components( self, **kwd ):
|
||||
raise 'No valid method of associating provided components: %s' % kwd
|
||||
def create_private_user_role( self, user ):
|
||||
raise "Unimplemented Method"
|
||||
def get_private_user_role( self, user ):
|
||||
raise "Unimplemented Method"
|
||||
def user_set_default_permissions( self, user, permissions={}, history=False, dataset=False ):
|
||||
raise "Unimplemented Method"
|
||||
def history_set_default_permissions( self, history, permissions=None, dataset=False, bypass_manage_permission=False ):
|
||||
raise "Unimplemented Method"
|
||||
def set_dataset_permissions( self, dataset, permissions ):
|
||||
raise "Unimplemented Method"
|
||||
def get_component_associations( self, **kwd ):
|
||||
raise "Unimplemented Method"
|
||||
def components_are_associated( self, **kwd ):
|
||||
return bool( self.get_component_associations( **kwd ) )
|
||||
def convert_permitted_action_strings( self, permitted_action_strings ):
|
||||
"""
|
||||
When getting permitted actions from an untrusted source like a
|
||||
form, ensure that they match our actual permitted actions.
|
||||
"""
|
||||
return filter( lambda x: x is not None, [ self.permitted_actions.get( action_string ) for action_string in permitted_action_strings ] )
|
||||
|
||||
class GalaxyRBACAgent( RBACAgent ):
|
||||
def __init__( self, model, permitted_actions=None ):
|
||||
self.model = model
|
||||
if permitted_actions:
|
||||
self.permitted_actions = permitted_actions
|
||||
def allow_action( self, user, action, **kwd ):
|
||||
if 'dataset' in kwd:
|
||||
return self.allow_dataset_action( user, action, kwd['dataset'] )
|
||||
raise 'No valid method of checking action (%s) on %s for user %s.' % ( action, kwd, user )
|
||||
def allow_dataset_action( self, user, action, dataset ):
|
||||
"""Returns true when user has permission to perform an action"""
|
||||
if not isinstance( dataset, self.model.Dataset ):
|
||||
dataset = dataset.dataset
|
||||
if not user:
|
||||
if action == self.permitted_actions.DATASET_ACCESS and action.action not in [ adra.action for adra in dataset.actions ]:
|
||||
return True # anons only get access, and only if there are no roles required for the access action
|
||||
# other actions (or if the dataset has roles defined for the access action) fall through to the false below
|
||||
elif action.action not in [ adra.action for adra in dataset.actions ]:
|
||||
if action.model == 'restrict':
|
||||
return True # implicit access to restrict-style actions if the dataset does not have the action
|
||||
# grant-style actions fall through to the false below
|
||||
else:
|
||||
user_role_ids = sorted( [ r.id for r in user.all_roles() ] )
|
||||
perms = self.get_dataset_permissions( dataset )
|
||||
if action in perms.keys():
|
||||
# the filter() returns a list of the dataset's role ids
|
||||
# of which the user is not a member. so an empty list
|
||||
# means the user has all of the required roles.
|
||||
if not filter( lambda x: x not in user_role_ids, [ r.id for r in perms[ action ] ] ):
|
||||
return True # user has all of the roles required to perform the action
|
||||
# fall through to the false. user is missing at least one required role
|
||||
return False # default is to reject
|
||||
def guess_derived_permissions_for_datasets( self, datasets=[] ):
|
||||
"""Returns a dict of { action : [ role, role, ... ] } for the output dataset based upon provided datasets"""
|
||||
perms = {}
|
||||
for dataset in datasets:
|
||||
if not isinstance( dataset, self.model.Dataset ):
|
||||
dataset = dataset.dataset
|
||||
these_perms = {}
|
||||
# initialize blank perms
|
||||
for action in self.get_actions():
|
||||
these_perms[ action ] = []
|
||||
# collect this dataset's perms
|
||||
these_perms = self.get_dataset_permissions( dataset )
|
||||
# join or intersect this dataset's permissions with others
|
||||
for action, roles in these_perms.items():
|
||||
if action not in perms.keys():
|
||||
perms[ action ] = roles
|
||||
else:
|
||||
if action.model == 'grant':
|
||||
# intersect existing roles with new roles
|
||||
perms[ action ] = filter( lambda x: x in perms[ action ], roles )
|
||||
elif action.model == 'restrict':
|
||||
# join existing roles with new roles
|
||||
perms[ action ].extend( filter( lambda x: x not in perms[ action ], roles ) )
|
||||
return perms
|
||||
def associate_components( self, **kwd ):
|
||||
if 'user' in kwd:
|
||||
if 'group' in kwd:
|
||||
return self.associate_user_group( kwd['user'], kwd['group'] )
|
||||
elif 'role' in kwd:
|
||||
return self.associate_user_role( kwd['user'], kwd['role'] )
|
||||
elif 'role' in kwd:
|
||||
if 'group' in kwd:
|
||||
return self.associate_group_role( kwd['group'], kwd['role'] )
|
||||
if 'action' in kwd:
|
||||
if 'dataset' in kwd and 'role' in kwd:
|
||||
return self.associate_action_dataset_role( kwd['action'], kwd['dataset'], kwd['role'] )
|
||||
raise 'No valid method of associating provided components: %s' % kwd
|
||||
def associate_user_group( self, user, group ):
|
||||
assoc = self.model.UserGroupAssociation( user, group )
|
||||
assoc.flush()
|
||||
return assoc
|
||||
def associate_user_role( self, user, role ):
|
||||
assoc = self.model.UserRoleAssociation( user, role )
|
||||
assoc.flush()
|
||||
return assoc
|
||||
def associate_group_role( self, group, role ):
|
||||
assoc = self.model.GroupRoleAssociation( group, role )
|
||||
assoc.flush()
|
||||
return assoc
|
||||
def associate_action_dataset_role( self, action, dataset, role ):
|
||||
assoc = self.model.ActionDatasetRoleAssociation( action, dataset, role )
|
||||
assoc.flush()
|
||||
return assoc
|
||||
def create_private_user_role( self, user ):
|
||||
# Create private role
|
||||
role = self.model.Role( name=user.email, description='Private Role for ' + user.email, type=self.model.Role.types.PRIVATE )
|
||||
role.flush()
|
||||
# Add user to role
|
||||
self.associate_components( role=role, user=user )
|
||||
return role
|
||||
def get_private_user_role( self, user, auto_create=False ):
|
||||
role = self.model.Role.filter( and_( self.model.Role.table.c.name == user.email,
|
||||
self.model.Role.table.c.type == self.model.Role.types.PRIVATE ) ).first()
|
||||
if not role:
|
||||
if auto_create:
|
||||
return self.create_private_user_role( user )
|
||||
else:
|
||||
return None
|
||||
return role
|
||||
def user_set_default_permissions( self, user, permissions = {}, history=False, dataset=False, bypass_manage_permission=False ):
|
||||
if user is None:
|
||||
return None
|
||||
if not permissions:
|
||||
permissions = { self.permitted_actions.DATASET_MANAGE_PERMISSIONS : [ self.get_private_user_role( user, auto_create=True ) ] }
|
||||
# Delete all of the previous defaults
|
||||
for dup in user.default_permissions:
|
||||
dup.delete()
|
||||
dup.flush()
|
||||
# Add the new defaults (if any)
|
||||
for action, roles in permissions.items():
|
||||
if isinstance( action, Action ):
|
||||
action = action.action
|
||||
for role in roles:
|
||||
dup = self.model.DefaultUserPermissions( user, action, role )
|
||||
dup.flush()
|
||||
if history:
|
||||
for history in user.active_histories:
|
||||
self.history_set_default_permissions( history, permissions=permissions, dataset=dataset, bypass_manage_permission=bypass_manage_permission )
|
||||
def user_get_default_permissions( self, user ):
|
||||
perms = {}
|
||||
for action in self.get_actions():
|
||||
perms[ action ] = []
|
||||
for dup in user.default_permissions:
|
||||
perms[ self.get_action( dup.action ) ].append( dup.role )
|
||||
return perms
|
||||
def history_set_default_permissions( self, history, permissions={}, dataset=False, bypass_manage_permission=False ):
|
||||
if not history.user:
|
||||
return None # default permissions on a userless history are none
|
||||
if not permissions:
|
||||
permissions = self.user_get_default_permissions( history.user )
|
||||
for dhp in history.default_permissions:
|
||||
dhp.delete()
|
||||
dhp.flush()
|
||||
for action, roles in permissions.items():
|
||||
if isinstance( action, Action ):
|
||||
action = action.action
|
||||
for role in roles:
|
||||
dhp = self.model.DefaultHistoryPermissions( history, action, role )
|
||||
dhp.flush()
|
||||
if dataset:
|
||||
for hda_in_history in history.datasets:
|
||||
if len( hda_in_history.dataset.library_associations ):
|
||||
continue # dataset has a library association, don't change the permissions
|
||||
if len( [ hda for hda in hda_in_history.dataset.history_associations if hda.history not in history.user.histories ] ):
|
||||
continue # dataset has a history association in a history the user doesn't own, don't change the permissions
|
||||
# bypass is used to change permissions of datasets in a userless history when logging in
|
||||
if bypass_manage_permission or self.allow_action( history.user, self.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset=hda_in_history.dataset ):
|
||||
self.set_dataset_permissions( hda_in_history.dataset, permissions )
|
||||
def history_get_default_permissions( self, history ):
|
||||
perms = {}
|
||||
for action in self.get_actions():
|
||||
perms[ action ] = []
|
||||
for dhp in history.default_permissions:
|
||||
perms[ self.get_action( dhp.action ) ].append( dhp.role )
|
||||
return perms
|
||||
def set_dataset_permissions( self, dataset, permissions={} ):
|
||||
# to delete permission on an action, pass in a blank list of
|
||||
# roles with that action. leaving an action out of the perm
|
||||
# dict simply leaves those perms untouched (if they exist)
|
||||
incoming_actions = []
|
||||
for action in permissions.keys():
|
||||
if isinstance( action, Action ):
|
||||
action = action.action
|
||||
incoming_actions.append( action )
|
||||
for adra in dataset.actions:
|
||||
if adra.action in incoming_actions:
|
||||
adra.delete()
|
||||
adra.flush()
|
||||
for action, roles in permissions.items():
|
||||
if isinstance( action, Action ):
|
||||
action = action.action
|
||||
for role in roles:
|
||||
self.associate_components( action=action, dataset=dataset, role=role )
|
||||
def get_dataset_permissions( self, dataset ):
|
||||
if not isinstance( dataset, self.model.Dataset ):
|
||||
dataset = dataset.dataset
|
||||
perms = {}
|
||||
for action in self.model.Dataset.permitted_actions.__dict__.values():
|
||||
perms[ action ] = []
|
||||
for adra in dataset.actions:
|
||||
perms[ self.get_action( adra.action ) ].append( adra.role )
|
||||
return perms
|
||||
def copy_dataset_permissions( self, src, dst ):
|
||||
if not isinstance( src, self.model.Dataset ):
|
||||
src = src.dataset
|
||||
if not isinstance( dst, self.model.Dataset ):
|
||||
dst = dst.dataset
|
||||
self.set_dataset_permissions( dst, self.get_dataset_permissions( src ) )
|
||||
def privately_share_dataset( self, dataset, users = [] ):
|
||||
intersect = None
|
||||
for user in users:
|
||||
roles = [ ura.role for ura in user.roles if ura.role.type == self.model.Role.types.SHARING ]
|
||||
if intersect is None:
|
||||
intersect = roles
|
||||
else:
|
||||
new_intersect = []
|
||||
for role in roles:
|
||||
if role in intersect:
|
||||
new_intersect.append( role )
|
||||
intersect = new_intersect
|
||||
sharing_role = None
|
||||
if intersect:
|
||||
for role in intersect:
|
||||
if not filter( lambda x: x not in users, [ ura.user for ura in role.users ] ):
|
||||
# only use a role if it contains ONLY the users we're sharing with
|
||||
sharing_role = role
|
||||
break
|
||||
if sharing_role is None:
|
||||
sharing_role = self.model.Role( name = "Sharing role for: " + ", ".join( [ u.email for u in users ] ),
|
||||
type = self.model.Role.types.SHARING )
|
||||
sharing_role.flush()
|
||||
for user in users:
|
||||
self.associate_components( user=user, role=sharing_role )
|
||||
self.set_dataset_permissions( dataset, { self.permitted_actions.DATASET_ACCESS : [ sharing_role ] } )
|
||||
def set_entity_role_associations( self, roles=[], users=[], groups=[], delete_existing_assocs=True ):
|
||||
for role in roles:
|
||||
if delete_existing_assocs:
|
||||
for a in role.users + role.groups:
|
||||
a.delete()
|
||||
a.flush()
|
||||
for user in users:
|
||||
self.associate_components( user=user, role=role )
|
||||
for group in groups:
|
||||
self.associate_components( group=group, role=role )
|
||||
def get_component_associations( self, **kwd ):
|
||||
assert len( kwd ) == 2, 'You must specify exactly 2 Galaxy security components to check for associations.'
|
||||
if 'dataset' in kwd:
|
||||
if 'action' in kwd:
|
||||
return self.model.ActionDatasetRoleAssociation.filter_by( action = kwd['action'].action, dataset_id = kwd['dataset'].id ).first()
|
||||
elif 'user' in kwd:
|
||||
if 'group' in kwd:
|
||||
return self.model.UserGroupAssociation.filter_by( group_id = kwd['group'].id, user_id = kwd['user'].id ).first()
|
||||
elif 'role' in kwd:
|
||||
return self.model.UserRoleAssociation.filter_by( role_id = kwd['role'].id, user_id = kwd['user'].id ).first()
|
||||
elif 'group' in kwd:
|
||||
if 'role' in kwd:
|
||||
return self.model.GroupRoleAssociation.filter_by( role_id = kwd['role'].id, group_id = kwd['group'].id ).first()
|
||||
raise 'No valid method of associating provided components: %s' % kwd
|
||||
def check_folder_contents( self, user, entry ):
|
||||
"""
|
||||
Return true if there are any datasets under 'folder' that the
|
||||
user has access permission on. We do this a lot and it's a
|
||||
pretty inefficient method, optimizations are welcomed.
|
||||
"""
|
||||
if isinstance( entry, self.model.Library ):
|
||||
return self.check_folder_contents( user, entry.root_folder )
|
||||
elif isinstance( entry, self.model.LibraryFolderDatasetAssociation ):
|
||||
return self.allow_action( user, self.permitted_actions.DATASET_ACCESS, dataset=entry )
|
||||
elif isinstance( entry, self.model.LibraryFolder ):
|
||||
for dataset in entry.active_datasets:
|
||||
if self.allow_action( user, self.permitted_actions.DATASET_ACCESS, dataset=dataset ):
|
||||
return True
|
||||
for folder in entry.active_folders:
|
||||
if self.check_folder_contents( user, folder ):
|
||||
return True
|
||||
return False
|
||||
else:
|
||||
raise 'Passed an illegal object to check_folder_contents: %s' % type( entry )
|
||||
|
||||
def get_permitted_actions( self, filter=None ):
|
||||
'''Utility method to return a subset of RBACAgent's permitted actions'''
|
||||
if filter is None:
|
||||
return RBACAgent.permitted_actions
|
||||
if not filter.endswith('_'):
|
||||
filter += '_'
|
||||
tmp_bunch = Bunch()
|
||||
[tmp_bunch.__dict__.__setitem__(k, v) for k, v in RBACAgent.permitted_actions.items() if k.startswith(filter)]
|
||||
return tmp_bunch
|
||||
@@ -1115,7 +1115,7 @@ class Tool:
|
||||
redirect_url_params = redirect_url_params.replace( "\n", " " ).replace( "\r", " " )
|
||||
return redirect_url_params
|
||||
|
||||
def parse_redirect_url( self, inp_data, param_dict ):
|
||||
def parse_redirect_url( self, data, param_dict ):
|
||||
"""Parse the REDIRECT_URL tool param"""
|
||||
# Tools that send data to an external application via a redirect must include the following 3 tool params:
|
||||
# REDIRECT_URL - the url to which the data is being sent
|
||||
@@ -1134,9 +1134,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
|
||||
@@ -1250,6 +1247,7 @@ class Tool:
|
||||
else: visible = False
|
||||
ext = fields.pop(0).lower()
|
||||
child_dataset = self.app.model.HistoryDatasetAssociation( extension=ext, parent_id=outdata.id, designation=designation, visible=visible, dbkey=outdata.dbkey, create_dataset=True )
|
||||
self.app.security_agent.copy_dataset_permissions( outdata.dataset, child_dataset.dataset )
|
||||
# Move data from temp location to dataset location
|
||||
shutil.move( filename, child_dataset.file_name )
|
||||
child_dataset.flush()
|
||||
@@ -1286,6 +1284,7 @@ class Tool:
|
||||
ext = fields.pop(0).lower()
|
||||
# Create new primary dataset
|
||||
primary_data = self.app.model.HistoryDatasetAssociation( extension=ext, designation=designation, visible=visible, dbkey=outdata.dbkey, create_dataset=True )
|
||||
self.app.security_agent.copy_dataset_permissions( outdata.dataset, primary_data.dataset )
|
||||
primary_data.flush()
|
||||
# Move data from temp location to dataset location
|
||||
shutil.move( filename, primary_data.file_name )
|
||||
|
||||
@@ -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 ):
|
||||
@@ -83,6 +86,14 @@ class DefaultToolAction( object ):
|
||||
data = NoneDataset( datatypes_registry = trans.app.datatypes_registry )
|
||||
if data.dbkey not in [None, '?']:
|
||||
input_dbkey = data.dbkey
|
||||
|
||||
# Determine output dataset permission/roles list
|
||||
existing_datasets = [ inp for inp in inp_data.values() if inp ]
|
||||
if existing_datasets:
|
||||
output_permissions = trans.app.security_agent.guess_derived_permissions_for_datasets( existing_datasets )
|
||||
else:
|
||||
# No valid inputs, we will use history defaults
|
||||
output_permissions = trans.app.security_agent.history_get_default_permissions( trans.history )
|
||||
# Build name for output datasets based on tool name and input names
|
||||
if len( input_names ) == 1:
|
||||
on_text = input_names[0]
|
||||
@@ -136,6 +147,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
|
||||
@@ -195,6 +207,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 )
|
||||
@@ -209,7 +224,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
|
||||
|
||||
@@ -62,13 +62,25 @@ class UploadToolAction( object ):
|
||||
return self.upload_empty( trans, job, "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:
|
||||
temp_name = sniff.stream_to_file( urllib.urlopen( line ) )
|
||||
data_list.append( self.add_file( trans, temp_name, line, file_type, dbkey, info="uploaded url", space_to_tab=space_to_tab ) )
|
||||
data_list.append( self.add_file( trans, temp_name, NAME, file_type, dbkey, info="uploaded url", space_to_tab=space_to_tab ) )
|
||||
except Exception, e:
|
||||
return self.upload_empty( trans, job, "Error:", str( e ) )
|
||||
else:
|
||||
@@ -104,8 +116,9 @@ class UploadToolAction( object ):
|
||||
return dict( output=hda )
|
||||
|
||||
def upload_empty(self, trans, job, err_code, err_msg):
|
||||
data = trans.app.model.HistoryDatasetAssociation( create_dataset = True )
|
||||
data.name = err_code
|
||||
data = trans.app.model.HistoryDatasetAssociation( create_dataset=True )
|
||||
trans.app.security_agent.set_dataset_permissions( data.dataset, trans.app.security_agent.history_get_default_permissions( trans.history ) )
|
||||
data.name = err_code
|
||||
data.extension = "txt"
|
||||
data.dbkey = "?"
|
||||
data.info = err_msg
|
||||
@@ -130,12 +143,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 )
|
||||
@@ -204,6 +217,7 @@ class UploadToolAction( object ):
|
||||
info = 'uploaded %s file' %data_type
|
||||
|
||||
data = trans.app.model.HistoryDatasetAssociation( history = trans.history, extension = ext, create_dataset = True )
|
||||
trans.app.security_agent.set_dataset_permissions( data.dataset, trans.app.security_agent.history_get_default_permissions( trans.history ) )
|
||||
data.name = file_name
|
||||
data.dbkey = dbkey
|
||||
data.info = info
|
||||
|
||||
@@ -3,17 +3,14 @@ Basic tool parameters.
|
||||
"""
|
||||
|
||||
import logging, string, sys, os, os.path
|
||||
|
||||
from elementtree.ElementTree import XML, Element
|
||||
|
||||
from galaxy import config, datatypes, util
|
||||
from galaxy.web import form_builder
|
||||
from galaxy.util.bunch import Bunch
|
||||
|
||||
import validation, dynamic_options
|
||||
|
||||
# For BaseURLToolParameter
|
||||
from galaxy.web import url_for
|
||||
import galaxy.model
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -998,30 +995,16 @@ class DrillDownSelectToolParameter( ToolParameter ):
|
||||
|
||||
|
||||
class DataToolParameter( ToolParameter ):
|
||||
# TODO, Nate: Make sure the following unit tests appropriately test the dataset security
|
||||
# components. Add as many additional tests as necessary.
|
||||
"""
|
||||
Parameter that takes on one (or many) or a specific set of values.
|
||||
|
||||
TODO: There should be an alternate display that allows single selects to be
|
||||
displayed as radio buttons and multiple selects as a set of checkboxes
|
||||
|
||||
>>> # Mock up a history (not connected to database)
|
||||
>>> from galaxy.model import History, HistoryDatasetAssociation
|
||||
>>> from galaxy.util.bunch import Bunch
|
||||
>>> hist = History()
|
||||
>>> hist.flush()
|
||||
>>> hist.add_dataset( HistoryDatasetAssociation( id=1, extension='txt', create_dataset=True ) )
|
||||
>>> hist.add_dataset( HistoryDatasetAssociation( id=2, extension='bed', create_dataset=True ) )
|
||||
>>> hist.add_dataset( HistoryDatasetAssociation( id=3, extension='fasta', create_dataset=True ) )
|
||||
>>> hist.add_dataset( HistoryDatasetAssociation( id=4, extension='png', create_dataset=True ) )
|
||||
>>> hist.add_dataset( HistoryDatasetAssociation( id=5, extension='interval', create_dataset=True ) )
|
||||
>>> p = DataToolParameter( None, XML( '<param name="blah" type="data" format="interval"/>' ) )
|
||||
>>> print p.name
|
||||
blah
|
||||
>>> print p.get_html( trans=Bunch( history=hist ) )
|
||||
<select name="blah">
|
||||
<option value="2">2: Unnamed dataset</option>
|
||||
<option value="5" selected>5: Unnamed dataset</option>
|
||||
</select>
|
||||
TODO: The following must be fixed to test correctly for the new security_check tag in the DataToolParameter ( the last test below is broken )
|
||||
Nate's next passs at the dataset security stuff will dramatically alter this anyway.
|
||||
"""
|
||||
|
||||
def __init__( self, tool, elem ):
|
||||
@@ -1068,28 +1051,38 @@ class DataToolParameter( ToolParameter ):
|
||||
value = [ value ]
|
||||
field = form_builder.SelectField( self.name, self.multiple, None, self.refresh_on_change )
|
||||
# CRUCIAL: the dataset_collector function needs to be local to DataToolParameter.get_html_field()
|
||||
def dataset_collector( datasets, parent_hid ):
|
||||
for i, data in enumerate( datasets ):
|
||||
def dataset_collector( hdas, parent_hid ):
|
||||
for i, hda in enumerate( hdas ):
|
||||
if parent_hid is not None:
|
||||
hid = "%s.%d" % ( parent_hid, i + 1 )
|
||||
else:
|
||||
hid = str( data.hid )
|
||||
if not data.deleted and data.state not in [data.states.ERROR, data.states.DISCARDED] and data.visible:
|
||||
if self.options and data.get_dbkey() != filter_value:
|
||||
hid = str( hda.hid )
|
||||
if not hda.dataset.state in [galaxy.model.Dataset.states.ERROR, galaxy.model.Dataset.states.DISCARDED] and \
|
||||
hda.visible and \
|
||||
trans.app.security_agent.allow_action( trans.user, hda.permitted_actions.DATASET_ACCESS, dataset=hda ):
|
||||
# If we are sending data to an external application, then we need to make sure there are no roles
|
||||
# associated with the dataset that restrict it's access from "public". We determine this by sending
|
||||
# None as the user to the allow_action method.
|
||||
if self.tool and self.tool.tool_type == 'data_destination':
|
||||
if not trans.app.security_agent.allow_action( None, hda.permitted_actions.DATASET_ACCESS, dataset=hda ):
|
||||
continue
|
||||
if self.options and hda.get_dbkey() != filter_value:
|
||||
continue
|
||||
if isinstance( data.datatype, self.formats):
|
||||
selected = ( value and ( data in value ) )
|
||||
field.add_option( "%s: %s" % ( hid, data.name[:30] ), data.id, selected )
|
||||
if isinstance( hda.datatype, self.formats):
|
||||
selected = ( value and ( hda in value ) )
|
||||
field.add_option( "%s: %s" % ( hid, hda.name[:30] ), hda.id, selected )
|
||||
else:
|
||||
target_ext, converted_dataset = data.find_conversion_destination( self.formats, converter_safe = self.converter_safe( other_values, trans ) )
|
||||
target_ext, converted_dataset = hda.find_conversion_destination( self.formats, converter_safe = self.converter_safe( other_values, trans ) )
|
||||
if target_ext:
|
||||
if converted_dataset:
|
||||
data = converted_dataset
|
||||
selected = ( value and ( data in value ) )
|
||||
field.add_option( "%s: (as %s) %s" % ( hid, target_ext, data.name[:30] ), data.id, selected )
|
||||
hda = converted_dataset
|
||||
if not trans.app.security_agent.allow_action( trans.user, trans.app.security_agent.permitted_actions.DATASET_ACCESS, dataset=hda.dataset ):
|
||||
continue
|
||||
selected = ( value and ( hda in value ) )
|
||||
field.add_option( "%s: (as %s) %s" % ( hid, target_ext, hda.name[:30] ), hda.id, selected )
|
||||
# Also collect children via association object
|
||||
dataset_collector( data.children, hid )
|
||||
dataset_collector( history.datasets, None )
|
||||
dataset_collector( hda.children, hid )
|
||||
dataset_collector( history.active_datasets, None )
|
||||
some_data = bool( field.options )
|
||||
if some_data:
|
||||
if value is None or len( field.options ) == 1:
|
||||
|
||||
@@ -264,6 +264,18 @@ def string_as_bool( string ):
|
||||
else:
|
||||
return False
|
||||
|
||||
def listify( item ):
|
||||
"""
|
||||
Make a single item a single item list, or return a list if passed a
|
||||
list. Passing a None returns an empty list.
|
||||
"""
|
||||
if item is None:
|
||||
return []
|
||||
elif isinstance( item, list ):
|
||||
return item
|
||||
else:
|
||||
return [ item ]
|
||||
|
||||
def commaify(amount):
|
||||
orig = amount
|
||||
new = re.sub("^(-?\d+)(\d{3})", '\g<1>,\g<2>', amount)
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
The Galaxy web application.
|
||||
"""
|
||||
|
||||
from framework import expose, json, require_login, url_for, error, form, FormBuilder
|
||||
from framework import expose, json, require_login, require_admin, url_for, error, form, FormBuilder
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ class ASync( BaseController ):
|
||||
if STATUS == 'OK':
|
||||
key = hmac.new( trans.app.config.tool_secret, "%d:%d" % ( data.id, data.history_id), sha ).hexdigest()
|
||||
if key != data_secret:
|
||||
return "You do not have permision to alter data %s." % data_id
|
||||
return "You do not have permission to alter data %s." % data_id
|
||||
# push the job into the queue
|
||||
data.state = data.blurb = data.states.RUNNING
|
||||
log.debug('executing tool %s' % tool.id)
|
||||
@@ -104,6 +104,7 @@ class ASync( BaseController ):
|
||||
#history.datasets.add_dataset( data )
|
||||
|
||||
data = trans.app.model.HistoryDatasetAssociation( create_dataset = True, extension = GALAXY_TYPE )
|
||||
trans.app.security_agent.set_dataset_permissions( data.dataset, trans.app.security_agent.history_get_default_permissions( trans.history ) )
|
||||
data.name = GALAXY_NAME
|
||||
data.dbkey = GALAXY_BUILD
|
||||
data.info = GALAXY_INFO
|
||||
|
||||
@@ -104,31 +104,30 @@ class DatasetInterface( BaseController ):
|
||||
@web.expose
|
||||
def display(self, trans, dataset_id=None, filename=None, **kwd):
|
||||
"""Catches the dataset id and displays file contents as directed"""
|
||||
if filename is None or filename.lower() == "index":
|
||||
try:
|
||||
data = trans.app.model.HistoryDatasetAssociation.get( dataset_id )
|
||||
if data:
|
||||
mime = trans.app.datatypes_registry.get_mimetype_by_extension( data.extension.lower() )
|
||||
trans.response.set_content_type(mime)
|
||||
trans.log_event( "Display dataset id: %s" % str(dataset_id) )
|
||||
try:
|
||||
return open( data.file_name )
|
||||
except:
|
||||
return "This item contains no content"
|
||||
except:
|
||||
pass
|
||||
return "Invalid dataset specified"
|
||||
else:
|
||||
#display files from directory here
|
||||
try:
|
||||
file_path = os.path.join(trans.app.model.HistoryDatasetAssociation.get( dataset_id ).extra_files_path, filename)
|
||||
mime, encoding = mimetypes.guess_type(file_path)
|
||||
if mime is None:
|
||||
mime = trans.app.datatypes_registry.get_mimetype_by_extension(".".split(file_path)[-1])
|
||||
data = trans.app.model.HistoryDatasetAssociation.get( dataset_id )
|
||||
if not data:
|
||||
raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable( "Invalid reference dataset id: %s." % str( dataset_id ) )
|
||||
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 allowed to access this dataset" )
|
||||
|
||||
def _undelete( self, trans, id ):
|
||||
history = trans.get_history()
|
||||
@@ -142,7 +141,7 @@ class DatasetInterface( BaseController ):
|
||||
# Mark undeleted
|
||||
data.mark_undeleted()
|
||||
self.app.model.flush()
|
||||
trans.log_event( "Dataset id %s has been undeleted" % str(id) )
|
||||
trans.log_event( "Dataset id %s has been undeleted" % str(id) )
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -156,7 +155,6 @@ class DatasetInterface( BaseController ):
|
||||
if self._undelete( trans, id ):
|
||||
return "OK"
|
||||
raise "Error undeleting"
|
||||
|
||||
|
||||
@web.expose
|
||||
def copy_datasets( self, trans, source_dataset_ids = "", target_history_ids = "", new_history_name="", do_copy = False ):
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
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 ):
|
||||
libraries = trans.app.model.Library.filter( trans.app.model.Library.table.c.deleted==False ) \
|
||||
.order_by( trans.app.model.Library.table.c.name ).all()
|
||||
return trans.fill_template( '/library/browser.mako', libraries=libraries )
|
||||
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'] )
|
||||
@web.expose
|
||||
def download_dataset_from_folder(self, trans, id, **kwd):
|
||||
"""Catches the dataset id and displays file contents as directed"""
|
||||
# id refers to a LibraryFolderDatasetAssociation object
|
||||
lfda = trans.app.model.LibraryFolderDatasetAssociation.get( id )
|
||||
dataset = trans.app.model.Dataset.get( lfda.dataset_id )
|
||||
if not dataset:
|
||||
msg = 'Invalid id %s received for file downlaod' % str( id )
|
||||
return trans.response.send_redirect( web.url_for( action='library_browser', msg=msg, messagetype='error' ) )
|
||||
mime = trans.app.datatypes_registry.get_mimetype_by_extension( lfda.extension.lower() )
|
||||
trans.response.set_content_type( mime )
|
||||
fStat = os.stat( lfda.file_name )
|
||||
trans.response.headers[ 'Content-Length' ] = int( fStat.st_size )
|
||||
valid_chars = '.,^_-()[]0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
fname = lfda.name
|
||||
fname = ''.join( c in valid_chars and c or '_' for c in fname )[ 0:150 ]
|
||||
trans.response.headers[ "Content-Disposition" ] = "attachment; filename=GalaxyLibraryDataset-%s-[%s]" % ( str( id ), fname )
|
||||
try:
|
||||
return open( lfda.file_name )
|
||||
except:
|
||||
msg = 'This dataset contains no content'
|
||||
return trans.response.send_redirect( web.url_for( action='library_browser', msg=msg, messagetype='error' ) )
|
||||
@@ -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__ )
|
||||
|
||||
@@ -20,16 +16,20 @@ class RootController( BaseController ):
|
||||
return 'This link may not be followed from within Galaxy.'
|
||||
|
||||
@web.expose
|
||||
def index(self, trans, id=None, tool_id=None, mode=None, m_c=None, m_a=None, **kwd):
|
||||
def index(self, trans, id=None, tool_id=None, mode=None, workflow_id=None, m_c=None, m_a=None, **kwd):
|
||||
return trans.fill_template( "root/index.mako",
|
||||
tool_id=tool_id,
|
||||
workflow_id=workflow_id,
|
||||
m_c=m_c, m_a=m_a )
|
||||
|
||||
## ---- Tool related -----------------------------------------------------
|
||||
|
||||
@web.expose
|
||||
def tool_menu( self, trans ):
|
||||
return trans.fill_template('/root/tool_menu.mako', toolbox=self.get_toolbox() )
|
||||
if trans.app.config.require_login and not trans.user:
|
||||
return trans.fill_template( '/no_access.mako', message = 'Please log in to access Galaxy tools.' )
|
||||
else:
|
||||
return trans.fill_template('/root/tool_menu.mako', toolbox=self.get_toolbox() )
|
||||
|
||||
@web.expose
|
||||
def tool_help( self, trans, id ):
|
||||
@@ -55,6 +55,8 @@ class RootController( BaseController ):
|
||||
NOTE: No longer accepts "id" or "template" options for security reasons.
|
||||
"""
|
||||
history = trans.get_history()
|
||||
if trans.app.config.require_login and not trans.user:
|
||||
return trans.fill_template( '/no_access.mako', message = 'Please log in to access Galaxy histories.' )
|
||||
if as_xml:
|
||||
trans.response.set_content_type('text/xml')
|
||||
return trans.fill_template_mako( "root/history_as_xml.mako", history=history )
|
||||
@@ -88,7 +90,7 @@ class RootController( BaseController ):
|
||||
return trans.fill_template("root/history_item.mako", data=data, hid=hid)
|
||||
else:
|
||||
return trans.show_error_message( "Must specify a dataset id.")
|
||||
|
||||
|
||||
@web.json
|
||||
def history_item_updates( self, trans, ids=None, states=None ):
|
||||
# Avoid caching
|
||||
@@ -137,25 +139,28 @@ 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) )
|
||||
if self.app.memory_usage:
|
||||
m1 = trans.app.memory_usage.memory( m0, pretty=True )
|
||||
log.info( "End of root/display, memory used increased by %s" % m1 )
|
||||
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) )
|
||||
if self.app.memory_usage:
|
||||
m1 = trans.app.memory_usage.memory( m0, pretty=True )
|
||||
log.info( "End of root/display, memory used increased by %s" % m1 )
|
||||
try:
|
||||
return open( data.file_name )
|
||||
except:
|
||||
return "This dataset contains no content"
|
||||
else:
|
||||
return "You are not allowed to access this dataset"
|
||||
else:
|
||||
return "No dataset with id '%s'" % str( id )
|
||||
|
||||
@@ -167,9 +172,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 )
|
||||
@@ -179,9 +187,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
|
||||
|
||||
@@ -197,77 +208,113 @@ 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 ):
|
||||
params = util.Params( kwd, safe=False )
|
||||
|
||||
# The following for loop will save all metadata_spec items
|
||||
for name, spec in data.metadata.spec.items():
|
||||
if spec.get("readonly"):
|
||||
continue
|
||||
optional = p.get("is_"+name, None)
|
||||
if optional and optional == 'true':
|
||||
# optional element... == 'true' actually means it is NOT checked (and therefore ommitted)
|
||||
setattr(data.metadata, name, None)
|
||||
else:
|
||||
setattr( data.metadata, name, spec.unwrap( p.get (name, None) ) )
|
||||
if lid is None or trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_EDIT_METADATA, dataset=data ):
|
||||
edit_allowed = True
|
||||
else:
|
||||
edit_allowed = False
|
||||
if params.change:
|
||||
# The user clicked the Save button on the 'Change data type' form
|
||||
if not edit_allowed:
|
||||
return trans.show_error_message( "You are not authorized to change this dataset's metadata." )
|
||||
trans.app.datatypes_registry.change_datatype( data, params.datatype )
|
||||
trans.app.model.flush()
|
||||
elif params.save:
|
||||
# The user clicked the Save button on the 'Edit Attributes' form
|
||||
if not edit_allowed:
|
||||
return trans.show_error_message( "You are not authorized to change this dataset's metadata." )
|
||||
data.name = params.name
|
||||
data.info = params.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 = params.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( params.get (name, None) ) )
|
||||
|
||||
data.datatype.after_edit( data )
|
||||
trans.app.model.flush()
|
||||
return trans.show_ok_message( "Attributes updated", refresh_frames=['history'] )
|
||||
elif p.detect:
|
||||
# The user clicked the Auto-detect button on the 'Edit Attributes' form
|
||||
for name, spec in data.metadata.spec.items():
|
||||
# We need to be careful about the attributes we are resetting
|
||||
if name not in [ 'name', 'info', 'dbkey' ]:
|
||||
if spec.get( 'default' ):
|
||||
setattr( data.metadata, name, spec.unwrap( spec.get( 'default' ) ) )
|
||||
data.datatype.set_meta( data )
|
||||
data.datatype.after_edit( data )
|
||||
trans.app.model.flush()
|
||||
return trans.show_ok_message( "Attributes updated", refresh_frames=['history'] )
|
||||
elif p.convert_data:
|
||||
"""The user clicked the Convert button on the 'Convert to new format' form"""
|
||||
target_type = kwd.get("target_type", None)
|
||||
if target_type:
|
||||
msg = data.datatype.convert_dataset(trans, data, target_type)
|
||||
return trans.show_ok_message( msg, refresh_frames=['history'] )
|
||||
data.datatype.before_edit( data )
|
||||
|
||||
if "dbkey" in data.datatype.metadata_spec and not data.metadata.dbkey:
|
||||
# Copy dbkey into metadata, for backwards compatability
|
||||
# This looks like it does nothing, but getting the dbkey
|
||||
# returns the metadata dbkey unless it is None, in which
|
||||
# case it resorts to the old dbkey. Setting the dbkey
|
||||
# sets it properly in the metadata
|
||||
data.metadata.dbkey = data.dbkey
|
||||
# let's not overwrite the imported datatypes module with the variable datatypes?
|
||||
### the built-in 'id' is overwritten in lots of places as well
|
||||
ldatatypes = [x for x in trans.app.datatypes_registry.datatypes_by_extension.iterkeys()]
|
||||
ldatatypes.sort()
|
||||
trans.log_event( "Opened edit view on dataset %s" % str(id) )
|
||||
return trans.fill_template( "/dataset/edit_attributes.mako", data=data,
|
||||
datatypes=ldatatypes, err=None )
|
||||
data.datatype.after_edit( data )
|
||||
trans.app.model.flush()
|
||||
return trans.show_ok_message( "Attributes updated", refresh_frames=['history'] )
|
||||
elif params.detect:
|
||||
# The user clicked the Auto-detect button on the 'Edit Attributes' form
|
||||
if not edit_allowed:
|
||||
return trans.show_error_message( "You are not authorized to change this dataset's metadata." )
|
||||
for name, spec in data.metadata.spec.items():
|
||||
# We need to be careful about the attributes we are resetting
|
||||
if name not in [ 'name', 'info', 'dbkey' ]:
|
||||
if spec.get( 'default' ):
|
||||
setattr( data.metadata, name, spec.unwrap( spec.get( 'default' ) ) )
|
||||
data.datatype.set_meta( data )
|
||||
data.datatype.after_edit( data )
|
||||
trans.app.model.flush()
|
||||
return trans.show_ok_message( "Attributes updated", refresh_frames=['history'] )
|
||||
elif params.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 edit_allowed:
|
||||
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 params.update_roles:
|
||||
if not trans.user:
|
||||
return trans.show_error_message( "You must be logged in if you want to change permissions." )
|
||||
if trans.app.security_agent.allow_action( trans.user, data.dataset.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset = data.dataset ):
|
||||
permissions = {}
|
||||
for k, v in trans.app.model.Dataset.permitted_actions.items():
|
||||
in_roles = params.get( k + '_in', [] )
|
||||
if not isinstance( in_roles, list ):
|
||||
in_roles = [ in_roles ]
|
||||
in_roles = [ trans.app.model.Role.get( x ) for x in in_roles ]
|
||||
permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles
|
||||
trans.app.security_agent.set_dataset_permissions( data.dataset, permissions )
|
||||
data.dataset.refresh()
|
||||
else:
|
||||
return trans.show_error_message( "You are not authorized to change this dataset's permissions" )
|
||||
data.datatype.before_edit( data )
|
||||
|
||||
if "dbkey" in data.datatype.metadata_spec and not data.metadata.dbkey:
|
||||
# Copy dbkey into metadata, for backwards compatability
|
||||
# This looks like it does nothing, but getting the dbkey
|
||||
# returns the metadata dbkey unless it is None, in which
|
||||
# case it resorts to the old dbkey. Setting the dbkey
|
||||
# sets it properly in the metadata
|
||||
data.metadata.dbkey = data.dbkey
|
||||
# let's not overwrite the imported datatypes module with the variable datatypes?
|
||||
### the built-in 'id' is overwritten in lots of places as well
|
||||
ldatatypes = [x for x in trans.app.datatypes_registry.datatypes_by_extension.iterkeys()]
|
||||
ldatatypes.sort()
|
||||
trans.log_event( "Opened edit view on dataset %s" % str(id) )
|
||||
return trans.fill_template( "/dataset/edit_attributes.mako", data=data, datatypes=ldatatypes, err=None )
|
||||
else:
|
||||
return trans.show_error_message( "You do not have permission to edit this dataset's (%s) attributes." % id )
|
||||
|
||||
def __delete_dataset( self, trans, id ):
|
||||
data = self.app.model.HistoryDatasetAssociation.get( id )
|
||||
@@ -311,7 +358,7 @@ class RootController( BaseController ):
|
||||
def delete_async( self, trans, id = None, **kwd):
|
||||
if id:
|
||||
try:
|
||||
int( id )
|
||||
id = int( id )
|
||||
except:
|
||||
return "Dataset id '%s' is invalid" %str( id )
|
||||
self.__delete_dataset( trans, id )
|
||||
@@ -347,8 +394,13 @@ class RootController( BaseController ):
|
||||
if history:
|
||||
if history.user_id != None and user:
|
||||
assert user.id == history.user_id, "History does not belong to current user"
|
||||
history_names.append(history.name)
|
||||
# Delete DefaultHistoryPermissions
|
||||
for dhp in history.default_permissions:
|
||||
dhp.delete()
|
||||
dhp.flush()
|
||||
# Mark history as deleted in db
|
||||
history.deleted = True
|
||||
history_names.append(history.name)
|
||||
# If deleting the current history, make a new current.
|
||||
if history == trans.get_history():
|
||||
trans.new_history()
|
||||
@@ -364,6 +416,42 @@ class RootController( BaseController ):
|
||||
return trans.show_message( "History deleted: %s" % ",".join(history_names),
|
||||
refresh_frames=['history'])
|
||||
|
||||
@web.expose
|
||||
def history_undelete( self, trans, id=[], **kwd):
|
||||
"""Undeletes a list of histories, ensures that histories are owned by current user"""
|
||||
history_names = []
|
||||
errors = []
|
||||
ok_msg = ""
|
||||
if id:
|
||||
if not isinstance( id, list ):
|
||||
id = id.split( "," )
|
||||
user = trans.get_user()
|
||||
for hid in id:
|
||||
try:
|
||||
int( hid )
|
||||
except:
|
||||
errors.append( "Invalid history: %s" % str( hid ) )
|
||||
continue
|
||||
history = self.app.model.History.get( hid )
|
||||
if history:
|
||||
if history.user != user:
|
||||
errors.append( "History does not belong to current user." )
|
||||
continue
|
||||
if history.purged:
|
||||
errors.append( "History has already been purged and can not be undeleted." )
|
||||
continue
|
||||
history_names.append( history.name )
|
||||
history.deleted = False
|
||||
else:
|
||||
errors.append( "Not able to find history %s." % str( hid ) )
|
||||
trans.log_event( "History id %s marked as undeleted" % str(hid) )
|
||||
self.app.model.flush()
|
||||
if history_names:
|
||||
ok_msg = "Histories (%s) have been undeleted." % ", ".join( history_names )
|
||||
else:
|
||||
errors.append( "You must select at least one history to undelete." )
|
||||
return self.history_available( trans, id=','.join( id ), show_deleted=True, ok_msg = ok_msg, error_msg = " ".join( errors ) )
|
||||
|
||||
@web.expose
|
||||
def history_undelete( self, trans, id=[], **kwd):
|
||||
"""Undeletes a list of histories, ensures that histories are owned by current user"""
|
||||
@@ -442,14 +530,45 @@ class RootController( BaseController ):
|
||||
if not email:
|
||||
return trans.fill_template("/history/share.mako", histories=histories, email=email, send_to_err=send_to_err)
|
||||
user = trans.get_user()
|
||||
send_to_user = trans.app.model.User.filter_by( email=email ).first()
|
||||
send_to_user = trans.app.model.User.filter( trans.app.model.User.table.c.email==email ).first()
|
||||
p = util.Params( kwd )
|
||||
if p.action and p.action == "no_share":
|
||||
trans.response.send_redirect( url_for( action='history_options' ) )
|
||||
if not send_to_user:
|
||||
send_to_err = "No such user"
|
||||
elif user.email == email:
|
||||
send_to_err = "You can't send histories to yourself"
|
||||
else:
|
||||
# if we're not checking or changing permissions, skip this step
|
||||
if not p.action or ( p.action and p.action != 'share' ):
|
||||
# ugly
|
||||
can_change = {}
|
||||
cannot_change = {}
|
||||
for history in histories:
|
||||
for hda in history.active_datasets:
|
||||
if not trans.app.security_agent.allow_action( send_to_user, trans.app.security_agent.permitted_actions.DATASET_ACCESS, dataset=hda ):
|
||||
if trans.app.security_agent.allow_action( user, trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset=hda ) and \
|
||||
not hda.dataset.library_associations:
|
||||
# don't change perms on datasets that exist in the library.
|
||||
if p.action and p.action == "private":
|
||||
trans.app.security_agent.privately_share_dataset( hda.dataset, users=[ user, send_to_user ] )
|
||||
elif p.action and p.action == "public":
|
||||
trans.app.security_agent.set_dataset_permissions( hda.dataset, { trans.app.security_agent.permitted_actions.DATASET_ACCESS : [] } )
|
||||
elif history not in can_change:
|
||||
can_change[history] = [ hda ]
|
||||
else:
|
||||
can_change[history].append( hda )
|
||||
else:
|
||||
if p.action and p.action in [ "private", "public" ]:
|
||||
pass # don't change stuff that the user doesn't have permission to change
|
||||
elif history not in cannot_change:
|
||||
cannot_change[history] = [ hda ]
|
||||
else:
|
||||
cannot_change[history].append( hda )
|
||||
if can_change or cannot_change:
|
||||
return trans.fill_template("/history/share.mako", histories=histories, email=email, send_to_err=send_to_err, can_change=can_change, cannot_change=cannot_change)
|
||||
for history in histories:
|
||||
new_history = history.copy()
|
||||
new_history = history.copy( target_user=send_to_user )
|
||||
new_history.name = history.name+" from "+user.email
|
||||
new_history.user_id = send_to_user.id
|
||||
trans.log_event( "History share, id: %s, name: '%s': to new id: %s" % (str(history.id), history.name, str(new_history.id)) )
|
||||
@@ -505,7 +624,7 @@ 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()
|
||||
@@ -633,7 +752,7 @@ 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"""
|
||||
if trans.app.memory_usage:
|
||||
# Keep track of memory usage
|
||||
@@ -641,6 +760,12 @@ class RootController( BaseController ):
|
||||
try:
|
||||
history = trans.app.model.History.get( history_id )
|
||||
data = trans.app.model.HistoryDatasetAssociation( name = name, info = info, extension = ext, dbkey = dbkey, create_dataset = True )
|
||||
if copy_access_from:
|
||||
copy_access_from = trans.app.model.HistoryDatasetAssociation.get( copy_access_from )
|
||||
trans.app.security_agent.copy_dataset_permissions( copy_access_from.dataset, data.dataset )
|
||||
else:
|
||||
permissions = trans.app.security_agent.history_get_default_permissions( history )
|
||||
trans.app.security_agent.set_dataset_permissions( data.dataset, permissions )
|
||||
data.flush()
|
||||
data_file = open( data.file_name, "wb" )
|
||||
file_data.file.seek( 0 )
|
||||
@@ -660,9 +785,33 @@ class RootController( BaseController ):
|
||||
m1 = trans.app.memory_usage.memory( m0, pretty=True )
|
||||
log.info( "End of root/history_add_to, memory used increased by %s" % m1 )
|
||||
return trans.show_ok_message("Dataset "+str(data.hid)+" added to history "+str(history_id)+".")
|
||||
except:
|
||||
except Exception, e:
|
||||
trans.log_event( "Failed to add dataset to history: %s" % ( e ) )
|
||||
return trans.show_error_message("Adding File to History has Failed")
|
||||
|
||||
@web.expose
|
||||
def history_set_default_permissions( self, trans, **kwd ):
|
||||
"""Sets the user's default permissions for the current history"""
|
||||
if trans.user:
|
||||
if 'update_roles' in kwd:
|
||||
history = trans.get_history()
|
||||
p = util.Params( kwd )
|
||||
permissions = {}
|
||||
for k, v in trans.app.model.Dataset.permitted_actions.items():
|
||||
in_roles = p.get( k + '_in', [] )
|
||||
if not isinstance( in_roles, list ):
|
||||
in_roles = [ in_roles ]
|
||||
in_roles = [ trans.app.model.Role.get( x ) for x in in_roles ]
|
||||
permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles
|
||||
dataset = 'dataset' in kwd
|
||||
bypass_manage_permission = 'bypass_manage_permission' in kwd
|
||||
trans.app.security_agent.history_set_default_permissions( history, permissions, dataset=dataset, bypass_manage_permission=bypass_manage_permission )
|
||||
return trans.show_ok_message( 'Default history permissions have been changed.' )
|
||||
return trans.fill_template( 'history/permissions.mako' )
|
||||
else:
|
||||
#user not logged in, history group must be only public
|
||||
return trans.show_error_message( "You must be logged in to change a history's default permissions." )
|
||||
|
||||
@web.expose
|
||||
def dataset_make_primary( self, trans, id=None):
|
||||
"""Copies a dataset and makes primary"""
|
||||
@@ -685,7 +834,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
|
||||
@@ -693,8 +842,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 ):
|
||||
@@ -737,3 +893,5 @@ class RootController( BaseController ):
|
||||
@web.expose
|
||||
def generate_error( self, trans ):
|
||||
raise Exception( "Fake error!" )
|
||||
|
||||
|
||||
|
||||
@@ -1,14 +1,25 @@
|
||||
"""
|
||||
Contains the user interface in the Universe class
|
||||
"""
|
||||
|
||||
from galaxy.web.base.controller import *
|
||||
|
||||
from galaxy.model.orm import *
|
||||
from galaxy import util
|
||||
import logging, os, string
|
||||
from random import choice
|
||||
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
require_login_template = """
|
||||
<h1>Welcome to Galaxy</h1>
|
||||
|
||||
<p>
|
||||
This installation of Galaxy has been configured such that only users who are logged in may use it.%s
|
||||
</p>
|
||||
<p/>
|
||||
"""
|
||||
require_login_nocreation_template = require_login_template % ""
|
||||
require_login_creation_template = require_login_template % " If you don't already have an account, <a href='%s'>you may create one</a>."
|
||||
|
||||
class User( BaseController ):
|
||||
|
||||
@web.expose
|
||||
@@ -70,57 +81,71 @@ class User( BaseController ):
|
||||
|
||||
@web.expose
|
||||
def login( self, trans, email='', password='' ):
|
||||
if trans.app.memory_usage:
|
||||
# Keep track of memory usage
|
||||
m0 = trans.app.memory_usage.memory()
|
||||
email_error = password_error = None
|
||||
# Attempt login
|
||||
if trans.app.config.require_login:
|
||||
refresh_frames = [ 'masthead', 'history', 'tools' ]
|
||||
else:
|
||||
refresh_frames = [ 'masthead', 'history' ]
|
||||
if email or password:
|
||||
user = trans.app.model.User.filter_by( email=email ).first()
|
||||
user = trans.app.model.User.filter( trans.app.model.User.table.c.email==email ).first()
|
||||
if not user:
|
||||
email_error = "No such user"
|
||||
elif user.deleted:
|
||||
email_error = "This account has been marked deleted, contact your Galaxy administrator to restore the account."
|
||||
elif user.external:
|
||||
return trans.show_error_message( "This account was created for use with an external authentication "
|
||||
+ "method. Please contact your local Galaxy administrator to activate it." )
|
||||
email_error = "This account was created for use with an external authentication method, contact your local Galaxy administrator to activate it."
|
||||
elif not user.check_password( password ):
|
||||
password_error = "Invalid password"
|
||||
else:
|
||||
trans.handle_user_login( user )
|
||||
trans.log_event( "User logged in" )
|
||||
return trans.show_ok_message( "Now logged in as " + user.email, refresh_frames=['masthead', 'history'] )
|
||||
if trans.app.memory_usage:
|
||||
m1 = trans.app.memory_usage.memory( m0, pretty=True )
|
||||
log.info( "End of user/login, memory used increased by %s" % m1 )
|
||||
return trans.show_form(
|
||||
web.FormBuilder( web.url_for(), "Login", submit_text="Login" )
|
||||
.add_text( "email", "Email address", value=email, error=email_error )
|
||||
msg = "Now logged in as " + user.email + "."
|
||||
if trans.app.config.require_login:
|
||||
msg += ' <a href="%s">Click here</a> to continue to the front page.' % web.url_for( '/static/welcome.html' )
|
||||
return trans.show_ok_message( msg, refresh_frames=refresh_frames )
|
||||
form = web.FormBuilder( web.url_for(), "Login", submit_text="Login" ) \
|
||||
.add_text( "email", "Email address", value=email, error=email_error ) \
|
||||
.add_password( "password", "Password", value='', error=password_error,
|
||||
help="<a href='%s'>Forgot password? Reset here</a>" % web.url_for( action='reset_password' ) ) )
|
||||
help="<a href='%s'>Forgot password? Reset here</a>" % web.url_for( action='reset_password' ) )
|
||||
if trans.app.config.require_login:
|
||||
if trans.app.config.allow_user_creation:
|
||||
return trans.show_form( form, header = require_login_creation_template % web.url_for( action = 'create' ) )
|
||||
else:
|
||||
return trans.show_form( form, header = require_login_nocreation_template )
|
||||
else:
|
||||
return trans.show_form( form )
|
||||
|
||||
@web.expose
|
||||
def logout( self, trans ):
|
||||
if trans.app.memory_usage:
|
||||
# Keep track of memory usage
|
||||
m0 = trans.app.memory_usage.memory()
|
||||
if trans.app.config.require_login:
|
||||
refresh_frames = [ 'masthead', 'history', 'tools' ]
|
||||
else:
|
||||
refresh_frames = [ 'masthead', 'history' ]
|
||||
# Since logging an event requires a session, we'll log prior to ending the session
|
||||
trans.log_event( "User logged out" )
|
||||
trans.handle_user_logout()
|
||||
if trans.app.memory_usage:
|
||||
m1 = trans.app.memory_usage.memory( m0, pretty=True )
|
||||
log.info( "End of user/logout, memory used increased by %s" % m1 )
|
||||
return trans.show_ok_message( "You are no longer logged in", refresh_frames=['masthead', 'history'] )
|
||||
msg = "You are no longer logged in."
|
||||
if trans.app.config.require_login:
|
||||
msg += ' <a href="%s">Click here</a> to return to the login page.' % web.url_for( controller='user', action='login' )
|
||||
return trans.show_ok_message( msg, refresh_frames=refresh_frames )
|
||||
|
||||
@web.expose
|
||||
def create( self, trans, email='', password='', confirm='',subscribe=False ):
|
||||
if trans.app.memory_usage:
|
||||
# Keep track of memory usage
|
||||
m0 = trans.app.memory_usage.memory()
|
||||
def create( self, trans, email='', password='', confirm='', subscribe=False ):
|
||||
if trans.app.config.require_login:
|
||||
refresh_frames = [ 'masthead', 'history', 'tools' ]
|
||||
else:
|
||||
refresh_frames = [ 'masthead', 'history' ]
|
||||
if not trans.app.config.allow_user_creation and not trans.user_is_admin():
|
||||
return trans.show_error_message( 'User registration is disabled. Please contact your Galaxy administrator for an account.' )
|
||||
email_error = password_error = confirm_error = None
|
||||
if email:
|
||||
if len( email ) == 0 or "@" not in email or "." not in email:
|
||||
email_error = "Please enter a real email address"
|
||||
elif len( email) > 255:
|
||||
elif len( email ) > 255:
|
||||
email_error = "Email address exceeds maximum allowable length"
|
||||
elif trans.app.model.User.filter_by( email=email ).first():
|
||||
elif trans.app.model.User.filter( and_( trans.app.model.User.table.c.email==email,
|
||||
trans.app.model.User.table.c.deleted==False ) ).first():
|
||||
email_error = "User with that email already exists"
|
||||
elif len( password ) < 6:
|
||||
password_error = "Please use a password of at least 6 characters"
|
||||
@@ -130,7 +155,9 @@ class User( BaseController ):
|
||||
user = trans.app.model.User( email=email )
|
||||
user.set_password_cleartext( password )
|
||||
user.flush()
|
||||
trans.handle_user_login( user )
|
||||
trans.app.security_agent.create_private_user_role( user )
|
||||
trans.app.security_agent.user_set_default_permissions( user ) #we set default user permissions, before we log in and set the history permisions
|
||||
trans.handle_user_login( user ) #This has a call to history_set_default_permissions (needed when logging with a history), user needs to have default permisions set before loging in
|
||||
trans.log_event( "User created a new account" )
|
||||
trans.log_event( "User logged in" )
|
||||
#subscribe user to email list
|
||||
@@ -139,9 +166,6 @@ class User( BaseController ):
|
||||
mail.write("To: %s\nFrom: %s\nSubject: Join Mailing List\n\nJoin Mailing list." % (trans.app.config.mailing_join_addr,email) )
|
||||
if mail.close():
|
||||
return trans.show_warn_message( "Now logged in as " + user.email+". However, subscribing to the mailing list has failed.", refresh_frames=['masthead', 'history'] )
|
||||
if trans.app.memory_usage:
|
||||
m1 = trans.app.memory_usage.memory( m0, pretty=True )
|
||||
log.info( "End of user/create, memory used increased by %s" % m1 )
|
||||
return trans.show_ok_message( "Now logged in as " + user.email, refresh_frames=['masthead', 'history'] )
|
||||
return trans.show_form(
|
||||
web.FormBuilder( web.url_for(), "Create account", submit_text="Create" )
|
||||
@@ -151,13 +175,13 @@ class User( BaseController ):
|
||||
.add_input( "checkbox","Subscribe To Mailing List","subscribe", value='subscribe' ) )
|
||||
|
||||
@web.expose
|
||||
def reset_password(self, trans, email=None, **kwd):
|
||||
def reset_password( self, trans, email=None, **kwd ):
|
||||
error = ''
|
||||
reset_user = trans.app.model.User.filter_by( email=email ).first()
|
||||
reset_user = trans.app.model.User.filter( trans.app.model.User.table.c.email==email ).first()
|
||||
user = trans.get_user()
|
||||
if reset_user:
|
||||
if user and user.id != reset_user.id:
|
||||
error = "You may only reset your own password"
|
||||
error = "You may only reset your own password"
|
||||
else:
|
||||
chars = string.letters + string.digits
|
||||
new_pass = ""
|
||||
@@ -166,13 +190,33 @@ class User( BaseController ):
|
||||
mail = os.popen("%s -t" % trans.app.config.sendmail_path, 'w')
|
||||
mail.write("To: %s\nFrom: no-reply@%s\nSubject: Galaxy Password Reset\n\nYour password has been reset to \"%s\" (no quotes)." % (email, trans.request.remote_addr, new_pass) )
|
||||
if mail.close():
|
||||
return trans.show_ok_message( "Failed to reset password! If this problem persist, submit a bug report.")
|
||||
return trans.show_error_message( 'Failed to reset password. If this problem persists, please submit a bug report.' )
|
||||
reset_user.set_password_cleartext( new_pass )
|
||||
reset_user.flush()
|
||||
trans.log_event( "User reset password: %s" % email )
|
||||
return trans.show_ok_message( "Password has been reset and emailed to: %s." % email)
|
||||
return trans.show_ok_message( "Password has been reset and emailed to: %s. <a href='%s'>Click here</a> to return to the login form." % ( email, web.url_for( action='login' ) ) )
|
||||
elif email != None:
|
||||
error = "The specified user does not exist"
|
||||
return trans.show_form(
|
||||
web.FormBuilder( web.url_for(), "Reset Password", submit_text="Submit" )
|
||||
.add_text( "email", "Email", value=email, error=error ) )
|
||||
|
||||
@web.expose
|
||||
def set_default_permissions( self, trans, **kwd ):
|
||||
"""Sets the user's default permissions for the new histories"""
|
||||
if trans.user:
|
||||
if 'update_roles' in kwd:
|
||||
p = util.Params( kwd )
|
||||
permissions = {}
|
||||
for k, v in trans.app.model.Dataset.permitted_actions.items():
|
||||
in_roles = p.get( k + '_in', [] )
|
||||
if not isinstance( in_roles, list ):
|
||||
in_roles = [ in_roles ]
|
||||
in_roles = [ trans.app.model.Role.get( x ) for x in in_roles ]
|
||||
permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles
|
||||
trans.app.security_agent.user_set_default_permissions( trans.user, permissions )
|
||||
return trans.show_ok_message( 'Default new history permissions have been changed.' )
|
||||
return trans.fill_template( 'user/permissions.mako' )
|
||||
else:
|
||||
# User not logged in, history group must be only public
|
||||
return trans.show_error_message( "You must be logged in to change your default permitted actions." )
|
||||
|
||||
@@ -13,12 +13,17 @@ from galaxy.util.bunch import Bunch
|
||||
from galaxy.util.topsort import topsort, topsort_levels, CycleError
|
||||
from galaxy.workflow.modules import *
|
||||
from galaxy.model.mapping import desc
|
||||
from galaxy.model.orm import *
|
||||
|
||||
class WorkflowController( BaseController ):
|
||||
|
||||
@web.expose
|
||||
@web.require_login( "use Galaxy workflows" )
|
||||
def index( self, trans ):
|
||||
return trans.fill_template( "workflow/index.mako" )
|
||||
|
||||
@web.expose
|
||||
@web.require_login( "use Galaxy workflows" )
|
||||
def list( self, trans ):
|
||||
"""
|
||||
Render workflow main page (management of existing workflows)
|
||||
"""
|
||||
@@ -33,7 +38,29 @@ 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 )
|
||||
|
||||
@web.expose
|
||||
@web.require_login( "use Galaxy workflows" )
|
||||
def list_for_run( self, trans ):
|
||||
"""
|
||||
Render workflow list for analysis view (just allows running workflow
|
||||
or switching to management view)
|
||||
"""
|
||||
user = trans.get_user()
|
||||
workflows = trans.sa_session.query( model.StoredWorkflow ) \
|
||||
.filter_by( user=user, deleted=False ) \
|
||||
.order_by( desc( model.StoredWorkflow.c.update_time ) ) \
|
||||
.all()
|
||||
shared_by_others = trans.sa_session \
|
||||
.query( model.StoredWorkflowUserShareAssociation ) \
|
||||
.filter_by( user=user ) \
|
||||
.filter( model.StoredWorkflow.c.deleted == False ) \
|
||||
.order_by( desc( model.StoredWorkflow.c.update_time ) ) \
|
||||
.all()
|
||||
return trans.fill_template( "workflow/list_for_run.mako",
|
||||
workflows = workflows,
|
||||
shared_by_others = shared_by_others )
|
||||
|
||||
@@ -44,7 +71,8 @@ class WorkflowController( BaseController ):
|
||||
# Load workflow from database
|
||||
stored = get_stored_workflow( trans, id )
|
||||
if email:
|
||||
other = model.User.filter_by( email=email ).first()
|
||||
other = model.User.filter( and_( model.user.table.c.email==email,
|
||||
model.User.table.c.deleted==False ) ).first()
|
||||
if not other:
|
||||
mtype = "error"
|
||||
msg = ( "User '%s' does not exist" % email )
|
||||
@@ -61,10 +89,10 @@ class WorkflowController( BaseController ):
|
||||
share.stored_workflow = stored
|
||||
share.user = other
|
||||
session = trans.sa_session
|
||||
session.save( share )
|
||||
session.save_or_update( share )
|
||||
session.flush()
|
||||
trans.set_message( "Workflow '%s' shared with user '%s'" % ( stored.name, other.email ) )
|
||||
return self.index( trans )
|
||||
return self.list( trans )
|
||||
return trans.fill_template( "workflow/share.mako",
|
||||
message = msg,
|
||||
messagetype = mtype,
|
||||
@@ -79,7 +107,7 @@ class WorkflowController( BaseController ):
|
||||
stored.name = new_name
|
||||
trans.sa_session.flush()
|
||||
trans.set_message( "Workflow renamed to '%s'." % new_name )
|
||||
return self.index( trans )
|
||||
return self.list( trans )
|
||||
else:
|
||||
return form( url_for( id=trans.security.encode_id(stored.id) ), "Rename workflow", submit_text="Rename" ) \
|
||||
.add_text( "new_name", "Workflow Name", value=stored.name )
|
||||
@@ -104,11 +132,11 @@ class WorkflowController( BaseController ):
|
||||
new_stored.user = user
|
||||
# Persist
|
||||
session = trans.sa_session
|
||||
session.save( new_stored )
|
||||
session.save_or_update( new_stored )
|
||||
session.flush()
|
||||
# Display the management page
|
||||
trans.set_message( 'Clone created with name "%s"' % new_stored.name )
|
||||
return self.index( trans )
|
||||
return self.list( trans )
|
||||
|
||||
@web.expose
|
||||
@web.require_login( "create workflows" )
|
||||
@@ -129,11 +157,11 @@ class WorkflowController( BaseController ):
|
||||
stored_workflow.latest_workflow = workflow
|
||||
# Persist
|
||||
session = trans.sa_session
|
||||
session.save( stored_workflow )
|
||||
session.save_or_update( stored_workflow )
|
||||
session.flush()
|
||||
# Display the management page
|
||||
trans.set_message( "Workflow '%s' created" % stored_workflow.name )
|
||||
return self.index( trans )
|
||||
return self.list( trans )
|
||||
else:
|
||||
return form( url_for(), "Create new workflow", submit_text="Create" ) \
|
||||
.add_text( "workflow_name", "Workflow Name", value="Unnamed workflow" )
|
||||
@@ -150,7 +178,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" )
|
||||
@@ -430,11 +458,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) ) ) )
|
||||
|
||||
|
||||
@@ -60,6 +60,19 @@ def require_login( verb="perform this action" ):
|
||||
return decorator
|
||||
return argcatcher
|
||||
|
||||
def require_admin( func ):
|
||||
def decorator( self, trans, *args, **kwargs ):
|
||||
admin_users = trans.app.config.get( "admin_users", "" ).split( "," )
|
||||
if not admin_users:
|
||||
return trans.show_error_message( "You must be logged in as an administrator to access this feature, but no administrators are set in the Galaxy configuration." )
|
||||
user = trans.get_user()
|
||||
if not user:
|
||||
return trans.show_error_message( "You must be logged in as an administrator to access this feature." )
|
||||
if not user.email in admin_users:
|
||||
return trans.show_error_message( "You must be an administrator to access this feature." )
|
||||
return func( self, trans, *args, **kwargs )
|
||||
return decorator
|
||||
|
||||
NOT_SET = object()
|
||||
|
||||
class MessageException( Exception ):
|
||||
@@ -119,6 +132,8 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
|
||||
self.workflow_building_mode = False
|
||||
# Always have a valid galaxy session
|
||||
self.__ensure_valid_session()
|
||||
if self.app.config.require_login:
|
||||
self.__ensure_logged_in_user( environ )
|
||||
@property
|
||||
def sa_session( self ):
|
||||
"""
|
||||
@@ -218,8 +233,7 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
|
||||
galaxy_session.user = self.__get_or_create_remote_user( remote_user_email )
|
||||
galaxy_session_requires_flush = True
|
||||
elif galaxy_session.user.email != remote_user_email:
|
||||
# Session exists but is not associated with the correct
|
||||
# remote user
|
||||
# Session exists but is not associated with the correct remote user
|
||||
invalidate_existing_session = True
|
||||
user_for_new_session = self.__get_or_create_remote_user( remote_user_email )
|
||||
log.warning( "User logged in as '%s' externally, but has a cookie as '%s' invalidating session",
|
||||
@@ -252,6 +266,19 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
|
||||
if prev_galaxy_session:
|
||||
objects_to_flush.append( prev_galaxy_session )
|
||||
sa_session.flush( objects_to_flush )
|
||||
def __ensure_logged_in_user( self, environ ):
|
||||
allowed_paths = (
|
||||
url_for( controller='root', action='index' ),
|
||||
url_for( controller='root', action='tool_menu' ),
|
||||
url_for( controller='root', action='masthead' ),
|
||||
url_for( controller='root', action='history' ),
|
||||
url_for( controller='user', action='login' ),
|
||||
url_for( controller='user', action='create' ),
|
||||
url_for( controller='user', action='reset_password' ),
|
||||
url_for( controller='library', action='browse' )
|
||||
)
|
||||
if self.galaxy_session.user is None and self.request.path not in allowed_paths:
|
||||
self.response.send_redirect( url_for( controller='root', action='index' ) )
|
||||
def __create_new_session( self, prev_galaxy_session=None, user_for_new_session=None ):
|
||||
"""
|
||||
Create a new GalaxySession for this request, possibly with a connection
|
||||
@@ -278,16 +305,16 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
|
||||
def __get_or_create_remote_user( self, remote_user_email ):
|
||||
"""
|
||||
Return the user in $HTTP_REMOTE_USER and create if necessary
|
||||
|
||||
Caller is responsible for flushing the returned user.
|
||||
"""
|
||||
# remote_user middleware ensures HTTP_REMOTE_USER exists
|
||||
user = self.app.model.User.filter_by( email=remote_user_email ).first()
|
||||
user = self.app.model.User.filter( self.app.model.User.table.c.email==remote_user_email ).first()
|
||||
if user is None:
|
||||
user = self.app.model.User( email=remote_user_email )
|
||||
user.set_password_cleartext( 'external' )
|
||||
user.external = True
|
||||
#self.log_event( "Automatically created account '%s'", user.email )
|
||||
elif user.deleted:
|
||||
return self.show_error_message( "Your account is no longer valid, contact your Galaxy administrator to activate your account." )
|
||||
return user
|
||||
def __update_session_cookie( self ):
|
||||
"""
|
||||
@@ -301,20 +328,25 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
|
||||
Login a new user (possibly newly created)
|
||||
- create a new session
|
||||
- associate new session with user
|
||||
- if old session had a history and it was not associated with a user, associate it with the new session.
|
||||
- if old session had a history and it was not associated with a user, associate it with the new session,
|
||||
otherwise associate the current session's history with the user
|
||||
"""
|
||||
prev_galaxy_session = self.galaxy_session
|
||||
prev_galaxy_session.is_valid = False
|
||||
self.galaxy_session = self.__create_new_session( prev_galaxy_session, user )
|
||||
if prev_galaxy_session.current_history:
|
||||
history = prev_galaxy_session.current_history
|
||||
if history.user is None:
|
||||
self.galaxy_session.add_history( history )
|
||||
self.galaxy_session.current_history = history
|
||||
history.user = user
|
||||
self.sa_session.flush( [ prev_galaxy_session, self.galaxy_session, history ] )
|
||||
elif self.galaxy_session.current_history:
|
||||
history = self.galaxy_session.current_history
|
||||
else:
|
||||
self.sa_session.flush( [ prev_galaxy_session, self.galaxy_session ] )
|
||||
history = self.history
|
||||
if history not in self.galaxy_session.histories:
|
||||
self.galaxy_session.add_history( history )
|
||||
if history.user is None:
|
||||
history.user = user
|
||||
self.galaxy_session.current_history = history
|
||||
self.app.security_agent.history_set_default_permissions( history, dataset=True, bypass_manage_permission=True )
|
||||
self.sa_session.flush( [ prev_galaxy_session, self.galaxy_session, history ] )
|
||||
self.__update_session_cookie()
|
||||
def handle_user_logout( self ):
|
||||
"""
|
||||
@@ -368,6 +400,8 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
|
||||
history.user = self.galaxy_session.user
|
||||
# Track genome_build with history
|
||||
history.genome_build = util.dbnames.default_value
|
||||
# Set the user's default history permissions
|
||||
self.app.security_agent.history_set_default_permissions( history )
|
||||
# Save
|
||||
self.sa_session.flush( [ self.galaxy_session, history ] )
|
||||
return history
|
||||
@@ -380,7 +414,13 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
|
||||
self.galaxy_session.user = user
|
||||
self.sa_session.flush( [ self.galaxy_session ] )
|
||||
user = property( get_user, set_user )
|
||||
|
||||
|
||||
def user_is_admin( self ):
|
||||
admin_users = self.app.config.get( "admin_users", "" ).split( "," )
|
||||
if self.user and admin_users and self.user.email in admin_users:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_toolbox(self):
|
||||
"""Returns the application toolbox"""
|
||||
return self.app.toolbox
|
||||
@@ -426,12 +466,12 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
|
||||
Convenience method for displaying an warn message. See `show_message`.
|
||||
"""
|
||||
return self.show_message( message, 'warning', refresh_frames )
|
||||
def show_form( self, form ):
|
||||
def show_form( self, form, header=None ):
|
||||
"""
|
||||
Convenience method for displaying a simple page with a single HTML
|
||||
form.
|
||||
"""
|
||||
return self.fill_template( "form.mako", form=form )
|
||||
return self.fill_template( "form.mako", form=form, header=header )
|
||||
def fill_template(self, filename, **kwargs):
|
||||
"""
|
||||
Fill in a template, putting any keyword arguments on the context.
|
||||
@@ -468,8 +508,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 )
|
||||
@@ -480,13 +520,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 ):
|
||||
"""
|
||||
@@ -506,3 +547,4 @@ class Bunch( dict ):
|
||||
return self[key]
|
||||
def __setattr__( self, key, value ):
|
||||
self[key] = value
|
||||
|
||||
|
||||
@@ -84,15 +84,19 @@ class WebApplication( object ):
|
||||
friendly objects, finds the appropriate method to handle the request
|
||||
and calls it.
|
||||
"""
|
||||
# Setup the transaction
|
||||
trans = self.transaction_factory( environ )
|
||||
# Map url using routes
|
||||
path_info = trans.request.path_info
|
||||
path_info = environ.get( 'PATH_INFO', '' )
|
||||
map = self.mapper.match( path_info )
|
||||
if map == None:
|
||||
raise httpexceptions.HTTPNotFound( "No route for " + path_info )
|
||||
# Save the complete mapper dict, we pop things off so they don't get passed down
|
||||
raw_map = dict( map )
|
||||
# Setup routes
|
||||
rc = routes.request_config()
|
||||
rc.mapper = self.mapper
|
||||
rc.mapper_dict = map
|
||||
rc.environ = environ
|
||||
# Setup the transaction
|
||||
trans = self.transaction_factory( environ )
|
||||
rc.redirect = trans.response.send_redirect
|
||||
# Get the controller class
|
||||
controller_name = map.pop( 'controller', None )
|
||||
controller = self.controllers.get( controller_name, None )
|
||||
@@ -111,12 +115,6 @@ class WebApplication( object ):
|
||||
# Is the method callable
|
||||
if not callable( method ):
|
||||
raise httpexceptions.HTTPNotFound( "Action not callable for " + path_info )
|
||||
# Setup routes
|
||||
rc = routes.request_config()
|
||||
rc.mapper = self.mapper
|
||||
rc.mapper_dict = raw_map
|
||||
rc.environ = environ
|
||||
rc.redirect = trans.response.send_redirect
|
||||
# Combine mapper args and query string / form args and call
|
||||
kwargs = trans.request.params.mixed()
|
||||
kwargs.update( map )
|
||||
@@ -336,4 +334,4 @@ def flatten( seq ):
|
||||
for y in flatten( x, encoding ):
|
||||
yield y
|
||||
else:
|
||||
yield x
|
||||
yield x
|
||||
|
||||
@@ -27,6 +27,7 @@ class Configuration( object ):
|
||||
self.new_file_path = resolve_path( kwargs.get( "new_file_path", "database/tmp" ), self.root )
|
||||
self.id_secret = kwargs.get( "id_secret", "USING THE DEFAULT IS NOT SECURE!" )
|
||||
self.use_remote_user = string_as_bool( kwargs.get( "use_remote_user", "False" ) )
|
||||
self.require_login = string_as_bool( kwargs.get( "require_login", "False" ) )
|
||||
self.template_path = resolve_path( kwargs.get( "template_path", "templates" ), self.root )
|
||||
self.template_cache = resolve_path( kwargs.get( "template_cache_path", "database/reports/compiled_templates" ), self.root )
|
||||
self.admin_pass = kwargs.get('admin_pass',"galaxy")
|
||||
|
||||
@@ -104,7 +104,7 @@ class Users( BaseController ):
|
||||
cutoff_time = datetime.utcnow() - timedelta( days=int( not_logged_in_for_days ) )
|
||||
now = strftime( "%Y-%m-%d %H:%M:%S" )
|
||||
users = []
|
||||
for user in galaxy.model.User.query().order_by( galaxy.model.User.table.c.email ).all():
|
||||
for user in galaxy.model.User.filter( galaxy.model.User.table.c.deleted==False ).order_by( galaxy.model.User.table.c.email ).all():
|
||||
if user.galaxy_sessions:
|
||||
last_galaxy_session = user.galaxy_sessions[ 0 ]
|
||||
if last_galaxy_session.update_time < cutoff_time:
|
||||
|
||||
@@ -43,8 +43,8 @@ use_lint = false
|
||||
# NEVER enable this on a public site (even test or QA)
|
||||
# use_interactive = true
|
||||
|
||||
# Admin Password
|
||||
admin_pass = "galaxy"
|
||||
# Force everyone to log in (disable anonymous access)
|
||||
require_login = False
|
||||
|
||||
# path to sendmail
|
||||
sendmail_path = /usr/sbin/sendmail
|
||||
|
||||
@@ -207,6 +207,12 @@ def purge_histories( h, d, m, cutoff_time, remove_from_disk ):
|
||||
metadata_file.purged = True
|
||||
metadata_file.flush()
|
||||
print "%s" % metadata_file.file_name()
|
||||
for lda in dataset.library_associations:
|
||||
for metadata_file in m.filter( m.table.c.lda_id==lda.id ).all():
|
||||
metadata_file.deleted = True
|
||||
metadata_file.purged = True
|
||||
metadata_file.flush()
|
||||
print "%s" % metadata_file.file_name()
|
||||
dataset_count += 1
|
||||
try:
|
||||
disk_space += file_size
|
||||
@@ -275,6 +281,12 @@ def purge_datasets( d, m, cutoff_time, remove_from_disk ):
|
||||
metadata_file.purged = True
|
||||
metadata_file.flush()
|
||||
print "%s" % metadata_file.file_name()
|
||||
for lda in dataset.library_associations:
|
||||
for metadata_file in m.filter( m.table.c.lda_id==lda.id ).all():
|
||||
metadata_file.deleted = True
|
||||
metadata_file.purged = True
|
||||
metadata_file.flush()
|
||||
print "%s" % metadata_file.file_name()
|
||||
dataset_count += 1
|
||||
try:
|
||||
disk_space += file_size
|
||||
@@ -310,6 +322,12 @@ def purge_dataset( dataset, d, m ):
|
||||
if not shared_data.deleted:
|
||||
purgable = False
|
||||
break
|
||||
if purgable:
|
||||
# This check handles the library_folder_dataset_association approach to sharing.
|
||||
for shared_data in dataset.library_associations:
|
||||
if not shared_data.deleted:
|
||||
purgable = False
|
||||
break
|
||||
if purgable:
|
||||
dataset.purged = True
|
||||
dataset.file_size = 0
|
||||
@@ -326,6 +344,12 @@ def purge_dataset( dataset, d, m ):
|
||||
metadata_file.purged = True
|
||||
metadata_file.flush()
|
||||
print "%s" % metadata_file.file_name()
|
||||
for lda in dataset.library_associations:
|
||||
for metadata_file in m.filter( m.table.c.lda_id==lda.id ).all():
|
||||
metadata_file.deleted = True
|
||||
metadata_file.purged = True
|
||||
metadata_file.flush()
|
||||
print "%s" % metadata_file.file_name()
|
||||
try:
|
||||
# Remove associated extra files from disk if they exist
|
||||
os.unlink( dataset.extra_files_path )
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
import sys, os
|
||||
|
||||
assert sys.version_info[:2] >= ( 2, 4 )
|
||||
|
||||
from eggs import get_full_platform, get_noplatform
|
||||
lib = os.path.abspath( os.path.join( os.path.dirname( __file__ ), "..", "lib" ) )
|
||||
sys.path.append( lib )
|
||||
|
||||
from galaxy.eggs import get_platform, get_noplatform
|
||||
print get_noplatform()
|
||||
print get_full_platform()
|
||||
print get_platform( platform=True )
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
#!/usr/bin/env python
|
||||
#Dan Blankenberg
|
||||
"""
|
||||
Backup your database before running.
|
||||
|
||||
Updates a pre-security/library database with necessary schema changes
|
||||
and sets default roles/permissions for users and their histories and datasets.
|
||||
|
||||
It will reset permissions on histories and datasets if are already set.
|
||||
|
||||
This has been tested with Postgres, SQLite, and MySQL databases.
|
||||
|
||||
Due to limitations of SQLite this script is unable to add
|
||||
foreign keys to existing SQLite tables (fks are ignored anyway).
|
||||
|
||||
Remember to backup your database before running.
|
||||
"""
|
||||
|
||||
import sys, os, ConfigParser, tempfile
|
||||
import galaxy.app, galaxy.model
|
||||
import sqlalchemy
|
||||
|
||||
assert sys.version_info[:2] >= ( 2, 4 )
|
||||
|
||||
def main():
|
||||
def print_warning( warning_text ):
|
||||
print "\nWarning: %s\n" % ( warning_text )
|
||||
|
||||
print
|
||||
print "The purpose of this script is to update an existing Galaxy database that does not have Library or Security settings to a version that does."
|
||||
print
|
||||
print "This script will set default roles for users (If run on an already updated database this will reset all datasets to initial permissions - public)."
|
||||
print
|
||||
print "*** It is important to backup your database before you continue. ***"
|
||||
print
|
||||
print "If you have backed up your database and would like to run this script, type 'yes'."
|
||||
print
|
||||
should_continue = raw_input("enter 'yes' to continue>")
|
||||
if should_continue.lower() != "yes":
|
||||
print "Script aborted by user."
|
||||
sys.exit(0)
|
||||
|
||||
#Load Configuration from file
|
||||
ini_file = sys.argv.pop(1)
|
||||
conf_parser = ConfigParser.ConfigParser({'here':os.getcwd()})
|
||||
conf_parser.read(ini_file)
|
||||
configuration = {}
|
||||
for key, value in conf_parser.items("app:main"): configuration[key] = value
|
||||
|
||||
#if we don't load the tools, the app will startup much faster
|
||||
empty_xml = tempfile.NamedTemporaryFile()
|
||||
empty_xml.write( "<root/>" )
|
||||
empty_xml.flush()
|
||||
configuration['tool_config_file'] = empty_xml.name
|
||||
#No need to load job runners
|
||||
configuration['enable_job_running'] = False
|
||||
|
||||
print
|
||||
print "Loading app, with database_create_tables=False, to add Columns to existing tables"
|
||||
print
|
||||
#we set database_create_tables to False, then load the app
|
||||
configuration['database_create_tables'] = False
|
||||
app = galaxy.app.UniverseApplication( global_conf = ini_file, **configuration )
|
||||
|
||||
#try to guess the database type that we have, in order to execute the proper raw SQL commands
|
||||
if app.config.database_connection:
|
||||
dialect = galaxy.model.mapping.guess_dialect_for_url( app.config.database_connection )
|
||||
else:
|
||||
dialect = "sqlite" #default dialect is sqlite
|
||||
|
||||
#Now we alter exisiting tables
|
||||
#Unfortunately SQLAlchemy does not support this
|
||||
#SQLite is very lacking in its implementation of the ALTER command, we'll do what we can...
|
||||
|
||||
#we cannot check with SA to see if new columns exist, so we will try and except (trying to access the table with our current SA Metadata which references missing columns will cause exception)
|
||||
#user table
|
||||
#this table needs to be altered differently depending on if we are using sqlite or not
|
||||
if dialect == "sqlite":
|
||||
try:
|
||||
app.model.session.execute( "ALTER TABLE 'galaxy_user' ADD COLUMN 'deleted' BOOLEAN default 0" ) #'true' and 'false' doesn't work properly in SQLite --> both are always True (is sqlite actually storing a string here, which is always true when not empty?)
|
||||
except sqlalchemy.exceptions.OperationalError, e:
|
||||
print_warning( "adding column 'deleted' failed: %s" % ( e ) )
|
||||
try:
|
||||
app.model.session.execute( "ALTER TABLE 'galaxy_user' ADD COLUMN 'purged' BOOLEAN default 0" )
|
||||
except sqlalchemy.exceptions.OperationalError, e:
|
||||
print_warning( "adding column 'purged' failed: %s" % ( e ) )
|
||||
else:
|
||||
try:
|
||||
app.model.session.execute( "ALTER TABLE galaxy_user ADD COLUMN deleted BOOLEAN default false" )
|
||||
except ( sqlalchemy.exceptions.ProgrammingError, sqlalchemy.exceptions.OperationalError ), e: #Postgres and MySQL raise different Exceptions for this same failure.
|
||||
print_warning( "adding column 'deleted' failed: %s" % ( e ) )
|
||||
try:
|
||||
app.model.session.execute( "ALTER TABLE galaxy_user ADD COLUMN purged BOOLEAN default false" )
|
||||
except ( sqlalchemy.exceptions.ProgrammingError, sqlalchemy.exceptions.OperationalError ), e:
|
||||
print_warning( "adding column 'purged' failed: %s" % ( e ) )
|
||||
|
||||
#these alters are the same, regardless if we are using sqlite
|
||||
#hda table
|
||||
try:
|
||||
app.model.session.execute( "ALTER TABLE history_dataset_association ADD COLUMN copied_from_library_folder_dataset_association_id INTEGER" )
|
||||
except ( sqlalchemy.exceptions.ProgrammingError, sqlalchemy.exceptions.OperationalError ), e:
|
||||
print_warning( "adding column 'copied_from_library_folder_dataset_association_id' failed: %s" % ( e ) )
|
||||
|
||||
#MetadataFile table
|
||||
try:
|
||||
app.model.session.execute( "ALTER TABLE metadata_file ADD COLUMN lda_id INTEGER" )
|
||||
except ( sqlalchemy.exceptions.ProgrammingError, sqlalchemy.exceptions.OperationalError ), e:
|
||||
print_warning( "adding column 'lda_id' failed: %s" % ( e ) )
|
||||
|
||||
|
||||
#now create indexes for new columns in user and metadata file tables
|
||||
try:
|
||||
i = sqlalchemy.Index( 'ix_galaxy_user_deleted', app.model.User.table.c.deleted )
|
||||
i.create()
|
||||
except Exception, e:
|
||||
print_warning( "Adding index failed: %s" % ( e ) )
|
||||
try:
|
||||
i = sqlalchemy.Index( 'ix_galaxy_user_purged', app.model.User.table.c.purged )
|
||||
i.create()
|
||||
except Exception, e:
|
||||
print_warning( "Adding index failed: %s" % ( e ) )
|
||||
try:
|
||||
i = sqlalchemy.Index( 'ix_metadata_file_lda_id', app.model.MetadataFile.table.c.lda_id )
|
||||
i.create()
|
||||
except Exception, e:
|
||||
print_warning( "Adding index failed: %s" % ( e ) )
|
||||
|
||||
|
||||
#Now we shutdown the app
|
||||
app.shutdown()
|
||||
del app
|
||||
print
|
||||
print "Columns added to tables, restarting app, with database_create_tables=True"
|
||||
|
||||
#We restart the app, this time with create_tables == True
|
||||
configuration['database_create_tables'] = True
|
||||
app = galaxy.app.UniverseApplication( global_conf = ini_file, **configuration )
|
||||
|
||||
##Now we add foreign key constraints as necessary for columns added above
|
||||
print "Adding foreign key constraints"
|
||||
if dialect != "sqlite":
|
||||
try:
|
||||
app.model.session.execute( "ALTER TABLE history_dataset_association ADD FOREIGN KEY (copied_from_library_folder_dataset_association_id) REFERENCES library_folder_dataset_association(id)" )
|
||||
except Exception, e:
|
||||
print_warning( "Adding foreign key constraint to table has failed for an unknown reason: %s" % ( e ) )
|
||||
try:
|
||||
app.model.session.execute( "ALTER TABLE metadata_file ADD FOREIGN KEY (lda_id) REFERENCES library_folder_dataset_association(id)" )
|
||||
except Exception, e:
|
||||
print_warning( "Adding foreign key constraint to table has failed for an unknown reason: %s" % ( e ) )
|
||||
|
||||
else:
|
||||
#Can't do this in SQLite
|
||||
#SQLite ignores (but parses on initial table creation) foreign key constraints anyway, see: http://www.sqlite.org/omitted.html (there is someway to set up behavior using triggers)
|
||||
print_warning( "Adding foreign key constraints to table is not supported in SQLite." )
|
||||
|
||||
#create default permisions for users, histories, etc
|
||||
print "creating private roles and setting defaults for existing users and their histories and datasets"
|
||||
|
||||
#Setup each user with a private role and then set their default permisions and set permisions on existing histories and datasets
|
||||
for user in app.model.User.query().all():
|
||||
print "Setting up user: %s." % user.email
|
||||
if not app.model.security_agent.get_private_user_role( user ):
|
||||
app.model.security_agent.create_private_user_role( user )
|
||||
else:
|
||||
print_warning( "user (%s) already has a private role, (re)setting defaults anyway" % ( user.email ) )
|
||||
#set default permissions and permissions on existing items
|
||||
#we will do this even if a role for the user already exists (slower, but safer)
|
||||
app.model.security_agent.user_set_default_permissions( user, history=True, dataset=True, bypass_manage_permission=True )
|
||||
|
||||
app.model.flush()
|
||||
|
||||
app.shutdown()
|
||||
print
|
||||
print "Update finished, please review output for warnings and errors."
|
||||
empty_xml.close() #close tempfile, it will automatically be deleted off system
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script must be executed from the $UNIVERSE_HOME directory
|
||||
# e.g., sh ./scripts/update_database/update_database_with_security_libraries.sh
|
||||
|
||||
. ./scripts/get_python.sh
|
||||
. ./setup_paths.sh
|
||||
|
||||
$GALAXY_PYTHON ./scripts/update_database/update_database_with_security_libraries.py ./universe_wsgi.ini $@
|
||||
|
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;
|
||||
}
|
||||
@@ -79,7 +79,12 @@ function make_left_panel( panel_el, center_el, border_el ) {
|
||||
}
|
||||
}
|
||||
).find( "div" ).show();;
|
||||
|
||||
var force_panel = function( op ) {
|
||||
if ( ( hidden && op == 'show' ) || ( ! hidden && op == 'hide' ) ) {
|
||||
toggle();
|
||||
}
|
||||
}
|
||||
return { force_panel: force_panel };
|
||||
};
|
||||
|
||||
function make_right_panel( panel_el, center_el, border_el ) {
|
||||
@@ -173,7 +178,12 @@ function make_right_panel( panel_el, center_el, border_el ) {
|
||||
}
|
||||
}
|
||||
).find( "div" ).show();
|
||||
return { handle_minwidth_hint: handle_minwidth_hint };
|
||||
var force_panel = function( op ) {
|
||||
if ( ( hidden && op == 'show' ) || ( ! hidden && op == 'hide' ) ) {
|
||||
toggle();
|
||||
}
|
||||
}
|
||||
return { handle_minwidth_hint: handle_minwidth_hint, force_panel: force_panel };
|
||||
};
|
||||
|
||||
// Modal dialog boxes
|
||||
@@ -234,4 +244,4 @@ function make_popupmenu( button_element, options ) {
|
||||
} );
|
||||
};
|
||||
$( button_element ).click( click );
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1 +1 @@
|
||||
var hidden_width=7;var border_tweak=9;var jq=jQuery;function ensure_dd_helper(){if(jq("#DD-helper").length==0){$("<div id='DD-helper'/>").css({background:"white",opacity:0,zIndex:9000,position:"absolute",top:0,left:0,width:"100%",height:"100%"}).appendTo("body").hide()}}function make_left_panel(E,A,B){var D=false;var C=null;resize=function(F){var G=F;if(F<0){F=0}jq(E).css("width",F);jq(B).css("left",G);jq(A).css("left",F+7);if(document.recalc){document.recalc()}};toggle=function(){if(D){jq(B).removeClass("hover");jq(B).animate({left:C},"fast");jq(E).css("left",-C).show().animate({left:0},"fast",function(){resize(C);jq(B).removeClass("hidden")});D=false}else{C=jq(B).position().left;jq(A).css("left",hidden_width);if(document.recalc){document.recalc()}jq(B).removeClass("hover");jq(E).animate({left:-C},"fast");jq(B).animate({left:-1},"fast",function(){jq(this).addClass("hidden")});D=true}};jq(B).hover(function(){jq(this).addClass("hover")},function(){jq(this).removeClass("hover")}).draggable({start:function(F,G){jq("#DD-helper").show()},stop:function(F,G){jq("#DD-helper").hide();return false},drag:function(F,G){x=G.position.left;x=Math.min(400,Math.max(100,x));if(D){jq(E).css("left",0);jq(B).removeClass("hidden");D=false}resize(x);G.position.left=x;G.position.top=$(this).data("draggable").originalPosition.top},click:function(){toggle()}}).find("div").show()}function make_right_panel(A,E,G){var I=false;var F=false;var C=null;var D=function(J){jq(A).css("width",J);jq(E).css("right",J+9);jq(G).css("right",J).css("left","");if(document.recalc){document.recalc()}};var H=function(){if(I){jq(G).removeClass("hover");jq(G).animate({right:C},"fast");jq(A).css("right",-C).show().animate({right:0},"fast",function(){D(C);jq(G).removeClass("hidden")});I=false}else{C=jq(document).width()-jq(G).position().left-border_tweak;jq(E).css("right",hidden_width+1);if(document.recalc){document.recalc()}jq(G).removeClass("hover");jq(A).animate({right:-C},"fast");jq(G).animate({right:-1},"fast",function(){jq(this).addClass("hidden")});I=true}F=false};var B=function(J){var K=jq(E).width()-(I?C:0);if(K<J){if(!I){H();F=true}}else{if(F){H();F=false}}};jq(G).hover(function(){jq(this).addClass("hover")},function(){jq(this).removeClass("hover")}).draggable({start:function(J,K){jq("#DD-helper").show()},stop:function(J,K){x=K.position.left;w=jq(window).width();x=Math.min(w-100,x);x=Math.max(w-400,x);D(w-x-border_tweak);jq("#DD-helper").hide();return false},click:function(){H()},drag:function(J,K){x=K.position.left;w=jq(window).width();x=Math.min(w-100,x);x=Math.max(w-400,x);if(I){jq(A).css("right",0);jq(G).removeClass("hidden");I=false}D(w-x-border_tweak);K.position.left=x;K.position.top=$(this).data("draggable").originalPosition.top}}).find("div").show();return{handle_minwidth_hint:B}}function hide_modal(){$(".dialog-box-container").fadeOut(function(){$("#overlay").hide()})}function show_modal(E,B,D,C){$(".dialog-box").find(".title").html(E);var A=$(".dialog-box").find(".buttons").html("");if(D){$.each(D,function(F,G){A.append($("<button/>").text(F).click(G));A.append(" ")});A.show()}else{A.hide()}var A=$(".dialog-box").find(".extra_buttons").html("");if(C){$.each(C,function(F,G){A.append($("<button/>").text(F).click(G));A.append(" ")});A.show()}else{A.hide()}if(B=="progress"){B=$("<img src='../images/yui/rel_interstitial_loading.gif')' />")}$(".dialog-box").find(".body").html(B);if(!$(".dialog-box-container").is(":visible")){$("#overlay").show();$(".dialog-box-container").fadeIn()}}function make_popupmenu(D,B){var A=$("<div class='popupmenu'><div class='popupmenu-top'><div class='popupmenu-top-inner'/></div></div>").appendTo("body");$.each(B,function(G,F){$("<div class='popupmenu-item' />").html(G).click(F).appendTo(A)});var C=function(){$(A).unbind().hide();$("#popup-helper").unbind().hide()};var E=function(){var F=$(D).offset();$("#popup-helper").mousedown(C).show();$(A).click(C).css({top:-1000}).show().css({top:F.top+$(D).height()+9,left:F.left+$(D).width()-$(A).width()})};$(D).click(E)};
|
||||
var hidden_width=7;var border_tweak=9;var jq=jQuery;function ensure_dd_helper(){if(jq("#DD-helper").length==0){$("<div id='DD-helper'/>").css({background:"white",opacity:0,zIndex:9000,position:"absolute",top:0,left:0,width:"100%",height:"100%"}).appendTo("body").hide()}}function make_left_panel(F,B,C){var E=false;var D=null;resize=function(G){var H=G;if(G<0){G=0}jq(F).css("width",G);jq(C).css("left",H);jq(B).css("left",G+7);if(document.recalc){document.recalc()}};toggle=function(){if(E){jq(C).removeClass("hover");jq(C).animate({left:D},"fast");jq(F).css("left",-D).show().animate({left:0},"fast",function(){resize(D);jq(C).removeClass("hidden")});E=false}else{D=jq(C).position().left;jq(B).css("left",hidden_width);if(document.recalc){document.recalc()}jq(C).removeClass("hover");jq(F).animate({left:-D},"fast");jq(C).animate({left:-1},"fast",function(){jq(this).addClass("hidden")});E=true}};jq(C).hover(function(){jq(this).addClass("hover")},function(){jq(this).removeClass("hover")}).draggable({start:function(G,H){jq("#DD-helper").show()},stop:function(G,H){jq("#DD-helper").hide();return false},drag:function(G,H){x=H.position.left;x=Math.min(400,Math.max(100,x));if(E){jq(F).css("left",0);jq(C).removeClass("hidden");E=false}resize(x);H.position.left=x;H.position.top=$(this).data("draggable").originalPosition.top},click:function(){toggle()}}).find("div").show();var A=function(G){if((E&&G=="show")||(!E&&G=="hide")){toggle()}};return{force_panel:A}}function make_right_panel(A,E,H){var J=false;var G=false;var C=null;var D=function(K){jq(A).css("width",K);jq(E).css("right",K+9);jq(H).css("right",K).css("left","");if(document.recalc){document.recalc()}};var I=function(){if(J){jq(H).removeClass("hover");jq(H).animate({right:C},"fast");jq(A).css("right",-C).show().animate({right:0},"fast",function(){D(C);jq(H).removeClass("hidden")});J=false}else{C=jq(document).width()-jq(H).position().left-border_tweak;jq(E).css("right",hidden_width+1);if(document.recalc){document.recalc()}jq(H).removeClass("hover");jq(A).animate({right:-C},"fast");jq(H).animate({right:-1},"fast",function(){jq(this).addClass("hidden")});J=true}G=false};var B=function(K){var L=jq(E).width()-(J?C:0);if(L<K){if(!J){I();G=true}}else{if(G){I();G=false}}};jq(H).hover(function(){jq(this).addClass("hover")},function(){jq(this).removeClass("hover")}).draggable({start:function(K,L){jq("#DD-helper").show()},stop:function(K,L){x=L.position.left;w=jq(window).width();x=Math.min(w-100,x);x=Math.max(w-400,x);D(w-x-border_tweak);jq("#DD-helper").hide();return false},click:function(){I()},drag:function(K,L){x=L.position.left;w=jq(window).width();x=Math.min(w-100,x);x=Math.max(w-400,x);if(J){jq(A).css("right",0);jq(H).removeClass("hidden");J=false}D(w-x-border_tweak);L.position.left=x;L.position.top=$(this).data("draggable").originalPosition.top}}).find("div").show();var F=function(K){if((J&&K=="show")||(!J&&K=="hide")){I()}};return{handle_minwidth_hint:B,force_panel:F}}function hide_modal(){$(".dialog-box-container").fadeOut(function(){$("#overlay").hide()})}function show_modal(E,B,D,C){$(".dialog-box").find(".title").html(E);var A=$(".dialog-box").find(".buttons").html("");if(D){$.each(D,function(F,G){A.append($("<button/>").text(F).click(G));A.append(" ")});A.show()}else{A.hide()}var A=$(".dialog-box").find(".extra_buttons").html("");if(C){$.each(C,function(F,G){A.append($("<button/>").text(F).click(G));A.append(" ")});A.show()}else{A.hide()}if(B=="progress"){B=$("<img src='../images/yui/rel_interstitial_loading.gif')' />")}$(".dialog-box").find(".body").html(B);if(!$(".dialog-box-container").is(":visible")){$("#overlay").show();$(".dialog-box-container").fadeIn()}}function make_popupmenu(D,B){var A=$("<div class='popupmenu'><div class='popupmenu-top'><div class='popupmenu-top-inner'/></div></div>").appendTo("body");$.each(B,function(G,F){$("<div class='popupmenu-item' />").html(G).click(F).appendTo(A)});var C=function(){$(A).unbind().hide();$("#popup-helper").unbind().hide()};var E=function(){var F=$(D).offset();$("#popup-helper").mousedown(C).show();$(A).click(C).css({top:-1000}).show().css({top:F.top+$(D).height()+9,left:F.left+$(D).width()-$(A).width()})};$(D).click(E)};
|
||||
@@ -1 +1 @@
|
||||
$.ui.plugin.add("draggable","scrollPanel",{drag:function(G,H){var J=$(this).data("draggable");clearTimeout(J.timeout);var B=H.options,D=J.element,A=B.panel,C=A.position(),I=A.width(),E=A.height();viewport=A.parent();viewport_w=viewport.width(),viewport_h=viewport.height(),element_w=D.width(),element_h=D.height(),moved=false,close=5,nudge=23,p_min_x=-(I-viewport_w),p_min_y=-(E-viewport_h),p_max_x=0,p_max_y=0,min_vis_x=-C.left,max_vis_x=min_vis_x+viewport_w,min_vis_y=-C.top,max_vis_y=min_vis_y+viewport_h,mouse_x=H.position.left+J.offset.click.left;mouse_y=H.position.top+J.offset.click.top;if((C.left<p_max_x)&&(mouse_x-close<min_vis_x)){var K=Math.min(nudge,p_max_x-C.left);A.css("left",C.left+K);moved=true;J.offset.parent.left+=K;H.position.left-=K}if((!moved)&&(C.left>p_min_x)&&(mouse_x+close>max_vis_x)){var K=Math.min(nudge,C.left-p_min_x);A.css("left",C.left-K);moved=true;J.offset.parent.left-=K;H.position.left+=K}if((!moved)&&(C.top<p_max_y)&&(mouse_y-close<min_vis_y)){var K=Math.min(nudge,p_max_y-C.top);A.css("top",C.top+K);var F=A.position().top-C.top;J.offset.parent.top+=F;H.position.top-=F;moved=true}if((!moved)&&(C.top>p_min_y)&&(mouse_y+close>max_vis_y)){var K=Math.min(nudge,C.top-p_min_x);A.css("top",(C.top-K)+"px");var F=C.top-A.position().top;J.offset.parent.top-=F;H.position.top+=F;moved=true}H.position.left=Math.max(H.position.left,0);H.position.top=Math.max(H.position.top,0);H.position.left=Math.min(H.position.left,I-element_w);H.position.top=Math.min(H.position.top,E-element_h);if(moved){$.ui.ddmanager.prepareOffsets(J,G)}if(moved){J.old_e=G;J.timeout=setTimeout(function(){J.mouseMove(G)},50)}},stop:function(C,B){var A=$(this).data("draggable");clearTimeout(A.timeout)}});
|
||||
$.ui.plugin.add("draggable","scrollPanel",{drag:function(G,H){var J=$(this).data("draggable");clearTimeout(J.timeout);var B=H.options,D=J.element,A=B.panel,C=A.position(),I=A.width(),E=A.height();viewport=A.parent();viewport_w=viewport.width(),viewport_h=viewport.height(),element_w=D.width(),element_h=D.height(),moved=false,close_dist=5,nudge=23,p_min_x=-(I-viewport_w),p_min_y=-(E-viewport_h),p_max_x=0,p_max_y=0,min_vis_x=-C.left,max_vis_x=min_vis_x+viewport_w,min_vis_y=-C.top,max_vis_y=min_vis_y+viewport_h,mouse_x=H.position.left+J.offset.click.left;mouse_y=H.position.top+J.offset.click.top;if((C.left<p_max_x)&&(mouse_x-close_dist<min_vis_x)){var K=Math.min(nudge,p_max_x-C.left);A.css("left",C.left+K);moved=true;J.offset.parent.left+=K;H.position.left-=K}if((!moved)&&(C.left>p_min_x)&&(mouse_x+close_dist>max_vis_x)){var K=Math.min(nudge,C.left-p_min_x);A.css("left",C.left-K);moved=true;J.offset.parent.left-=K;H.position.left+=K}if((!moved)&&(C.top<p_max_y)&&(mouse_y-close_dist<min_vis_y)){var K=Math.min(nudge,p_max_y-C.top);A.css("top",C.top+K);var F=A.position().top-C.top;J.offset.parent.top+=F;H.position.top-=F;moved=true}if((!moved)&&(C.top>p_min_y)&&(mouse_y+close_dist>max_vis_y)){var K=Math.min(nudge,C.top-p_min_x);A.css("top",(C.top-K)+"px");var F=C.top-A.position().top;J.offset.parent.top-=F;H.position.top+=F;moved=true}H.position.left=Math.max(H.position.left,0);H.position.top=Math.max(H.position.top,0);H.position.left=Math.min(H.position.left,I-element_w);H.position.top=Math.min(H.position.top,E-element_h);if(moved){$.ui.ddmanager.prepareOffsets(J,G)}if(moved){J.old_e=G;J.timeout=setTimeout(function(){J.mouseMove(G)},50)}},stop:function(C,B){var A=$(this).data("draggable");clearTimeout(A.timeout)}});
|
||||
@@ -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,125 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
|
||||
<%def name="title()">Galaxy Administration</%def>
|
||||
|
||||
<h2>Administration</h2>
|
||||
|
||||
<p>Choose a task from the menu on the left.</p>
|
||||
<p>
|
||||
<strong>Security</strong> - Data security in Galaxy is a new feature, so familiarize yourself with the details, which can be found
|
||||
here or in our <a href="http://g2.trac.bx.psu.edu/wiki/SecurityFeatures" target="_blank">data security page</a>. The data security
|
||||
process incorporates users, groups and roles, and enables the application of certain restrictions on datasets. The default process
|
||||
is to not apply any security restrictions to datasets, and data becomes more restricted with each new "permission restriction" applied
|
||||
to it. With no restrictions, users should not see any difference in the way Galaxy has always worked. The general definitions of
|
||||
these entities are:
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Users</strong> - registered Galaxy users that have created a Galaxy account. Users can be members of a group and can
|
||||
be associated with 1 or more roles. If a user is not authenticated during a Galaxy session, they will not have access to any
|
||||
of the security features, and datasets they create during that session will have no security restrictions applied to them
|
||||
(i.e., they will be considered "public").
|
||||
</li>
|
||||
<p></p>
|
||||
<li>
|
||||
<strong>Groups</strong> - a set of 0 or more users which are considered members of the group. Groups can be associated with 0
|
||||
or more roles, simplifying the process of applying "permission restrictions" to the data between a select group of users.
|
||||
</li>
|
||||
<p></p>
|
||||
<li>
|
||||
<strong>Roles</strong> - associate users and groups with specific permissions on datasets. For example, users in groups A and B
|
||||
can be associated with role C which gives them the "access" permission on datasets D, E and F. Roles have a type which is currently
|
||||
one of the following:
|
||||
<ul>
|
||||
<li>
|
||||
<strong>private</strong> - every user is associated automatically with their own private role, and administrators cannot
|
||||
manage them.
|
||||
</li>
|
||||
<li>
|
||||
<strong>user</strong> - this is currently not used, but eventually any registered user will be able to create a new role
|
||||
and this will be it's type.
|
||||
</li>
|
||||
<li>
|
||||
<strong>sharing</strong> - a role created automatically during a Galaxy session that enables a user to share data with
|
||||
another user. This can generally be considered a temporary role, but this role type is evolving.
|
||||
</li>
|
||||
<li><strong>admin</strong> - a role created by a Galaxy administrator.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<p></p>
|
||||
<li>
|
||||
<strong>Permissions</strong> - for any dataset, applying one of the following role "permission restrictions" will restrict the
|
||||
use of the dataset.
|
||||
<ul>
|
||||
<li>
|
||||
<strong>access</strong> - users associated with the role can import this dataset into their history for analysis.
|
||||
<p>
|
||||
If no roles with the 'access' permission restriction are associated with a dataset, the dataset is "public" and may
|
||||
be shared with anyone. Public library datasets will be accessible to all users (as well as anyone not logged in
|
||||
during a Galaxy session) from the list of libraries displayed when the "Access Libraries stored locally" tool is used.
|
||||
</p>
|
||||
<p>
|
||||
Associating a dataset with a role that includes the "access" permission restriction narrows the set of users that can
|
||||
access it. For example, if 'Role A' includes the "access" permission restriction and 'Role A' is associated with the dataset,
|
||||
only those users and groups who are associated with 'Role A' may access the dataset.
|
||||
</p>
|
||||
<p>
|
||||
If multiple roles that include the "access" permission restriction are associated with a dataset, access to the
|
||||
dataset is derived from the intersection of the users associated with the roles. For example, if 'Role A' and 'Role B' are
|
||||
associated with a dataset, only those users and groups who are associated with both 'Role A' AND 'Role B' may access the dataset.
|
||||
</p>
|
||||
<p>
|
||||
In order for a user to make a dataset private to just themselves, they should associate the dataset with their private role
|
||||
(the role identical to their Galaxy user name / email address). Associating additional roles that include the 'access'
|
||||
permission restriction is not advised since the behavior will probably not be as expected. Role permission restrictions
|
||||
are always logically "ANDed" together, so only the user will be able to access the dataset since they are the only member of
|
||||
their "private role". To make a dataset private to themselves and one or more other users, the user should create a new role
|
||||
(note: this functionality is still under development for non administrator users) and associate the dataset with that role,
|
||||
not their "private role".
|
||||
</p>
|
||||
<p>
|
||||
Private data (data associated with roles that include the "access" permission restriction) must be made public in order
|
||||
to be used with external sources, such as the "view at UCSC" link, or the "Perform genome analysis and prediction with EpiGRAPH"
|
||||
tool. Being "made public" means removing the association of all roles that include the "access" permission restriction from
|
||||
the dataset.
|
||||
<p>
|
||||
</li>
|
||||
<li><strong>edit metadata</strong> - users associated with the role can edit this dataset's metadata in the dataset library.</li>
|
||||
<li>
|
||||
<strong>manage permissions</strong> - users associated with the role can manage the roles associated with this dataset. If no
|
||||
roles that include the 'manage permissions' are associated with the dataset, only administrators can modify it's permission
|
||||
restrictions.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p>The menu on the left provides the following features</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Manage users</strong> - provides a view of the registered users and all groups and non-private roles associated
|
||||
with each user.
|
||||
</li>
|
||||
<p></p>
|
||||
<li>
|
||||
<strong>Manage groups</strong> - provides a view of all groups along with the members of the group and the roles associated with
|
||||
each group (both private and non-private roles). Non-private roles include a link to a page that allows you to manage the users
|
||||
and groups that are associated with the role. The page also includes a view of the library datasets that are associated with the
|
||||
role and the various "permission restrictions" applied to each dataset.
|
||||
</li>
|
||||
<p></p>
|
||||
<li>
|
||||
<strong>Manage non-private roles</strong> - provides a view of all non-private roles along with the role type, and the users and groups that
|
||||
are associated with the role.
|
||||
</li>
|
||||
<p></p>
|
||||
<li>
|
||||
<strong>Manage libraries</strong> - Dataset libraries enable a Galaxy administrator to upload datasets into a library. Only
|
||||
administrators can create dataset libraries and maintain their contents ( datasets ) and the security applied to them. From
|
||||
the Galaxy analysis view, the "Access Libraries stored locally" tool in the "Get Data" tool
|
||||
section allows users to access the datasets in a library ( if the user is not restricted from accessing the datasets by the
|
||||
security rules applied to them ) by "uploading" them into their histories. This "uploading" process will not make a copy of
|
||||
the dataset, however, but will be a sort of pointer to the dataset on disk. This approach allows for multiple users to have
|
||||
access to a single ( possibly very large ) dataset on disk.
|
||||
</li>
|
||||
</ul>
|
||||
<p></p><p></p>
|
||||
@@ -0,0 +1,112 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
## Render a row
|
||||
<%def name="render_row( group, members, roles, ctr, anchored, curr_anchor )">
|
||||
%if ctr % 2 == 1:
|
||||
<tr class="odd_row">
|
||||
%else:
|
||||
<tr>
|
||||
%endif
|
||||
<td>
|
||||
${group.name}
|
||||
<a id="group-${group.id}-popup" class="popup-arrow" style="display: none;">▼</a>
|
||||
<div popupmenu="group-${group.id}-popup">
|
||||
<a class="action-button" href="${h.url_for( action='undelete_group', group_id=group.id )}">Undelete</a>
|
||||
<a class="action-button" href="${h.url_for( action='purge_group', group_id=group.id )}">Purge</a>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
%for user in members:
|
||||
<li>${user.email}</li>
|
||||
%endfor
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
%for role in roles:
|
||||
<li>${role.description}</li>
|
||||
%endfor
|
||||
</ul>
|
||||
%if not anchored:
|
||||
<a name="${curr_anchor}"></a>
|
||||
<div style="float: right;"><a href="#TOP">top</a></div>
|
||||
%endif
|
||||
</td>
|
||||
</tr>
|
||||
</%def>
|
||||
|
||||
<a name="TOP"><h2>Deleted Groups</h2></a>
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
%if len( groups_members_roles ) == 0:
|
||||
There are no deleted Galaxy groups
|
||||
%else:
|
||||
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
|
||||
<%
|
||||
render_quick_find = len( groups_members_roles ) > 50
|
||||
ctr = 0
|
||||
%>
|
||||
%if render_quick_find:
|
||||
<%
|
||||
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
|
||||
anchor_loc = 0
|
||||
anchored = False
|
||||
curr_anchor = 'A'
|
||||
%>
|
||||
<tr style="background: #EEE">
|
||||
<td colspan="3" style="text-align: center; border-bottom: 1px solid #D8B365">
|
||||
Jump to letter:
|
||||
%for a in anchors:
|
||||
| <a href="#${a}">${a}</a>
|
||||
%endfor
|
||||
</td>
|
||||
</tr>
|
||||
%endif
|
||||
<tr class="header">
|
||||
<td>Name</td>
|
||||
<td>Members</td>
|
||||
<td>Roles</td>
|
||||
</tr>
|
||||
%for ctr, group_tuple in enumerate( groups_members_roles ):
|
||||
<%
|
||||
group = group_tuple[0]
|
||||
members = group_tuple[1]
|
||||
roles = group_tuple[2]
|
||||
%>
|
||||
%if render_quick_find and not group.name.upper().startswith( curr_anchor ):
|
||||
<% anchored = False %>
|
||||
%endif
|
||||
%if render_quick_find and group.name.upper().startswith( curr_anchor ):
|
||||
%if not anchored:
|
||||
${render_row( group, members, roles, ctr, anchored, curr_anchor )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_row( group, members, roles, ctr, anchored, curr_anchor )}
|
||||
%endif
|
||||
%elif render_quick_find:
|
||||
%for anchor in anchors[ anchor_loc: ]:
|
||||
%if group.name.upper().startswith( anchor ):
|
||||
%if not anchored:
|
||||
<% curr_anchor = anchor %>
|
||||
${render_row( group, members, roles, ctr, anchored, curr_anchor )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_row( group, members, roles, ctr, anchored, curr_anchor )}
|
||||
%endif
|
||||
<%
|
||||
anchor_loc = anchors.index( anchor )
|
||||
break
|
||||
%>
|
||||
%endif
|
||||
%endfor
|
||||
%else:
|
||||
${render_row( group, members, roles, ctr, True, '' )}
|
||||
%endif
|
||||
%endfor
|
||||
</table>
|
||||
%endif
|
||||
@@ -0,0 +1,112 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
## Render a row
|
||||
<%def name="render_row( role, groups, users, ctr, anchored, curr_anchor )">
|
||||
%if ctr % 2 == 1:
|
||||
<tr class="odd_row">
|
||||
%else:
|
||||
<tr>
|
||||
%endif
|
||||
<td>
|
||||
${role.description}
|
||||
<a id="role-${role.id}-popup" class="popup-arrow" style="display: none;">▼</a>
|
||||
<div popupmenu="role-${role.id}-popup">
|
||||
<a class="action-button" href="${h.url_for( action='undelete_role', role_id=role.id )}">Undelete</a>
|
||||
<a class="action-button" href="${h.url_for( action='purge_role', role_id=role.id )}">Purge</a>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
%for group in groups:
|
||||
<li>${group.name}</li>
|
||||
%endfor
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
%for user in users:
|
||||
<li>${user.email}</li>
|
||||
%endfor
|
||||
</ul>
|
||||
%if not anchored:
|
||||
<a name="${curr_anchor}"></a>
|
||||
<div style="float: right;"><a href="#TOP">top</a></div>
|
||||
%endif
|
||||
</td>
|
||||
</tr>
|
||||
</%def>
|
||||
|
||||
<a name="TOP"><h2>Deleted Roles</h2></a>
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
%if len( roles_groups_users ) == 0:
|
||||
There are no deleted Galaxy roles
|
||||
%else:
|
||||
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
|
||||
<%
|
||||
render_quick_find = len( roles_groups_users ) > 50
|
||||
ctr = 0
|
||||
%>
|
||||
%if render_quick_find:
|
||||
<%
|
||||
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
|
||||
anchor_loc = 0
|
||||
anchored = False
|
||||
curr_anchor = 'A'
|
||||
%>
|
||||
<tr style="background: #EEE">
|
||||
<td colspan="3" style="text-align: center; border-bottom: 1px solid #D8B365">
|
||||
Jump to letter:
|
||||
%for a in anchors:
|
||||
| <a href="#${a}">${a}</a>
|
||||
%endfor
|
||||
</td>
|
||||
</tr>
|
||||
%endif
|
||||
<tr class="header">
|
||||
<td>Name</td>
|
||||
<td>Associated Groups</td>
|
||||
<td>Associated Users</td>
|
||||
</tr>
|
||||
%for ctr, role_tuple in enumerate( roles_groups_users ):
|
||||
<%
|
||||
role = role_tuple[0]
|
||||
groups = role_tuple[1]
|
||||
users = role_tuple[2]
|
||||
%>
|
||||
%if render_quick_find and not role.description.upper().startswith( curr_anchor ):
|
||||
<% anchored = False %>
|
||||
%endif
|
||||
%if render_quick_find and role.description.upper().startswith( curr_anchor ):
|
||||
%if not anchored:
|
||||
${render_row( role, groups, users, ctr, anchored, curr_anchor )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_row( role, groups, users, ctr, anchored, curr_anchor )}
|
||||
%endif
|
||||
%elif render_quick_find:
|
||||
%for anchor in anchors[ anchor_loc: ]:
|
||||
%if role.description.upper().startswith( anchor ):
|
||||
%if not anchored:
|
||||
<% curr_anchor = anchor %>
|
||||
${render_row( role, groups, users, ctr, anchored, curr_anchor )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_row( role, groups, users, ctr, anchored, curr_anchor )}
|
||||
%endif
|
||||
<%
|
||||
anchor_loc = anchors.index( anchor )
|
||||
break
|
||||
%>
|
||||
%endif
|
||||
%endfor
|
||||
%else:
|
||||
${render_row( role, groups, users, ctr, True, '' )}
|
||||
%endif
|
||||
%endfor
|
||||
</table>
|
||||
%endif
|
||||
@@ -0,0 +1,57 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
## Render a role
|
||||
<%def name="render_role( group, role )">
|
||||
<li>
|
||||
%if role and not role.type == trans.app.model.Role.types.PRIVATE:
|
||||
<a href="${h.url_for( action='role', role_id=role.id, edit=True )}">${role.description}</a>
|
||||
<a id="role-${role.id}-popup" class="popup-arrow" style="display: none;">▼</a>
|
||||
<div popupmenu="role-${role.id}-popup">
|
||||
<a class="action-button" href="${h.url_for( controller='admin', action='remove_role_from_group', group_id=group.id, role_id=role.id )}">Remove role from group</a>
|
||||
</div>
|
||||
%elif role:
|
||||
${role.description}
|
||||
%endif
|
||||
</li>
|
||||
</%def>
|
||||
|
||||
## Render a user
|
||||
<%def name="render_user( group, user )">
|
||||
<li>
|
||||
<a href="${h.url_for( controller='admin', action='user', user_id=user.id )}">${user.email}</a>
|
||||
<a id="user-${user.id}-popup" class="popup-arrow" style="display: none;">▼</a>
|
||||
<div popupmenu="user-${user.id}-popup">
|
||||
<a class="action-button" href="${h.url_for( controller='admin', action='remove_user_from_group', user_id=user.id, group_id=group.id )}">Remove user from group</a>
|
||||
</div>
|
||||
</li>
|
||||
</%def>
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
%if len( users ) > 0 or len( roles ) > 0:
|
||||
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
|
||||
<tr class="header">
|
||||
<td>Users associated with ${group.name}</td>
|
||||
<td>Roles associated with ${group.name}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
%for user in users:
|
||||
${render_user( group, user )}
|
||||
%endfor
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
%for role in roles:
|
||||
${render_role( group, role )}
|
||||
%endfor
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
%endif
|
||||
@@ -0,0 +1,115 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
## Render a user row
|
||||
<%def name="render_user_row( user, ctr )">
|
||||
%if ctr % 2 == 1:
|
||||
<tr class="odd_row">
|
||||
%else:
|
||||
<tr>
|
||||
%endif
|
||||
<td><input type="checkbox" name="members" value="${user.id}"/> ${user.email}</td>
|
||||
</tr>
|
||||
</%def>
|
||||
|
||||
## Render a role row
|
||||
<%def name="render_role_row( role, ctr, anchored, curr_anchor )">
|
||||
%if ctr % 2 == 1:
|
||||
<tr class="odd_row">
|
||||
%else:
|
||||
<tr>
|
||||
%endif
|
||||
<td>
|
||||
%if not anchored:
|
||||
<div style="float: right;"><a href="#TOP">top</a></div>
|
||||
<a name="${curr_anchor}"><input type="checkbox" name="roles" value="${role.id}"/> ${role.name}: ${role.description}</a>
|
||||
%else:
|
||||
<input type="checkbox" name="roles" value="${role.id}"/> ${role.name}: ${role.description}
|
||||
%endif
|
||||
</td>
|
||||
</tr>
|
||||
</%def>
|
||||
|
||||
<a name="TOP"><h2>Create Group</h2></a>
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
<form name="group_create" action="${h.url_for( controller='admin', action='new_group' )}" method="post" >
|
||||
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
|
||||
<tr><td colspan="2">Name: <input name="name" type="textfield" value="" size=40"/></td></tr>
|
||||
<%
|
||||
render_quick_find = len( users ) > 50
|
||||
ctr = 0
|
||||
%>
|
||||
%if render_quick_find:
|
||||
<%
|
||||
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
|
||||
anchor_loc = 0
|
||||
anchored = False
|
||||
curr_anchor = 'A'
|
||||
%>
|
||||
<tr style="background: #EEE">
|
||||
<td colspan="2" style="border-bottom: 1px solid #D8B365; text-align: center;">
|
||||
Jump to letter:
|
||||
%for a in anchors:
|
||||
| <a href="#${a}">${a}</a>
|
||||
%endfor
|
||||
</td>
|
||||
</tr>
|
||||
%endif
|
||||
<tr class="header">
|
||||
<td>Select to add users to group</td>
|
||||
<td>Select to associate roles with group</td>
|
||||
</tr>
|
||||
<tr>
|
||||
## Render users
|
||||
<td valign="top">
|
||||
<table border="0" cellspacing="0" cellpadding="0" width="100%">
|
||||
%for ctr, user in enumerate( users ):
|
||||
${render_user_row( user, ctr )}
|
||||
%endfor
|
||||
</table>
|
||||
</td>
|
||||
## Render roles
|
||||
<td valign="top">
|
||||
<% curr_anchor = 'A' %>
|
||||
<table border="0" cellspacing="0" cellpadding="0" width="100%">
|
||||
%for ctr, role in enumerate( roles ):
|
||||
%if render_quick_find and not role.description.upper().startswith( curr_anchor ):
|
||||
<% anchored = False %>
|
||||
%endif
|
||||
%if render_quick_find and role.description.upper().startswith( curr_anchor ):
|
||||
%if not anchored:
|
||||
${render_role_row( role, ctr, anchored, curr_anchor )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_role_row( role, ctr, anchored, curr_anchor )}
|
||||
%endif
|
||||
%elif render_quick_find:
|
||||
%for anchor in anchors[ anchor_loc: ]:
|
||||
%if role.description.upper().startswith( anchor ):
|
||||
%if not anchored:
|
||||
<% curr_anchor = anchor %>
|
||||
${render_role_row( role, ctr, anchored, curr_anchor )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_role_row( role, ctr, anchored, curr_anchor )}
|
||||
%endif
|
||||
<%
|
||||
anchor_loc = anchors.index( anchor )
|
||||
break
|
||||
%>
|
||||
%endif
|
||||
%endfor
|
||||
%else:
|
||||
${render_role_row( role, ctr, True, '' )}
|
||||
%endif
|
||||
%endfor
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td colspan="2"><input type="submit" name="create_group_button" value="Create"/></td></tr>
|
||||
</table>
|
||||
</form>
|
||||
@@ -0,0 +1,101 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
## Render a row
|
||||
<%def name="render_row( user, ctr, anchored, curr_anchor, check )">
|
||||
%if ctr % 2 == 1:
|
||||
<tr class="odd_row">
|
||||
%else:
|
||||
<tr>
|
||||
%endif
|
||||
<td>
|
||||
%if check:
|
||||
<input type="checkbox" name="members" value="${user.id}" checked/> ${user.email}
|
||||
%else:
|
||||
<input type="checkbox" name="members" value="${user.id}"/> ${user.email}
|
||||
%endif
|
||||
%if not anchored:
|
||||
<a name="${curr_anchor}"></a>
|
||||
<div style="float: right;"><a href="#TOP">top</a></div>
|
||||
%endif
|
||||
</td>
|
||||
</tr>
|
||||
</%def>
|
||||
|
||||
<a name="TOP"> </a>
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
%if len( users ) == 0:
|
||||
<tr><td>There are no Galaxy users</td></tr>
|
||||
%else:
|
||||
<form name="group_members_edit" action="${h.url_for( controller='admin', action='group_members_edit' )}" method="post" >
|
||||
<input type="hidden" name="group_id" value="${group.id}" />
|
||||
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
|
||||
<%
|
||||
render_quick_find = len( users ) > 50
|
||||
ctr = 0
|
||||
%>
|
||||
%if render_quick_find:
|
||||
<%
|
||||
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
|
||||
anchor_loc = 0
|
||||
anchored = False
|
||||
curr_anchor = 'A'
|
||||
%>
|
||||
<tr style="background: #EEE">
|
||||
<td colspan="3" style="border-bottom: 1px solid #D8B365; text-align: center;">
|
||||
Jump to letter:
|
||||
%for a in anchors:
|
||||
| <a href="#${a}">${a}</a>
|
||||
%endfor
|
||||
</td>
|
||||
</tr>
|
||||
%endif
|
||||
<tr class="header"><td>Select to add user to ${group.name}</td></tr>
|
||||
%for ctr, user in enumerate( users ):
|
||||
<% check = False %>
|
||||
%for member in members:
|
||||
%if member.email == user.email:
|
||||
<%
|
||||
check = True
|
||||
break
|
||||
%>
|
||||
%endif
|
||||
%endfor
|
||||
%if render_quick_find and not user.email.upper().startswith( curr_anchor ):
|
||||
<% anchored = False %>
|
||||
%endif
|
||||
%if render_quick_find and user.email.upper().startswith( curr_anchor ):
|
||||
%if not anchored:
|
||||
${render_row( user, ctr, anchored, curr_anchor, check )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_row( user, ctr, anchored, curr_anchor, check )}
|
||||
%endif
|
||||
%elif render_quick_find:
|
||||
%for anchor in anchors[ anchor_loc: ]:
|
||||
%if user.email.upper().startswith( anchor ):
|
||||
%if not anchored:
|
||||
<% curr_anchor = anchor %>
|
||||
${render_row( user, ctr, anchored, curr_anchor, check )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_row( user, ctr, anchored, curr_anchor, check )}
|
||||
%endif
|
||||
<%
|
||||
anchor_loc = anchors.index( anchor )
|
||||
break
|
||||
%>
|
||||
%endif
|
||||
%endfor
|
||||
%else:
|
||||
${render_row( user, ctr, True, '', check )}
|
||||
%endif
|
||||
%endfor
|
||||
<tr><td><input type="submit" name="group_members_edit_button" value="Add selected users to group"/></td></tr>
|
||||
</table>
|
||||
</form>
|
||||
%endif
|
||||
@@ -0,0 +1,34 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">Change group name</div>
|
||||
<div class="toolFormBody">
|
||||
<form name="library" action="${h.url_for( controller='admin', action='group' )}" method="post" >
|
||||
<div class="form-row">
|
||||
<label>Name:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="text" name="name" value="${group.name}" size="40"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="hidden" name="rename" value="submitted"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="hidden" name="group_id" value="${group.id}"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<input type="submit" name="rename_group_button" value="Save"/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,110 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
## Render a row
|
||||
<%def name="render_row( role, ctr, anchored, curr_anchor, check )">
|
||||
%if ctr % 2 == 1:
|
||||
<tr class="odd_row">
|
||||
%else:
|
||||
<tr>
|
||||
%endif
|
||||
<td>
|
||||
%if check:
|
||||
<input type="checkbox" name="roles" value="${role.id}" checked/> ${role.name}
|
||||
%else:
|
||||
<input type="checkbox" name="roles" value="${role.id}"/> ${role.name}
|
||||
%endif
|
||||
</td>
|
||||
<td>${role.description}</td>
|
||||
<td>
|
||||
${role.type}
|
||||
%if not anchored:
|
||||
<a name="${curr_anchor}"></a>
|
||||
<div style="float: right;"><a href="#TOP">top</a></div>
|
||||
%endif
|
||||
</td>
|
||||
</tr>
|
||||
</%def>
|
||||
|
||||
<a name="TOP"> </a>
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
%if len( roles ) == 0:
|
||||
<tr><td>There are no Galaxy roles</td></tr>
|
||||
%else:
|
||||
<form name="group_roles_edit" action="${h.url_for( controller='admin', action='group_roles_edit' )}" method="post" >
|
||||
<input type="hidden" name="group_id" value="${group.id}" />
|
||||
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
|
||||
<%
|
||||
render_quick_find = len( roles ) > 50
|
||||
ctr = 0
|
||||
%>
|
||||
%if render_quick_find:
|
||||
<%
|
||||
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
|
||||
anchor_loc = 0
|
||||
anchored = False
|
||||
curr_anchor = 'A'
|
||||
%>
|
||||
<tr style="background: #EEE">
|
||||
<td style="border-bottom: 1px solid #D8B365; text-align: center;">
|
||||
Jump to letter:
|
||||
%for a in anchors:
|
||||
| <a href="#${a}">${a}</a>
|
||||
%endfor
|
||||
</td>
|
||||
</tr>
|
||||
%endif
|
||||
<tr class="header">
|
||||
<td>Select to associate role with ${group.name}</td>
|
||||
<td>Description</td>
|
||||
<td>Role Type</td>
|
||||
</tr>
|
||||
%for ctr, role in enumerate( roles ):
|
||||
<% check = False %>
|
||||
%for group_role in group_roles:
|
||||
%if group_role.name == role.name:
|
||||
<%
|
||||
check = True
|
||||
break
|
||||
%>
|
||||
%endif
|
||||
%endfor
|
||||
%if render_quick_find and not role.description.upper().startswith( curr_anchor ):
|
||||
<% anchored = False %>
|
||||
%endif
|
||||
%if render_quick_find and role.description.upper().startswith( curr_anchor ):
|
||||
%if not anchored:
|
||||
${render_row( role, ctr, anchored, curr_anchor, check )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_row( role, ctr, anchored, curr_anchor, check )}
|
||||
%endif
|
||||
%elif render_quick_find:
|
||||
%for anchor in anchors[ anchor_loc: ]:
|
||||
%if role.description.upper().startswith( anchor ):
|
||||
%if not anchored:
|
||||
<% curr_anchor = anchor %>
|
||||
${render_row( role, ctr, anchored, curr_anchor, check )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_row( role, ctr, anchored, curr_anchor, check )}
|
||||
%endif
|
||||
<%
|
||||
anchor_loc = anchors.index( anchor )
|
||||
break
|
||||
%>
|
||||
%endif
|
||||
%endfor
|
||||
%else:
|
||||
${render_row( role, ctr, True, '', check )}
|
||||
%endif
|
||||
%endfor
|
||||
<tr><td><input type="submit" name="group_roles_edit_button" value="Update role associations" /></td></tr>
|
||||
</table>
|
||||
</form>
|
||||
%endif
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
## Render a row
|
||||
<%def name="render_row( group, members, roles, ctr, anchored, curr_anchor )">
|
||||
%if ctr % 2 == 1:
|
||||
<tr class="odd_row">
|
||||
%else:
|
||||
<tr>
|
||||
%endif
|
||||
<td>
|
||||
<a href="${h.url_for( controller='admin', action='group', group_id=group.id )}">${group.name}</a>
|
||||
<a id="group-${group.id}-popup" class="popup-arrow" style="display: none;">▼</a>
|
||||
<div popupmenu="group-${group.id}-popup">
|
||||
<a class="action-button" href="${h.url_for( controller='admin', action='group', rename=True, group_id=group.id )}">Rename this group</a>
|
||||
<a class="action-button" href="${h.url_for( controller='admin', action='group_members_edit', group_id=group.id )}">Change associated users</a>
|
||||
<a class="action-button" href="${h.url_for( controller='admin', action='group_roles_edit', group_id=group.id )}">Change associated roles</a>
|
||||
<a class="action-button" href="${h.url_for( controller='admin', action='mark_group_deleted', group_id=group.id )}">Mark group deleted</a>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
%for user in members:
|
||||
<li><a href="${h.url_for( controller='admin', action='user', user_id=user.id )}">${user.email}</a></li>
|
||||
%endfor
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
%for role in roles:
|
||||
<li>
|
||||
%if not role.type == trans.app.model.Role.types.PRIVATE:
|
||||
<a href="${h.url_for( controller='admin', action='role', role_id=role.id )}">${role.description}</a>
|
||||
%else:
|
||||
${role.description}
|
||||
%endif
|
||||
</li>
|
||||
%endfor
|
||||
</ul>
|
||||
%if not anchored:
|
||||
<a name="${curr_anchor}"></a>
|
||||
<div style="float: right;"><a href="#TOP">top</a></div>
|
||||
%endif
|
||||
</td>
|
||||
</tr>
|
||||
</%def>
|
||||
|
||||
<a name="TOP"><h2>Groups</h2></a>
|
||||
|
||||
<ul class="manage-table-actions">
|
||||
<li><a class="action-button" href="${h.url_for( controller='admin', action='create_group' )}">Create a new group</a></li>
|
||||
<li><a class="action-button" href="${h.url_for( controller='admin', action='deleted_groups' )}">Manage deleted groups</a></li>
|
||||
</ul>
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
%if len( groups_members_roles ) == 0:
|
||||
There are no Galaxy groups
|
||||
%else:
|
||||
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
|
||||
<%
|
||||
render_quick_find = len( groups_members_roles ) > 50
|
||||
ctr = 0
|
||||
%>
|
||||
%if render_quick_find:
|
||||
<%
|
||||
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
|
||||
anchor_loc = 0
|
||||
anchored = False
|
||||
curr_anchor = 'A'
|
||||
%>
|
||||
<tr style="background: #EEE">
|
||||
<td colspan="3" style="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>Name</td>
|
||||
<td>Associated Users</td>
|
||||
<td>Associated Roles</td>
|
||||
</tr>
|
||||
%for ctr, group_tuple in enumerate( groups_members_roles ):
|
||||
<%
|
||||
group = group_tuple[0]
|
||||
members = group_tuple[1]
|
||||
roles = group_tuple[2]
|
||||
%>
|
||||
%if render_quick_find and not group.name.upper().startswith( curr_anchor ):
|
||||
<% anchored = False %>
|
||||
%endif
|
||||
%if render_quick_find and group.name.upper().startswith( curr_anchor ):
|
||||
%if not anchored:
|
||||
${render_row( group, members, roles, ctr, anchored, curr_anchor )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_row( group, members, roles, ctr, anchored, curr_anchor )}
|
||||
%endif
|
||||
%elif render_quick_find:
|
||||
%for anchor in anchors[ anchor_loc: ]:
|
||||
%if group.name.upper().startswith( anchor ):
|
||||
%if not anchored:
|
||||
<% curr_anchor = anchor %>
|
||||
${render_row( group, members, roles, ctr, anchored, curr_anchor )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_row( group, members, roles, ctr, anchored, curr_anchor )}
|
||||
%endif
|
||||
<%
|
||||
anchor_loc = anchors.index( anchor )
|
||||
break
|
||||
%>
|
||||
%endif
|
||||
%endfor
|
||||
%else:
|
||||
${render_row( group, members, roles, ctr, True, '' )}
|
||||
%endif
|
||||
%endfor
|
||||
</table>
|
||||
%endif
|
||||
@@ -0,0 +1,117 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
<%def name="javascripts()">
|
||||
${parent.javascripts()}
|
||||
<script type="text/javascript">
|
||||
$(function(){
|
||||
$("input:text:first").focus();
|
||||
})
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="render_select( name, options )">
|
||||
<select name="${name}" id="${name}" style="min-width: 250px; height: 150px;" multiple>
|
||||
%for option in options:
|
||||
<option value="${option[0]}">${option[1]}</option>
|
||||
%endfor
|
||||
</select>
|
||||
</%def>
|
||||
|
||||
<script type="text/javascript">
|
||||
$().ready(function() {
|
||||
$('#users_add_button').click(function() {
|
||||
return !$('#out_users option:selected').remove().appendTo('#in_users');
|
||||
});
|
||||
$('#users_remove_button').click(function() {
|
||||
return !$('#in_users option:selected').remove().appendTo('#out_users');
|
||||
});
|
||||
$('#groups_add_button').click(function() {
|
||||
return !$('#out_groups option:selected').remove().appendTo('#in_groups');
|
||||
});
|
||||
$('#groups_remove_button').click(function() {
|
||||
return !$('#in_groups option:selected').remove().appendTo('#out_groups');
|
||||
});
|
||||
$('form#associate_role_user_group').submit(function() {
|
||||
$('#in_users option').each(function(i) {
|
||||
$(this).attr("selected", "selected");
|
||||
});
|
||||
$('#in_groups option').each(function(i) {
|
||||
$(this).attr("selected", "selected");
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">Role '${role.name}'</div>
|
||||
<div class="toolFormBody">
|
||||
<form name="associate_role_user_group" id="associate_role_user_group" action="${h.url_for( action='role', role_id=role.id )}" method="post" >
|
||||
<div class="form-row">
|
||||
<div style="float: left; margin-right: 10px;">
|
||||
Users associated with '${role.name}'<br/>
|
||||
${render_select( "in_users", in_users )}<br/>
|
||||
<input type="submit" id="users_remove_button" value=">>"/>
|
||||
</div>
|
||||
<div>
|
||||
Users not associated with '${role.name}'<br/>
|
||||
${render_select( "out_users", out_users )}<br/>
|
||||
<input type="submit" id="users_add_button" value="<<"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div style="float: left; margin-right: 10px;">
|
||||
Groups associated with '${role.name}'<br/>
|
||||
${render_select( "in_groups", in_groups )}<br/>
|
||||
<input type="submit" id="groups_remove_button" value=">>"/>
|
||||
</div>
|
||||
<div>
|
||||
Groups not associated with '${role.name}'<br/>
|
||||
${render_select( "out_groups", out_groups )}<br/>
|
||||
<input type="submit" id="groups_add_button" value="<<"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<input type="submit" name="role_members_edit_button" value="Save"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<br clear="left"/>
|
||||
<br/>
|
||||
%if len( library_dataset_actions ) > 0:
|
||||
<h3>Library datasets associated with role '${role.name}'</h3>
|
||||
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
%for ctr, library, in enumerate( library_dataset_actions.keys() ):
|
||||
<li>
|
||||
<img src="${h.url_for( '/static/images/silk/book_open.png' )}" class="rowIcon"/>
|
||||
${library.name}
|
||||
<ul>
|
||||
%for folder_path, permissions in library_dataset_actions[ library ].items():
|
||||
<li>
|
||||
<img src="/static/images/silk/folder_page.png" class="rowIcon"/>
|
||||
${folder_path}
|
||||
<ul>
|
||||
% for permission in permissions:
|
||||
<ul>
|
||||
<li>${permission}</li>
|
||||
</ul>
|
||||
%endfor
|
||||
</ul>
|
||||
</li>
|
||||
%endfor
|
||||
</ul>
|
||||
</li>
|
||||
%endfor
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
%endif
|
||||
@@ -0,0 +1,118 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
## Render a user row
|
||||
<%def name="render_user_row( user, ctr )">
|
||||
%if ctr % 2 == 1:
|
||||
<tr class="odd_row">
|
||||
%else:
|
||||
<tr>
|
||||
%endif
|
||||
<td><input type="checkbox" name="users" value="${user.id}"/> ${user.email}</td>
|
||||
</tr>
|
||||
</%def>
|
||||
|
||||
## Render a group row
|
||||
<%def name="render_group_row( group, ctr, anchored, curr_anchor )">
|
||||
%if ctr % 2 == 1:
|
||||
<tr class="odd_row">
|
||||
%else:
|
||||
<tr>
|
||||
%endif
|
||||
<td>
|
||||
%if not anchored:
|
||||
<div style="float: right;"><a href="#TOP">top</a></div>
|
||||
<a name="${curr_anchor}"><input type="checkbox" name="groups" value="${group.id}"/> ${group.name}</a>
|
||||
%else:
|
||||
<input type="checkbox" name="groups" value="${group.id}"/> ${group.name}
|
||||
%endif
|
||||
</td>
|
||||
</tr>
|
||||
</%def>
|
||||
|
||||
<a name="TOP"><h2>Create Role</h2></a>
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
<form name="role_create" action="${h.url_for( controller='admin', action='new_role' )}" method="post" >
|
||||
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
|
||||
<tr>
|
||||
<td>Name: <input name="name" type="textfield" value="" size=40"/></td>
|
||||
<td>Description: <input name="description" type="textfield" value="" size=40"/></td>
|
||||
</tr>
|
||||
<%
|
||||
render_quick_find = len( users ) > 50
|
||||
ctr = 0
|
||||
%>
|
||||
%if render_quick_find:
|
||||
<%
|
||||
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
|
||||
anchor_loc = 0
|
||||
anchored = False
|
||||
curr_anchor = 'A'
|
||||
%>
|
||||
<tr style="background: #EEE">
|
||||
<td colspan="2" style="border-bottom: 1px solid #D8B365; text-align: center;">
|
||||
Jump to letter:
|
||||
%for a in anchors:
|
||||
| <a href="#${a}">${a}</a>
|
||||
%endfor
|
||||
</td>
|
||||
</tr>
|
||||
%endif
|
||||
<tr class="header">
|
||||
<td>Select to associate users with role</td>
|
||||
<td>Select to associate groups with role</td>
|
||||
</tr>
|
||||
<tr>
|
||||
## Render users
|
||||
<td valign="top">
|
||||
<table border="0" cellspacing="0" cellpadding="0" width="100%">
|
||||
%for ctr, user in enumerate( users ):
|
||||
${render_user_row( user, ctr )}
|
||||
%endfor
|
||||
</table>
|
||||
</td>
|
||||
## Render roles
|
||||
<td valign="top">
|
||||
<% curr_anchor = 'A' %>
|
||||
<table border="0" cellspacing="0" cellpadding="0" width="100%">
|
||||
%for ctr, group in enumerate( groups ):
|
||||
%if render_quick_find and not group.name.upper().startswith( curr_anchor ):
|
||||
<% anchored = False %>
|
||||
%endif
|
||||
%if render_quick_find and group.name.upper().startswith( curr_anchor ):
|
||||
%if not anchored:
|
||||
${render_group_row( group, ctr, anchored, curr_anchor )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_group_row( group, ctr, anchored, curr_anchor )}
|
||||
%endif
|
||||
%elif render_quick_find:
|
||||
%for anchor in anchors[ anchor_loc: ]:
|
||||
%if group.name.upper().startswith( anchor ):
|
||||
%if not anchored:
|
||||
<% curr_anchor = anchor %>
|
||||
${render_group_row( group, ctr, anchored, curr_anchor )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_group_row( group, ctr, anchored, curr_anchor )}
|
||||
%endif
|
||||
<%
|
||||
anchor_loc = anchors.index( anchor )
|
||||
break
|
||||
%>
|
||||
%endif
|
||||
%endfor
|
||||
%else:
|
||||
${render_group_row( group, ctr, True, '' )}
|
||||
%endif
|
||||
%endfor
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td colspan="2"><input type="submit" name="create_role_button" value="Create"/></td></tr>
|
||||
</table>
|
||||
</form>
|
||||
@@ -0,0 +1,41 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">Change role name and description</div>
|
||||
<div class="toolFormBody">
|
||||
<form name="library" action="${h.url_for( controller='admin', action='role' )}" method="post" >
|
||||
<div class="form-row">
|
||||
<label>Name:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="text" name="name" value="${role.name}" size="40"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>Description:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input name="description" type="textfield" value="${role.description}" size=40"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="hidden" name="rename" value="submitted"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="hidden" name="role_id" value="${role.id}"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<input type="submit" name="rename_role_button" value="Save"/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,129 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
## Render a row
|
||||
<%def name="render_row( role, ctr, anchored, curr_anchor )">
|
||||
%if ctr % 2 == 1:
|
||||
<tr class="odd_row">
|
||||
%else:
|
||||
<tr>
|
||||
%endif
|
||||
<td>
|
||||
${role.name}
|
||||
<a id="role-${role.id}-popup" class="popup-arrow" style="display: none;">▼</a>
|
||||
<div popupmenu="role-${role.id}-popup">
|
||||
<a class="action-button" href="${h.url_for( controller='admin', action='role', rename=True, role_id=role.id )}">Rename this role</a>
|
||||
<a class="action-button" href="${h.url_for( controller='admin', action='role', role_id=role.id )}">Change associated users and groups</a>
|
||||
<a class="action-button" href="${h.url_for( controller='admin', action='mark_role_deleted', role_id=role.id )}">Mark role deleted</a>
|
||||
</div>
|
||||
</td>
|
||||
<td>${role.description}</td>
|
||||
<td>${role.type}</td>
|
||||
<td>
|
||||
<ul>
|
||||
%for ura in role.users:
|
||||
<li>
|
||||
<a href="${h.url_for( controller='admin', action='user', user_id=ura.user.id )}">${ura.user.email}</a>
|
||||
<a id="user-${ura.user.id}-popup" class="popup-arrow" style="display: none;">▼</a>
|
||||
<div popupmenu="user-${ura.user.id}-popup">
|
||||
<a class="action-button" href="${h.url_for( controller='admin', action='remove_role_from_user', role_id=role.id, user_id=ura.user.id )}">Remove user from role</a>
|
||||
</div>
|
||||
</li>
|
||||
%endfor
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
%for gra in role.groups:
|
||||
<li>
|
||||
<a href="${h.url_for( controller='admin', action='group', group_id=gra.group.id )}">${gra.group.name}</a>
|
||||
<a id="group-${gra.group.id}-popup" class="popup-arrow" style="display: none;">▼</a>
|
||||
<div popupmenu="group-${gra.group.id}-popup">
|
||||
<a class="action-button" href="${h.url_for( controller='admin', action='remove_group_from_role', role_id=role.id, group_id=gra.group.id )}">Remove group from role</a>
|
||||
</div>
|
||||
</li>
|
||||
%endfor
|
||||
</ul>
|
||||
%if not anchored:
|
||||
<a name="${curr_anchor}"></a>
|
||||
<div style="float: right;"><a href="#TOP">top</a></div>
|
||||
%endif
|
||||
</td>
|
||||
</tr>
|
||||
</%def>
|
||||
|
||||
<a name="TOP"><h2>Roles</h2></a>
|
||||
|
||||
<ul class="manage-table-actions">
|
||||
<li><a class="action-button" href="${h.url_for( controller='admin', action='create_role' )}">Create a new role</a></li>
|
||||
<li><a class="action-button" href="${h.url_for( controller='admin', action='deleted_roles' )}">Manage deleted roles</a></li>
|
||||
</ul>
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
%if len( roles ) == 0:
|
||||
There are no Galaxy roles
|
||||
%else:
|
||||
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
|
||||
<%
|
||||
render_quick_find = len( roles ) > 50
|
||||
ctr = 0
|
||||
%>
|
||||
%if render_quick_find:
|
||||
<%
|
||||
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
|
||||
anchor_loc = 0
|
||||
anchored = False
|
||||
curr_anchor = 'A'
|
||||
%>
|
||||
<tr style="background: #EEE">
|
||||
<td colspan="4" style="text-align: center; border-bottom: 1px solid #D8B365">
|
||||
Jump to letter:
|
||||
%for a in anchors:
|
||||
| <a href="#${a}">${a}</a>
|
||||
%endfor
|
||||
</td>
|
||||
</tr>
|
||||
%endif
|
||||
<tr class="header">
|
||||
<td>Name</td>
|
||||
<td>Description</td>
|
||||
<td>Type</td>
|
||||
<td>Associated Users</td>
|
||||
<td>Associated Groups</td>
|
||||
</tr>
|
||||
%for ctr, role in enumerate( roles ):
|
||||
%if render_quick_find and not role.name.upper().startswith( curr_anchor ):
|
||||
<% anchored = False %>
|
||||
%endif
|
||||
%if render_quick_find and role.name.upper().startswith( curr_anchor ):
|
||||
%if not anchored:
|
||||
${render_row( role, ctr, anchored, curr_anchor )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_row( role, ctr, anchored, curr_anchor )}
|
||||
%endif
|
||||
%elif render_quick_find:
|
||||
%for anchor in anchors[ anchor_loc: ]:
|
||||
%if role.name.upper().startswith( anchor ):
|
||||
%if not anchored:
|
||||
<% curr_anchor = anchor %>
|
||||
${render_row( role, ctr, anchored, curr_anchor )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_row( role, ctr, anchored, curr_anchor )}
|
||||
%endif
|
||||
<%
|
||||
anchor_loc = anchors.index( anchor )
|
||||
break
|
||||
%>
|
||||
%endif
|
||||
%endfor
|
||||
%else:
|
||||
${render_row( role, ctr, True, '' )}
|
||||
%endif
|
||||
%endfor
|
||||
</table>
|
||||
%endif
|
||||
@@ -0,0 +1,57 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
## Render a role
|
||||
<%def name="render_role( user, role )">
|
||||
<li>
|
||||
%if role and not role.type == trans.app.model.Role.types.PRIVATE:
|
||||
<a href="${h.url_for( action='role', role_id=role.id, edit=True )}">${role.description}</a>
|
||||
<a id="role-${role.id}-popup" class="popup-arrow" style="display: none;">▼</a>
|
||||
<div popupmenu="role-${role.id}-popup">
|
||||
<a class="action-button" href="${h.url_for( controller='admin', action='remove_user_from_role', user_id=user.id, role_id=role.id )}">Remove user from role</a>
|
||||
</div>
|
||||
%elif role:
|
||||
${role.description}
|
||||
%endif
|
||||
</li>
|
||||
</%def>
|
||||
|
||||
## Render a group
|
||||
<%def name="render_group( user, group )">
|
||||
<li>
|
||||
<a href="${h.url_for( controller='admin', action='group_members_edit', group_id=group.id )}">${group.name}</a>
|
||||
<a id="group-${group.id}-popup" class="popup-arrow" style="display: none;">▼</a>
|
||||
<div popupmenu="group-${group.id}-popup">
|
||||
<a class="action-button" href="${h.url_for( controller='admin', action='remove_user_from_group', user_id=user.id, group_id=group.id )}">Remove user from group</a>
|
||||
</div>
|
||||
</li>
|
||||
</%def>
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
%if len( groups ) > 0 or len( roles ) > 0:
|
||||
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
|
||||
<tr class="header">
|
||||
<td>Groups of which ${user.email} is a member</td>
|
||||
<td>Roles associated with ${user.email}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
%for group in groups:
|
||||
${render_group( user, group )}
|
||||
%endfor
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
%for role in roles:
|
||||
${render_role( user, role )}
|
||||
%endfor
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
%endif
|
||||
@@ -0,0 +1,100 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
## Render a row
|
||||
<%def name="render_row( group, ctr, anchored, curr_anchor, check )">
|
||||
%if ctr % 2 == 1:
|
||||
<tr class="odd_row">
|
||||
%else:
|
||||
<tr>
|
||||
%endif
|
||||
<td>
|
||||
%if check:
|
||||
<input type="checkbox" name="groups" value="${group.id}" checked/> ${group.name}
|
||||
%else:
|
||||
<input type="checkbox" name="groups" value="${group.id}"/> ${group.name}
|
||||
%endif
|
||||
%if not anchored:
|
||||
<a name="${curr_anchor}"></a>
|
||||
<div style="float: right;"><a href="#TOP">top</a></div>
|
||||
%endif
|
||||
</td>
|
||||
</tr>
|
||||
</%def>
|
||||
|
||||
<a name="TOP"> </a>
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
%if len( groups ) == 0:
|
||||
<tr><td>There are no Galaxy groups</td></tr>
|
||||
%else:
|
||||
<form name="update_user_groups" action="${h.url_for( controller='admin', action='update_user_groups', user_id=user.id )}" method="post" >
|
||||
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
|
||||
<%
|
||||
render_quick_find = len( groups ) > 50
|
||||
ctr = 0
|
||||
%>
|
||||
%if render_quick_find:
|
||||
<%
|
||||
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
|
||||
anchor_loc = 0
|
||||
anchored = False
|
||||
curr_anchor = 'A'
|
||||
%>
|
||||
<tr style="background: #EEE">
|
||||
<td colspan="3" style="border-bottom: 1px solid #D8B365; text-align: center;">
|
||||
Jump to letter:
|
||||
%for a in anchors:
|
||||
| <a href="#${a}">${a}</a>
|
||||
%endfor
|
||||
</td>
|
||||
</tr>
|
||||
%endif
|
||||
<tr class="header"><td>Select to add ${user.email} to group</td></tr>
|
||||
%for ctr, group in enumerate( groups ):
|
||||
<% check = False %>
|
||||
%for user_group in user_groups:
|
||||
%if user_group.id == group.id:
|
||||
<%
|
||||
check = True
|
||||
break
|
||||
%>
|
||||
%endif
|
||||
%endfor
|
||||
%if render_quick_find and not group.name.upper().startswith( curr_anchor ):
|
||||
<% anchored = False %>
|
||||
%endif
|
||||
%if render_quick_find and group.name.upper().startswith( curr_anchor ):
|
||||
%if not anchored:
|
||||
${render_row( group, ctr, anchored, curr_anchor, check )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_row( group, ctr, anchored, curr_anchor, check )}
|
||||
%endif
|
||||
%elif render_quick_find:
|
||||
%for anchor in anchors[ anchor_loc: ]:
|
||||
%if group.name.upper().startswith( anchor ):
|
||||
%if not anchored:
|
||||
<% curr_anchor = anchor %>
|
||||
${render_row( group, ctr, anchored, curr_anchor, check )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_row( group, ctr, anchored, curr_anchor, check )}
|
||||
%endif
|
||||
<%
|
||||
anchor_loc = anchors.index( anchor )
|
||||
break
|
||||
%>
|
||||
%endif
|
||||
%endfor
|
||||
%else:
|
||||
${render_row( group, ctr, True, '', check )}
|
||||
%endif
|
||||
%endfor
|
||||
<tr><td><input type="submit" name="user_groups_edit_button" value="Add user to selected groups" /></td></tr>
|
||||
</table>
|
||||
</form>
|
||||
%endif
|
||||
@@ -0,0 +1,101 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
## Render a row
|
||||
<%def name="render_row( role, ctr, anchored, curr_anchor, check )">
|
||||
%if ctr % 2 == 1:
|
||||
<tr class="odd_row">
|
||||
%else:
|
||||
<tr>
|
||||
%endif
|
||||
<td>
|
||||
%if check:
|
||||
<input type="checkbox" name="roles" value="${role.id}" checked/> ${role.name}
|
||||
%else:
|
||||
<input type="checkbox" name="roles" value="${role.id}"/> ${role.name}
|
||||
%endif
|
||||
%if not anchored:
|
||||
<a name="${curr_anchor}"></a>
|
||||
<div style="float: right;"><a href="#TOP">top</a></div>
|
||||
%endif
|
||||
</td>
|
||||
</tr>
|
||||
</%def>
|
||||
|
||||
<a name="TOP"> </a>
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
%if len( roles ) == 0:
|
||||
<tr><td>There are no Galaxy roles</td></tr>
|
||||
%else:
|
||||
<form name="user_roles_edit" action="${h.url_for( controller='admin', action='user_roles_edit' )}" method="post" >
|
||||
<input type="hidden" name="user_id" value="${user.id}" />
|
||||
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
|
||||
<%
|
||||
render_quick_find = len( roles ) > 50
|
||||
ctr = 0
|
||||
%>
|
||||
%if render_quick_find:
|
||||
<%
|
||||
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
|
||||
anchor_loc = 0
|
||||
anchored = False
|
||||
curr_anchor = 'A'
|
||||
%>
|
||||
<tr style="background: #EEE">
|
||||
<td colspan="3" style="border-bottom: 1px solid #D8B365; text-align: center;">
|
||||
Jump to letter:
|
||||
%for a in anchors:
|
||||
| <a href="#${a}">${a}</a>
|
||||
%endfor
|
||||
</td>
|
||||
</tr>
|
||||
%endif
|
||||
<tr class="header"><td>Select to associate role with ${user.email}</td></tr>
|
||||
%for ctr, role in enumerate( roles ):
|
||||
<% check = False %>
|
||||
%for user_role in user_roles:
|
||||
%if user_role.id == role.id:
|
||||
<%
|
||||
check = True
|
||||
break
|
||||
%>
|
||||
%endif
|
||||
%endfor
|
||||
%if render_quick_find and not role.name.upper().startswith( curr_anchor ):
|
||||
<% anchored = False %>
|
||||
%endif
|
||||
%if render_quick_find and role.name.upper().startswith( curr_anchor ):
|
||||
%if not anchored:
|
||||
${render_row( role, ctr, anchored, curr_anchor, check )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_row( role, ctr, anchored, curr_anchor, check )}
|
||||
%endif
|
||||
%elif render_quick_find:
|
||||
%for anchor in anchors[ anchor_loc: ]:
|
||||
%if role.name.upper().startswith( anchor ):
|
||||
%if not anchored:
|
||||
<% curr_anchor = anchor %>
|
||||
${render_row( role, ctr, anchored, curr_anchor, check )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_row( role, ctr, anchored, curr_anchor, check )}
|
||||
%endif
|
||||
<%
|
||||
anchor_loc = anchors.index( anchor )
|
||||
break
|
||||
%>
|
||||
%endif
|
||||
%endfor
|
||||
%else:
|
||||
${render_row( role, ctr, True, '', check )}
|
||||
%endif
|
||||
%endfor
|
||||
<tr><td><input type="submit" name="user_roles_edit_button" value="Associate user with selected roles" /></td></tr>
|
||||
</table>
|
||||
</form>
|
||||
%endif
|
||||
@@ -0,0 +1,123 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
## Render a row
|
||||
<%def name="render_row( user, groups, roles, ctr, anchored, curr_anchor )">
|
||||
%if ctr % 2 == 1:
|
||||
<tr class="odd_row">
|
||||
%else:
|
||||
<tr>
|
||||
%endif
|
||||
<td>
|
||||
<a href="${h.url_for( controller='admin', action='user', user_id=user.id )}">${user.email}</a>
|
||||
<a id="user-${user.id}-popup" class="popup-arrow" style="display: none;">▼</a>
|
||||
<div popupmenu="user-${user.id}-popup">
|
||||
<a class="action-button" href="${h.url_for( action='reset_user_password', user_id=user.id )}">Reset password</a>
|
||||
<a class="action-button" href="${h.url_for( action='user_groups_edit', user_id=user.id )}">Change associated groups</a>
|
||||
<a class="action-button" href="${h.url_for( action='user_roles_edit', user_id=user.id )}">Change associated roles</a>
|
||||
%if allow_user_deletion:
|
||||
<a class="action-button" href="${h.url_for( action='mark_user_deleted', user_id=user.id )}">Mark user deleted</a>
|
||||
%endif
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
%for group in groups:
|
||||
<li><a href="${h.url_for( controller='admin', action='group', group_id=group.id )}">${group.name}</a></li>
|
||||
%endfor
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
%for role in roles:
|
||||
<li><a href="${h.url_for( controller='admin', action='role', role_id=role.id )}">${role.description}</a></li>
|
||||
%endfor
|
||||
</ul>
|
||||
%if not anchored:
|
||||
<a name="${curr_anchor}"></a>
|
||||
<div style="float: right;"><a href="#TOP">top</a></div>
|
||||
%endif
|
||||
</td>
|
||||
</tr>
|
||||
</%def>
|
||||
|
||||
<a name="TOP"><h2>Users</h2></a>
|
||||
|
||||
<ul class="manage-table-actions">
|
||||
<li><a class="action-button" href="${h.url_for( controller='admin', action='create_new_user' )}">Create a new user</a></li>
|
||||
%if allow_user_deletion:
|
||||
<li><a class="action-button" href="${h.url_for( controller='admin', action='deleted_users' )}">Manage deleted users</a></li>
|
||||
%endif
|
||||
</ul>
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
%if len( users_groups_roles ) == 0:
|
||||
There are no Galaxy users
|
||||
%else:
|
||||
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
|
||||
<%
|
||||
render_quick_find = len( users_groups_roles ) > 50
|
||||
ctr = 0
|
||||
%>
|
||||
%if render_quick_find:
|
||||
<%
|
||||
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
|
||||
anchor_loc = 0
|
||||
anchored = False
|
||||
curr_anchor = 'A'
|
||||
%>
|
||||
<tr style="background: #EEE">
|
||||
<td colspan="3" style="border-bottom: 1px solid #D8B365; text-align: center;">
|
||||
Jump to letter:
|
||||
%for a in anchors:
|
||||
| <a href="#${a}">${a}</a>
|
||||
%endfor
|
||||
</td>
|
||||
</tr>
|
||||
%endif
|
||||
<tr class="header">
|
||||
<td>Email</td>
|
||||
<td>Associated Groups</td>
|
||||
<td>Associated Roles</td>
|
||||
</tr>
|
||||
%for ctr, user_tuple in enumerate( users_groups_roles ):
|
||||
<%
|
||||
user = user_tuple[0]
|
||||
groups = user_tuple[1]
|
||||
roles = user_tuple[2]
|
||||
%>
|
||||
%if render_quick_find and not user.email.upper().startswith( curr_anchor ):
|
||||
<% anchored = False %>
|
||||
%endif
|
||||
%if render_quick_find and user.email.upper().startswith( curr_anchor ):
|
||||
%if not anchored:
|
||||
${render_row( user, groups, roles, ctr, anchored, curr_anchor )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_row( user, groups, roles, ctr, anchored, curr_anchor )}
|
||||
%endif
|
||||
%elif render_quick_find:
|
||||
%for anchor in anchors[ anchor_loc: ]:
|
||||
%if user.email.upper().startswith( anchor ):
|
||||
%if not anchored:
|
||||
<% curr_anchor = anchor %>
|
||||
${render_row( user, groups, roles, ctr, anchored, curr_anchor )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_row( user, groups, roles, ctr, anchored, curr_anchor )}
|
||||
%endif
|
||||
<%
|
||||
anchor_loc = anchors.index( anchor )
|
||||
break
|
||||
%>
|
||||
%endif
|
||||
%endfor
|
||||
%else:
|
||||
${render_row( user, groups, roles, ctr, True, '' )}
|
||||
%endif
|
||||
%endfor
|
||||
</table>
|
||||
%endif
|
||||
@@ -0,0 +1,114 @@
|
||||
<%inherit file="/base_panels.mako"/>
|
||||
|
||||
<%def name="init()">
|
||||
<%
|
||||
self.has_left_panel=True
|
||||
self.has_right_panel=False
|
||||
self.active_view="admin"
|
||||
%>
|
||||
</%def>
|
||||
|
||||
<%def name="stylesheets()">
|
||||
|
||||
${parent.stylesheets()}
|
||||
|
||||
## TODO: Clean up these styles and move into panel_layout.css (they are
|
||||
## used here and in the editor).
|
||||
|
||||
<style type="text/css">
|
||||
|
||||
#left {
|
||||
background: #C1C9E5 url(${h.url_for('/static/style/menu_bg.png')}) top repeat-x;
|
||||
}
|
||||
|
||||
div.toolMenu {
|
||||
margin: 5px;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
div.toolSectionPad {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 5px;
|
||||
font-size: 0px;
|
||||
}
|
||||
div.toolSectionDetailsInner {
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
div.toolSectionTitle {
|
||||
padding-bottom: 0px;
|
||||
font-weight: bold;
|
||||
}
|
||||
div.toolMenuGroupHeader {
|
||||
font-weight: bold;
|
||||
padding-top: 0.5em;
|
||||
padding-bottom: 0.5em;
|
||||
color: #333;
|
||||
font-style: italic;
|
||||
border-bottom: dotted #333 1px;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
div.toolTitle {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
margin-left: 16px;
|
||||
margin-right: 10px;
|
||||
display: list-item;
|
||||
list-style: square outside;
|
||||
}
|
||||
a:link, a:visited, a:active
|
||||
{
|
||||
color: #303030;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
</%def>
|
||||
|
||||
<%def name="left_panel()">
|
||||
<div class="unified-panel-header" unselectable="on">
|
||||
<div class='unified-panel-header-inner'>Administration</div>
|
||||
</div>
|
||||
<div class="unified-panel-body" style="overflow: auto;">
|
||||
<div class="toolMenu">
|
||||
<div class="toolSectionList">
|
||||
<div class="toolSectionTitle">
|
||||
<span>Security</span>
|
||||
</div>
|
||||
<div class="toolSectionBody">
|
||||
<div class="toolSectionBg">
|
||||
<div class="toolTitle"><a href="${h.url_for( action='users' )}" target="galaxy_main">Manage users</a></div>
|
||||
<div class="toolTitle"><a href="${h.url_for( action='groups' )}" target="galaxy_main">Manage groups</a></div>
|
||||
<div class="toolTitle"><a href="${h.url_for( action='roles' )}" target="galaxy_main">Manage roles</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="toolSectionPad"></div>
|
||||
<div class="toolSectionTitle">
|
||||
<span>Data</span>
|
||||
</div>
|
||||
<div class="toolSectionBody">
|
||||
<div class="toolSectionBg">
|
||||
<div class="toolTitle"><a href="${h.url_for( action='libraries' )}" target="galaxy_main">Manage libraries</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="toolSectionPad"></div>
|
||||
<div class="toolSectionTitle">
|
||||
<span>Server</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 class="toolTitle"><a href="${h.url_for( action='memdump' )}" target="galaxy_main">Profile memory usage</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="center_panel()">
|
||||
|
||||
<iframe name="galaxy_main" id="galaxy_main" frameborder="0" style="position: absolute; width: 100%; height: 100%;" src="${h.url_for( action='center' )}"> </iframe>
|
||||
|
||||
</%def>
|
||||
@@ -0,0 +1,22 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
<%def name="title()">Add Dataset to Library from History</%def>
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">Active datasets in your current history (${history.name})</div>
|
||||
<div class="toolFormBody">
|
||||
<form name="add_dataset_from_history">
|
||||
<input type="hidden" name="folder_id" value="${folder.id}"/>
|
||||
%for dataset in history.active_datasets:
|
||||
<div class="form-row">
|
||||
<input name="ids" value="${dataset.id}" type="checkbox"/>${dataset.hid}: ${dataset.name}
|
||||
</div>
|
||||
%endfor
|
||||
<input type="submit" name="add_dataset_from_history_button" value="Add selected datasets"/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,226 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="common.mako" import="render_dataset" />
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
<%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>
|
||||
|
||||
<%
|
||||
def name_sorted( l ):
|
||||
return sorted( l, lambda a, b: cmp( a.name.lower(), b.name.lower() ) )
|
||||
%>
|
||||
|
||||
<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#action_on_datasets_select option:selected").text() == "delete" ) {
|
||||
if ( confirm( "Click OK to delete these datasets?" ) ) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<%def name="render_folder( parent, parent_pad, deleted, created_lfda_ids )">
|
||||
<%
|
||||
##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
|
||||
|
||||
if created_lfda_ids and not isinstance ( created_lfda_ids, list ):
|
||||
created_lfda_id_list = created_lfda_ids.split( ',' )
|
||||
if created_lfda_id_list:
|
||||
created_lfda_ids = [ int( lfda_id ) for lfda_id in created_lfda_id_list ]
|
||||
%>
|
||||
<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>
|
||||
%if not deleted:
|
||||
<div popupmenu="folder-${parent.id}-popup">
|
||||
<a class="action-button" href="${h.url_for( controller='admin', action='dataset', folder_id=parent.id )}">Add a new dataset to this folder</a>
|
||||
<a class="action-button" href="${h.url_for( controller='admin', action='add_dataset_to_folder_from_history', folder_id=parent.id )}">Copy a dataset from your history to this folder</a>
|
||||
<a class="action-button" href="${h.url_for( controller='admin', action='folder', new=True, id=parent.id )}">Create a new sub-folder in this folder</a>
|
||||
<a class="action-button" href="${h.url_for( controller='admin', action='folder', rename=True, id=parent.id )}">Rename this folder</a>
|
||||
%if subfolder:
|
||||
<a class="action-button" confirm="Click OK to delete the folder '${parent.name}'" href="${h.url_for( action='folder', delete=True, id=parent.id )}">Remove this folder and its contents from the library</a>
|
||||
%endif
|
||||
</div>
|
||||
%endif
|
||||
</li>
|
||||
%if subfolder:
|
||||
<ul id="subFolder">
|
||||
%else:
|
||||
<ul>
|
||||
%endif
|
||||
%if deleted:
|
||||
<%
|
||||
parent_folders = parent.folders
|
||||
parent_datasets = parent.datasets
|
||||
%>
|
||||
%else:
|
||||
<%
|
||||
parent_folders = parent.active_folders
|
||||
parent_datasets = parent.active_datasets
|
||||
%>
|
||||
%endif
|
||||
%for folder in name_sorted( parent_folders ):
|
||||
${render_folder( folder, pad, deleted, created_lfda_ids )}
|
||||
%endfor
|
||||
%for dataset in name_sorted( parent_datasets ):
|
||||
<%
|
||||
if created_lfda_ids and dataset.id in created_lfda_ids:
|
||||
selected = True
|
||||
else:
|
||||
selected = False
|
||||
%>
|
||||
<li class="datasetRow" style="padding-left: ${pad + 18}px;">${render_dataset( dataset, selected, deleted )}</li>
|
||||
%endfor
|
||||
</ul>
|
||||
</%def>
|
||||
|
||||
<h2>
|
||||
%if deleted:
|
||||
Deleted
|
||||
%endif
|
||||
Libraries
|
||||
</h2>
|
||||
|
||||
<ul class="manage-table-actions">
|
||||
%if not deleted:
|
||||
<li>
|
||||
<a class="action-button" href="${h.url_for( action='library', new=True )}"><span>Create a new library</span></a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="action-button" href="${h.url_for( action='deleted_libraries' )}"><span>Manage deleted libraries</span></a>
|
||||
</li>
|
||||
%endif
|
||||
</ul>
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
%if not libraries:
|
||||
%if deleted:
|
||||
There are no deleted libraries
|
||||
%else:
|
||||
There are no libraries.
|
||||
%endif
|
||||
%else:
|
||||
<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">
|
||||
<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>
|
||||
%if not deleted:
|
||||
<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="Current state will not be saved, so undeleting the library will restore all of its contents. Click OK to delete the library named '${library.name}'?" href="${h.url_for( action='library', delete=True, id=library.id )}">Delete this library and its contents</a>
|
||||
</div>
|
||||
%else:
|
||||
<div popupmenu="library-${library.id}-popup">
|
||||
<a class="action-button" href="${h.url_for( action='undelete_library', id=library.id )}">Undelete this library and its contents</a>
|
||||
<a class="action-button" href="${h.url_for( action='purge_library', id=library.id )}">Purge this library and its contents</a>
|
||||
</div>
|
||||
%endif
|
||||
</th>
|
||||
<th width="100">Format</th>
|
||||
<th width="50">Db</th>
|
||||
<th width="200">Info</th>
|
||||
</table>
|
||||
</div>
|
||||
</li>
|
||||
<ul>
|
||||
${render_folder( library.root_folder, 0, deleted, created_lfda_ids )}
|
||||
</ul>
|
||||
<br/>
|
||||
##%endif
|
||||
%endfor
|
||||
</ul>
|
||||
%if not deleted:
|
||||
<p>
|
||||
<b>Perform action on selected datasets:</b>
|
||||
<select name="action" id="action_on_datasets_select">
|
||||
<option value="edit">Edit selected datasets' attributes and permissions</option>
|
||||
<option value="delete">Remove selected datasets from this library</option>
|
||||
</select>
|
||||
<input type="submit" class="primary-button" name="action_on_datasets_button" id="action_on_datasets_button" value="Go"/>
|
||||
</p>
|
||||
%endif
|
||||
</form>
|
||||
%endif
|
||||
@@ -0,0 +1,63 @@
|
||||
## Render the dataset `data`
|
||||
<%def name="render_dataset( data, selected, deleted )">
|
||||
<div class="historyItemWrapper historyItem historyItem-${data.state}" id="libraryItem-${data.id}">
|
||||
|
||||
## Header row for library items (name, state, action buttons)
|
||||
<div style="overflow: hidden;" class="historyItemTitleBar">
|
||||
<table cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td width="*">
|
||||
%if selected:
|
||||
<input type="checkbox" name="dataset_ids" value="${data.id}" checked/>
|
||||
%else:
|
||||
<input type="checkbox" name="dataset_ids" value="${data.id}"/>
|
||||
%endif
|
||||
<span class="historyItemTitle"><b>${data.display_name()}</b></span>
|
||||
%if not deleted:
|
||||
<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( controller='admin', action='dataset', id=data.id )}">Edit this dataset's attributes and permissions</a>
|
||||
%if data.has_data:
|
||||
<a class="action-button" href="${h.url_for( controller='admin', action='download_dataset_from_folder', id=data.id )}">Download this dataset</a>
|
||||
%endif
|
||||
<a class="action-button" confirm="Click OK to remove dataset '${data.name}'?" href="${h.url_for( controller='admin', action='dataset', delete=True, id=data.id )}">Remove this dataset from the library</a>
|
||||
</div>
|
||||
%endif
|
||||
</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 library items, extra info and actions, data "peek"
|
||||
<div id="info${data.id}" class="historyItemBody">
|
||||
<div>${data.blurb}</div>
|
||||
<div>
|
||||
%if data.has_data:
|
||||
%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.visible_children ) > 0:
|
||||
<div>
|
||||
There are ${len( data.visible_children )} secondary datasets.
|
||||
%for idx, child in enumerate( data.visible_children ):
|
||||
${ render_dataset( child, selected, deleted ) }
|
||||
%endfor
|
||||
</div>
|
||||
%endif
|
||||
</div>
|
||||
</%def>
|
||||
@@ -0,0 +1,105 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/dataset/security_common.mako" import="render_permission_form" />
|
||||
|
||||
|
||||
<%def name="title()">Edit Dataset Attributes</%def>
|
||||
|
||||
<%def name="datatype( dataset, datatypes )">
|
||||
<select name="datatype">
|
||||
## $datatypes.sort()
|
||||
%for ext in datatypes:
|
||||
%if dataset.ext == ext:
|
||||
<option value="${ext}" selected="yes">${ext}</option>
|
||||
%else:
|
||||
<option value="${ext}">${ext}</option>
|
||||
%endif
|
||||
%endfor
|
||||
</select>
|
||||
</%def>
|
||||
|
||||
<%
|
||||
roles = trans.app.model.Role.filter( trans.app.model.Role.table.c.deleted==False ).order_by( trans.app.model.Role.table.c.name ).all()
|
||||
%>
|
||||
|
||||
%if isinstance( dataset, list ):
|
||||
<%
|
||||
name_str = '%d selected datasets' % len( dataset )
|
||||
%>
|
||||
${render_permission_form( dataset[0].dataset, name_str, h.url_for( action='dataset' ), 'id', ",".join( [ str(d.id) for d in dataset ] ), roles )}
|
||||
%else:
|
||||
${render_permission_form( dataset.dataset, dataset.name, h.url_for( action='dataset' ), 'id', dataset.id, roles )}
|
||||
%endif
|
||||
|
||||
%if not isinstance( dataset, list ):
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">Edit Attributes</div>
|
||||
<div class="toolFormBody">
|
||||
<form name="edit_attributes" action="${h.url_for( controller='admin', action='dataset' )}" method="post">
|
||||
<input type="hidden" name="id" value="${dataset.id}"/>
|
||||
<div class="form-row">
|
||||
<label>Name:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="text" name="name" value="${dataset.name}" size="40"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>Info:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="text" name="info" value="${dataset.info}" size="40"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
%for name, spec in dataset.metadata.spec.items():
|
||||
%if spec.visible:
|
||||
<div class="form-row">
|
||||
<label>${spec.desc}:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
${dataset.metadata.get_html_by_name( name )}
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
%endif
|
||||
%endfor
|
||||
<div class="form-row">
|
||||
<input type="submit" name="save" value="Save"/>
|
||||
</div>
|
||||
</form>
|
||||
<form name="auto_detect" action="${h.url_for( controller='admin', action='dataset' )}" method="post">
|
||||
<input type="hidden" name="id" value="${dataset.id}"/>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="submit" name="detect" value="Auto-detect"/>
|
||||
</div>
|
||||
<div class="toolParamHelp" style="clear: both;">
|
||||
This will inspect the dataset and attempt to correct the above column values
|
||||
if they are not accurate.
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<p/>
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">Change data type</div>
|
||||
<div class="toolFormBody">
|
||||
<form name="change_datatype" action="${h.url_for( controller='admin', action='dataset' )}" method="post">
|
||||
<input type="hidden" name="id" value="${dataset.id}"/>
|
||||
<div class="form-row">
|
||||
<label>New Type:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
${datatype( dataset, datatypes )}
|
||||
</div>
|
||||
<div class="toolParamHelp" style="clear: both;">
|
||||
This will change the datatype of the existing dataset
|
||||
but <i>not</i> modify its contents. Use this if Galaxy
|
||||
has incorrectly guessed the type of your dataset.
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<input type="submit" name="change" value="Save"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<p/>
|
||||
%endif
|
||||
@@ -0,0 +1,110 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
<% import os %>
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
<div class="toolForm" id="new_dataset">
|
||||
<div class="toolFormTitle">Create a new library dataset</div>
|
||||
<div class="toolFormBody">
|
||||
<form name="tool_form" action="${h.url_for( controller='admin', action='dataset' )}" enctype="multipart/form-data" method="post">
|
||||
<input type="hidden" name="folder_id" value="${folder_id}"/>
|
||||
<div class="form-row">
|
||||
<label>File:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="file" name="file_data"/>
|
||||
</div>
|
||||
<div class="toolParamHelp" style="clear: both;">
|
||||
Upload a single file. Use the "Server Directory" feature below to upload an entire directory of files.
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>URL/Text:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<textarea name="url_paste" rows="5" cols="35"></textarea>
|
||||
</div>
|
||||
<div class="toolParamHelp" style="clear: both;">
|
||||
Specify a list of URLs (one per line) or paste the contents of a file.
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
%if trans.app.config.library_import_dir is not None:
|
||||
<div class="form-row">
|
||||
<label>Server Directory</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<select name="server_dir">
|
||||
<option>None</option>
|
||||
%for dir in os.listdir( trans.app.config.library_import_dir ):
|
||||
<option>${dir}</option>
|
||||
%endfor
|
||||
</select>
|
||||
</div>
|
||||
<div class="toolParamHelp" style="clear: both;">
|
||||
Upload all files in a subdirectory of <strong>${trans.app.config.library_import_dir}</strong> on the Galaxy server.
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
%endif
|
||||
<div class="form-row">
|
||||
<label>Convert spaces to tabs:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<div>
|
||||
<input type="checkbox" name="space_to_tab" value="Yes"/>Yes
|
||||
</div>
|
||||
</div>
|
||||
<div class="toolParamHelp" style="clear: both;">
|
||||
Use this option if you are manually entering intervals.
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>File Format:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<select name="extension">
|
||||
<option value="auto" selected>Auto-detect</option>
|
||||
%for file_format in file_formats:
|
||||
<option value="${file_format}">${file_format}</option>
|
||||
%endfor
|
||||
</select>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>Genome:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<select name="dbkey">
|
||||
%for dbkey in dbkeys:
|
||||
%if dbkey[1] == last_used_build:
|
||||
<option value="${dbkey[1]}" selected>${dbkey[0]}</option>
|
||||
%else:
|
||||
<option value="${dbkey[1]}">${dbkey[0]}</option>
|
||||
%endif
|
||||
%endfor
|
||||
</select>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<label>Restrict dataset access to specific roles:</label>
|
||||
<select name="roles" multiple="true" size="5">
|
||||
%for role in roles:
|
||||
<option value="${role.id}">${role.description}</option>
|
||||
%endfor
|
||||
</select>
|
||||
</div>
|
||||
<div class="toolParamHelp" style="clear: both;">
|
||||
Multi-select list - hold the appropriate key while clicking to select multiple roles. More restrictions can be applied after the upload is complete. Selecting no roles makes a dataset public.
|
||||
</div>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
<div class="form-row">
|
||||
<input type="submit" class="primary-button" name="new_dataset_button" value="Add Dataset(s) to Folder"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,41 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">Create a new folder</div>
|
||||
<div class="toolFormBody">
|
||||
<form name="folder" action="${h.url_for( controller='admin', action='folder' )}" method="post" >
|
||||
<div class="form-row">
|
||||
<label>Name:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="text" name="name" value="New Folder" size="40"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>Description:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="text" name="description" value="" size="40"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="hidden" name="new" value="submitted" size="40"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="hidden" name="id" value="${folder.id}" size="40"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<input type="submit" name="new_folder_button" value="Create"/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,35 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">Create a new library</div>
|
||||
<div class="toolFormBody">
|
||||
<form name="library" action="${h.url_for( controller='admin', action='library' )}" method="post" >
|
||||
<div class="form-row">
|
||||
<label>Name:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="text" name="name" value="New Library" size="40"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>Description:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="text" name="description" value="" size="40"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="hidden" name="new" value="submitted" size="40"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<input type="submit" name="create_library_button" value="Create"/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,41 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">Edit folder name and description</div>
|
||||
<div class="toolFormBody">
|
||||
<form name="folder" action="${h.url_for( controller='admin', action='folder' )}" method="post" >
|
||||
<div class="form-row">
|
||||
<label>Name:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="text" name="name" value="${folder.name}" size="40"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>Description:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="text" name="description" value="${folder.description}" size="40"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="hidden" name="rename" value="submitted" size="40"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="hidden" name="id" value="${folder.id}" size="40"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<input type="submit" name="rename_folder_button" value="Save"/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,48 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">Change library name and description</div>
|
||||
<div class="toolFormBody">
|
||||
<form name="library" action="${h.url_for( controller='admin', action='library' )}" method="post" >
|
||||
<div class="form-row">
|
||||
<label>Name:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="text" name="name" value="${library.name}" size="40"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>Description:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="text" name="description" value="${library.description}" size="40"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>Also change the root folder's name:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="checkbox" name="root_folder"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="hidden" name="rename" value="submitted"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="hidden" name="id" value="${library.id}"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<input type="submit" name="rename_library_button" value="Save"/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,4 +1,5 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
<%def name="title()">Memory Profiling</%def>
|
||||
<%
|
||||
@@ -44,7 +45,7 @@
|
||||
</%def>
|
||||
|
||||
%if msg:
|
||||
<div class="donemessage">${msg}</div>
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
%if heap is None:
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%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">
|
||||
<input type="submit" name="action" value="Reload"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,43 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">Create account</div>
|
||||
<div class="toolFormBody">
|
||||
<form name="form" action="${h.url_for( controller='admin', action='create_new_user' )}" method="post" >
|
||||
<div class="form-row">
|
||||
<label>Email address:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="text" name="email" value="${email}" size="40">
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>Password:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="password" name="password" value="${password}" size="40">
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>Confirm password:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="password" name="confirm" value="${confirm}" size="40">
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>Subscribe To Mailing List:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="checkbox" name="subscribe" value="${subscribe}" size="40">
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<input type="submit" name="user_create_button" value="Create">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,91 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
## Render a row
|
||||
<%def name="render_row( user, ctr, anchored, curr_anchor )">
|
||||
%if ctr % 2 == 1:
|
||||
<tr class="odd_row">
|
||||
%else:
|
||||
<tr>
|
||||
%endif
|
||||
<td>
|
||||
${user.email}
|
||||
<a id="user-${user.id}-popup" class="popup-arrow" style="display: none;">▼</a>
|
||||
<div popupmenu="user-${user.id}-popup">
|
||||
<a class="action-button" href="${h.url_for( controller="admin", action='undelete_user', user_id=user.id )}">Undelete</a>
|
||||
<a class="action-button" confirm="Purging a user will delete all of their histories, datasets, and group and role associations. Click OK to purge the user '${user.email}'" href="${h.url_for( controller="admin", action='purge_user', user_id=user.id )}">Purge</a>
|
||||
</div>
|
||||
%if not anchored:
|
||||
<a name="${curr_anchor}"></a>
|
||||
<div style="float: right;"><a href="#TOP">top</a></div>
|
||||
%endif
|
||||
</td>
|
||||
</tr>
|
||||
</%def>
|
||||
|
||||
<a name="TOP"><h2>Deleted Users</h2></a>
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
%if len( users ) == 0:
|
||||
There are no deleted Galaxy users
|
||||
%else:
|
||||
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
|
||||
<%
|
||||
render_quick_find = len( users ) > 50
|
||||
ctr = 0
|
||||
%>
|
||||
%if render_quick_find:
|
||||
<%
|
||||
anchors = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
|
||||
anchor_loc = 0
|
||||
anchored = False
|
||||
curr_anchor = 'A'
|
||||
%>
|
||||
<tr style="background: #EEE">
|
||||
<td colspan="3" style="text-align: center; border-bottom: 1px solid #D8B365">
|
||||
Jump to letter:
|
||||
%for a in anchors:
|
||||
| <a href="#${a}">${a}</a>
|
||||
%endfor
|
||||
</td>
|
||||
</tr>
|
||||
%endif
|
||||
<tr class="header">
|
||||
<td>Email</td>
|
||||
</tr>
|
||||
%for ctr, user in enumerate( users ):
|
||||
%if render_quick_find and not user.email.upper().startswith( curr_anchor ):
|
||||
<% anchored = False %>
|
||||
%endif
|
||||
%if render_quick_find and user.email.upper().startswith( curr_anchor ):
|
||||
%if not anchored:
|
||||
${render_row( user, ctr, anchored, curr_anchor )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_row( user, ctr, anchored, curr_anchor )}
|
||||
%endif
|
||||
%elif render_quick_find:
|
||||
%for anchor in anchors[ anchor_loc: ]:
|
||||
%if user.email.upper().startswith( anchor ):
|
||||
%if not anchored:
|
||||
<% curr_anchor = anchor %>
|
||||
${render_row( user, ctr, anchored, curr_anchor )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_row( user, ctr, anchored, curr_anchor )}
|
||||
%endif
|
||||
<%
|
||||
anchor_loc = anchors.index( anchor )
|
||||
break
|
||||
%>
|
||||
%endif
|
||||
%endfor
|
||||
%else:
|
||||
${render_row( user, ctr, True, '' )}
|
||||
%endif
|
||||
%endfor
|
||||
</table>
|
||||
%endif
|
||||
@@ -0,0 +1,35 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">Reset user password</div>
|
||||
<div class="toolFormBody">
|
||||
<form name="form" action="${h.url_for( controller='admin', action='reset_user_password' )}" method="post" >
|
||||
<input type="hidden" name="user_id" value="${user.id}" size="40">
|
||||
<div class="form-row">
|
||||
<label>Email:</label>
|
||||
${user.email}
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>Password:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="password" name="password" value="${password}" size="40">
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>Confirm password:</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="password" name="confirm" value="${confirm}" size="40">
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<input type="submit" name="reset_user_password_button" value="Reset">
|
||||
</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,16 +1,16 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>${self.title()}</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
||||
${self.stylesheets()}
|
||||
${self.javascripts()}
|
||||
</head>
|
||||
<head>
|
||||
<title>${self.title()}</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
||||
${self.stylesheets()}
|
||||
${self.javascripts()}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
${next.body()}
|
||||
</body>
|
||||
<body>
|
||||
${next.body()}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
## Default title
|
||||
@@ -28,4 +28,4 @@ ${self.javascripts()}
|
||||
## <![endif]-->
|
||||
<script type="text/javascript" src="${h.url_for('/static/scripts/jquery.js')}"></script>
|
||||
<script type="text/javascript" src="${h.url_for('/static/scripts/galaxy.base.js')}"></script>
|
||||
</%def>
|
||||
</%def>
|
||||
|
||||
@@ -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,14 +20,30 @@
|
||||
<%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
|
||||
<%def name="javascripts()">
|
||||
<!--[if lt IE 7]>
|
||||
<script type='text/javascript' src="/static/scripts/IE7.js"> </script>
|
||||
<script type='text/javascript' src="/static/scripts/ie7-recalc.js"> </script>
|
||||
<![endif]-->
|
||||
<!--[if lt IE 7]>
|
||||
<script type='text/javascript' src="/static/scripts/IE7.js"> </script>
|
||||
<script type='text/javascript' src="/static/scripts/ie7-recalc.js"> </script>
|
||||
<![endif]-->
|
||||
</%def>
|
||||
|
||||
## Default late-load javascripts
|
||||
@@ -27,45 +55,69 @@
|
||||
<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" ) );
|
||||
force_left_panel = lp.force_panel;
|
||||
%endif
|
||||
%if self.has_right_panel:
|
||||
var rp = make_right_panel( $("#right"), $("#center"), $("#right-border" ) );
|
||||
handle_minwidth_hint = rp.handle_minwidth_hint;
|
||||
force_right_panel = rp.force_panel;
|
||||
%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()}
|
||||
${self.stylesheets()}
|
||||
<title>${self.title()}</title>
|
||||
${self.javascripts()}
|
||||
${self.stylesheets()}
|
||||
</head>
|
||||
|
||||
<body scroll="no">
|
||||
## Background displays first
|
||||
<div id="background"></div>
|
||||
## Layer iframes over backgrounds
|
||||
<div id="masthead">
|
||||
${self.masthead()}
|
||||
</div>
|
||||
<div id="left">
|
||||
${self.left_panel()}
|
||||
</div>
|
||||
<div id="left-border"><div id="left-border-inner" style="display: none;"></div></div>
|
||||
<div id="center">
|
||||
${self.center_panel()}
|
||||
</div>
|
||||
<div id="right-border"><div id="right-border-inner" style="display: none;"></div></div>
|
||||
<div id="right">
|
||||
${self.right_panel()}
|
||||
</div>
|
||||
## Background displays first
|
||||
<div id="background"></div>
|
||||
## Layer iframes over backgrounds
|
||||
<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()}
|
||||
${next.body()}
|
||||
</body>
|
||||
## Scripts can be loaded later since they progressively add features to
|
||||
## the panels, but do not change layout
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
if data.id in source_dataset_ids:
|
||||
checked = " checked"
|
||||
%>
|
||||
<div class="form-row"><input type="checkbox" name="source_dataset_ids" value="${data.id}"${checked}> ${data.hid}: ${data.name}</div>
|
||||
<div class="form-row"><input type="checkbox" name="source_dataset_ids" value="${data.id}"${checked}/> ${data.hid}: ${data.name}</div>
|
||||
%endfor
|
||||
</div>
|
||||
</div>
|
||||
@@ -41,7 +41,7 @@
|
||||
if hist == trans.get_history():
|
||||
cur_history_text = " <strong>(current history)</strong>"
|
||||
%>
|
||||
<div class="form-row"><input type="checkbox" name="target_history_ids" value="${hist.id}"${checked}> ${i + 1}${cur_history_text}: ${hist.name}</div>
|
||||
<div class="form-row"><input type="checkbox" name="target_history_ids" value="${hist.id}"${checked}/> ${i + 1}${cur_history_text}: ${hist.name}</div>
|
||||
%endfor
|
||||
%if trans.get_user():
|
||||
<%
|
||||
@@ -50,12 +50,12 @@
|
||||
checked = " checked"
|
||||
%>
|
||||
<br>
|
||||
<div class="form-row"><input type="checkbox" name="target_history_ids" value="create_new_history"${checked}>New history named: <input type="textbox" name="new_history_name" value="${new_history_name}"></div>
|
||||
<div class="form-row"><input type="checkbox" name="target_history_ids" value="create_new_history"${checked}/>New history named: <input type="textbox" name="new_history_name" value="${new_history_name}"/></div>
|
||||
%endif
|
||||
</div>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
<div class="form-row" align="center"><input type="submit" class="primary-button" name="do_copy" value="Copy History Items"></div>
|
||||
<div class="form-row" align="center"><input type="submit" class="primary-button" name="do_copy" value="Copy History Items"/></div>
|
||||
</form>
|
||||
</div>
|
||||
</p>
|
||||
|
||||
@@ -1,146 +1,195 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%def name="title()">History Item Attributes</%def>
|
||||
<%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>
|
||||
<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>
|
||||
|
||||
<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}">
|
||||
<div class="form-row">
|
||||
<label>
|
||||
Name:
|
||||
</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="text" name="name" value="${data.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="${data.info}" size="40">
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
%for name, spec in data.metadata.spec.items():
|
||||
%if spec.visible:
|
||||
<%
|
||||
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_name}" value="${data.id}"/>
|
||||
<div class="form-row">
|
||||
<label>
|
||||
${spec.desc}:
|
||||
</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
${data.metadata.get_html_by_name( name )}
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
<label>
|
||||
Name:
|
||||
</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="text" name="name" value="${data.name}" size="40"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
%endif
|
||||
%endfor
|
||||
<div class="form-row">
|
||||
<input type="submit" name="save" value="Save">
|
||||
</div>
|
||||
</form>
|
||||
<form name="auto_detect" action="${h.url_for( action='edit' )}" method="post">
|
||||
<input type="hidden" name="id" value="${data.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>
|
||||
%if data.missing_meta():
|
||||
<div class="errormessagesmall">Required metadata values are missing. Some of these values may not be editable by the user. Selecting "Auto-detect" will attempt to fix these values.</div>
|
||||
%endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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 class="form-row">
|
||||
<label>
|
||||
Info:
|
||||
</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
<input type="text" name="info" value="${data.info}" size="40"/>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
%for name, spec in data.metadata.spec.items():
|
||||
%if spec.visible:
|
||||
<div class="form-row">
|
||||
<label>
|
||||
${spec.desc}:
|
||||
</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
${data.metadata.get_html_by_name( name )}
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
%endif
|
||||
%endfor
|
||||
<div class="form-row">
|
||||
<input type="submit" name="save" value="Save"/>
|
||||
</div>
|
||||
</form>
|
||||
<form name="auto_detect" action="${h.url_for( action='edit' )}" method="post">
|
||||
<input type="hidden" name="${id_name}" value="${data.id}"/>
|
||||
<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>
|
||||
<input type="submit" name="detect" value="Auto-detect"/>
|
||||
</div>
|
||||
|
||||
<div class="toolParamHelp" style="clear: both;">
|
||||
This will create a new dataset with the contents of this
|
||||
dataset converted to a new format.
|
||||
This will inspect the dataset and attempt to correct the above column values if they are not accurate.
|
||||
</div>
|
||||
</form>
|
||||
%if data.missing_meta():
|
||||
<div class="errormessagesmall">Required metadata values are missing. Some of these values may not be editable by the user. Selecting "Auto-detect" will attempt to fix these values.</div>
|
||||
%endif
|
||||
</div>
|
||||
</div>
|
||||
<p />
|
||||
%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="form-row">
|
||||
<input type="submit" name="convert_data" value="Convert"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<p />
|
||||
%endif
|
||||
%endif
|
||||
<div class="toolForm">
|
||||
<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_name}" value="${data.id}"/>
|
||||
<div class="form-row">
|
||||
<label>
|
||||
New Type:
|
||||
</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
${datatype( data, 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 />
|
||||
%else:
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">View Attributes</div>
|
||||
<div class="toolFormBody">
|
||||
<div class="form-row">
|
||||
<b>Name:</b> ${data.name}
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<input type="submit" name="convert_data" value="Convert">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p />
|
||||
%endif
|
||||
<b>Info:</b> ${data.info}
|
||||
<div style="clear: both"></div>
|
||||
<b>Data Format:</b> ${data.ext}
|
||||
<div style="clear: both"></div>
|
||||
%for name, spec in data.metadata.spec.items():
|
||||
%if spec.visible:
|
||||
<b>${spec.desc}:</b>
|
||||
%if spec.unwrap( spec.get( name ) ):
|
||||
${spec.unwrap( spec.get( name ) )}
|
||||
%else:
|
||||
${spec.no_value}
|
||||
%endif
|
||||
<div style="clear: both"></div>
|
||||
%endif
|
||||
%endfor
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p />
|
||||
%endif
|
||||
|
||||
|
||||
<div class="toolForm">
|
||||
<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}">
|
||||
<div class="form-row">
|
||||
<label>
|
||||
New Type:
|
||||
</label>
|
||||
<div style="float: left; width: 250px; margin-right: 10px;">
|
||||
${datatype( data, datatypes )}
|
||||
%if trans.app.security_agent.allow_action( trans.user, data.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset = data ):
|
||||
<%namespace file="/dataset/security_common.mako" import="render_permission_form" />
|
||||
${render_permission_form( data.dataset, data.name, h.url_for( action='edit' ), id_name, data.id, trans.user.all_roles() )}
|
||||
%elif trans.user:
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">View permissions</div>
|
||||
<div class="toolFormBody">
|
||||
<div class="form-row">
|
||||
%if data.dataset.actions:
|
||||
<ul>
|
||||
%for action, roles in trans.app.security_agent.get_dataset_permissions( data.dataset ).items():
|
||||
%if roles:
|
||||
<li>${action.description}</li>
|
||||
<ul>
|
||||
%for role in roles:
|
||||
<li>${role.description}</li>
|
||||
%endfor
|
||||
</ul>
|
||||
%endif
|
||||
%endfor
|
||||
</ul>
|
||||
%else:
|
||||
<p>This dataset is accessible by everyone (it is public).</p>
|
||||
%endif
|
||||
</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>
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">Copy History Item</div>
|
||||
<div class="toolFormBody">
|
||||
Click <a href="${h.url_for( controller='dataset', action='copy_datasets', source_dataset_ids=data.id, target_history_ids=data.history_id )}" target="galaxy_main">here</a> to make a copy of this history item.
|
||||
</div>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
%endif
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
<%def name="render_select( current_actions, action_key, action, all_roles )">
|
||||
<%
|
||||
in_roles = []
|
||||
for a in current_actions:
|
||||
if a.action == action.action:
|
||||
in_roles.append( a.role )
|
||||
out_roles = filter( lambda x: x not in in_roles, all_roles )
|
||||
%>
|
||||
<p><label>${action.action}:</label> ${action.description}</p>
|
||||
<div style="width: 100%; white-space: nowrap;">
|
||||
<div style="float: left; width: 50%;">
|
||||
Roles associated:<br/>
|
||||
<select name="${action_key}_in" id="${action_key}_in_select" class="in_select" style="max-width: 98%; width: 98%; height: 150px; font-size: 100%;" multiple>
|
||||
%for role in in_roles:
|
||||
<option value="${role.id}">${role.description}</option>
|
||||
%endfor
|
||||
</select> <br/>
|
||||
<div style="width: 98%; text-align: right"><input type="submit" id="${action_key}_remove_button" class="role_remove_button" value=">>"/></div>
|
||||
</div>
|
||||
<div style="width: 50%;">
|
||||
Roles not associated:<br/>
|
||||
<select name="${action_key}_out" id="${action_key}_out_select" style="max-width: 98%; width: 98%; height: 150px; font-size: 100%;" multiple>
|
||||
%for role in out_roles:
|
||||
<option value="${role.id}">${role.description}</option>
|
||||
%endfor
|
||||
</select> <br/>
|
||||
<input type="submit" id="${action_key}_add_button" class="role_add_button" value="<<"/>
|
||||
</div>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="render_permission_form( obj, obj_name, form_url, id_name, id, all_roles )">
|
||||
<%
|
||||
if isinstance( obj, trans.app.model.User ):
|
||||
current_actions = obj.default_permissions
|
||||
obj_str = 'user %s' % obj_name
|
||||
elif isinstance( obj, trans.app.model.History ):
|
||||
current_actions = obj.default_permissions
|
||||
obj_str = 'history %s' % obj_name
|
||||
elif isinstance( obj, trans.app.model.Dataset ):
|
||||
current_actions = obj.actions
|
||||
obj_str = obj_name
|
||||
else:
|
||||
current_actions = obj.dataset.actions
|
||||
obj_str = 'unknown object %s' %obj_name
|
||||
%>
|
||||
<script type="text/javascript">
|
||||
var q = jQuery.noConflict();
|
||||
q( document ).ready( function () {
|
||||
q( '.role_add_button' ).click( function() {
|
||||
var action = this.id.substring( 0, this.id.lastIndexOf( '_add_button' ) )
|
||||
var in_select = '#' + action + '_in_select';
|
||||
var out_select = '#' + action + '_out_select';
|
||||
return !q( out_select + ' option:selected' ).remove().appendTo( in_select );
|
||||
});
|
||||
q( '.role_remove_button' ).click( function() {
|
||||
var action = this.id.substring( 0, this.id.lastIndexOf( '_remove_button' ) )
|
||||
var in_select = '#' + action + '_in_select';
|
||||
var out_select = '#' + action + '_out_select';
|
||||
return !q( in_select + ' option:selected' ).remove().appendTo( out_select );
|
||||
});
|
||||
q( 'form#edit_role_associations' ).submit( function() {
|
||||
q( '.in_select option' ).each(function( i ) {
|
||||
q( this ).attr( "selected", "selected" );
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">Manage permissions and role associations for ${obj_str}</div>
|
||||
<div class="toolFormBody">
|
||||
<form name="edit_role_associations" id="edit_role_associations" action="${form_url}" method="post">
|
||||
<input type="hidden" name="${id_name}" value="${id}"/>
|
||||
<div class="form-row"></div>
|
||||
%for k, v in trans.app.model.Dataset.permitted_actions.items():
|
||||
<div class="form-row">
|
||||
${render_select( current_actions, k, v, all_roles )}
|
||||
</div>
|
||||
%endfor
|
||||
<div class="form-row">
|
||||
<input type="submit" name="update_roles" value="Save"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<p/>
|
||||
</%def>
|
||||
@@ -10,6 +10,10 @@ $(function(){
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
%if header:
|
||||
${header}
|
||||
%endif
|
||||
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">${form.title}</div>
|
||||
<div class="toolFormBody">
|
||||
@@ -21,9 +25,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>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
%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( controller='root', action='history_share' )}" target="galaxy_main">Share</a> current history</div>
|
||||
<li><a href="${h.url_for( action='history_set_default_permissions' )}">Change default permissions</a> for the current history</li>
|
||||
%endif
|
||||
<li><a href="${h.url_for( controller='root', action='history', show_deleted=True)}" target="galaxy_history">Show deleted</a> datasets in history</li>
|
||||
<li><a href="${h.url_for( controller='root', 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,7 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%def name="title()">Change Default Permissions on New Datasets in This History</%def>
|
||||
<%namespace file="/dataset/security_common.mako" import="render_permission_form" />
|
||||
|
||||
%if trans.user:
|
||||
${render_permission_form( trans.history, trans.history.name, h.url_for(), 'id', None, trans.user.all_roles() )}
|
||||
%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,90 @@
|
||||
<tr><td colspan="2" align="right"><input type="submit" name="history_share_btn" value="Submit"></td></tr>
|
||||
</form>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
%else:
|
||||
<style type="text/css">
|
||||
th
|
||||
{
|
||||
text-align: left;
|
||||
}
|
||||
td
|
||||
{
|
||||
vertical-align: top;
|
||||
}
|
||||
</style>
|
||||
<form action="${h.url_for( action='history_share' )}" method="post">
|
||||
%for history in histories:
|
||||
<input type="hidden" name="id" value="${history.id}">
|
||||
%endfor
|
||||
<input type="hidden" name="email" value="${email}">
|
||||
<div class="warningmessage">
|
||||
The history or histories you've chosen to share contain datasets that the user you're sharing with does not have permission to access. These datasets are shown below. Datasets which the user already has permission to access are not shown.
|
||||
</div>
|
||||
<p/>
|
||||
%if can_change:
|
||||
<div class="donemessage">
|
||||
The following datasets can be shared with ${email} by updating their permissions:
|
||||
<p/>
|
||||
<table cellpadding="0" cellspacing="8" border="0">
|
||||
<tr><th>Histories</th><th>Datasets</th></tr>
|
||||
%for history, datasets in can_change.items():
|
||||
<tr>
|
||||
<td>${history.name}</td>
|
||||
<td>
|
||||
%for dataset in datasets:
|
||||
${dataset.name}<br/>
|
||||
%endfor
|
||||
</td>
|
||||
</tr>
|
||||
%endfor
|
||||
</table>
|
||||
</div>
|
||||
<p/>
|
||||
%endif
|
||||
%if cannot_change:
|
||||
<div class="errormessage">
|
||||
The following datasets cannot be shared with ${email} because you do not have permission to change the permissions on them.
|
||||
<p/>
|
||||
<table cellpadding="0" cellspacing="8" border="0">
|
||||
<tr><th>Histories</th><th>Datasets</th></tr>
|
||||
%for history, datasets in cannot_change.items():
|
||||
<tr>
|
||||
<td>${history.name}</td>
|
||||
<td>
|
||||
%for dataset in datasets:
|
||||
${dataset.name}<br/>
|
||||
%endfor
|
||||
</td>
|
||||
</tr>
|
||||
%endfor
|
||||
</table>
|
||||
</div>
|
||||
<p/>
|
||||
%endif
|
||||
<div>
|
||||
<b>How would you like to proceed?</b>
|
||||
<p/>
|
||||
%if can_change:
|
||||
<input type="radio" name="action" value="public"> Set datasets above to public access
|
||||
%if cannot_change:
|
||||
(where possible)
|
||||
%endif
|
||||
<br/>
|
||||
<input type="radio" name="action" value="private"> Set datasets above to private access for me and the user(s) with whom I am sharing
|
||||
%if cannot_change:
|
||||
(where possible)
|
||||
%endif
|
||||
<br/>
|
||||
%endif
|
||||
<input type="radio" name="action" value="share"> Share anyway
|
||||
%if can_change:
|
||||
(don't change any permissions)
|
||||
%endif
|
||||
<br/>
|
||||
<input type="radio" name="action" value="no_share"> Don't share<br/>
|
||||
<br/>
|
||||
<input type="submit" name="submit" value="Ok"><br/>
|
||||
</div>
|
||||
</form>
|
||||
%endif
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%namespace file="common.mako" import="render_dataset" />
|
||||
<%namespace file="/message.mako" import="render_msg" />
|
||||
|
||||
<%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>
|
||||
|
||||
<%
|
||||
def name_sorted( l ):
|
||||
return sorted( l, lambda a, b: cmp( a.name.lower(), b.name.lower() ) )
|
||||
%>
|
||||
|
||||
<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;
|
||||
});
|
||||
});
|
||||
});
|
||||
</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 name_sorted( parent.active_folders ):
|
||||
${render_folder( folder, pad )}
|
||||
%endfor
|
||||
%for dataset in name_sorted( 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 msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
%if not libraries:
|
||||
No libraries contain datasets that you are allowed to access
|
||||
%else:
|
||||
<% can_access = False %>
|
||||
<form name="import_from_library" action="${h.url_for( controller='library', action='import_datasets' )}" method="post">
|
||||
<ul>
|
||||
%for library in libraries:
|
||||
%if trans.app.security_agent.check_folder_contents( trans.user, library ):
|
||||
<% can_access = True %>
|
||||
<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>
|
||||
%if can_access:
|
||||
<input type="submit" class="primary-button" name="import_dataset" value="Import selected datasets"/>
|
||||
%else:
|
||||
No libraries contain datasets that you are allowed to access
|
||||
%endif
|
||||
</form>
|
||||
%endif
|
||||
@@ -0,0 +1,56 @@
|
||||
## Render the dataset `data`
|
||||
<%def name="render_dataset( data )">
|
||||
<div class="historyItemWrapper historyItem historyItem-${data.state}" id="libraryItem-${data.id}">
|
||||
|
||||
## Header row for library items (name, state, action buttons)
|
||||
<div style="overflow: hidden;" class="historyItemTitleBar">
|
||||
<table cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td width="*">
|
||||
<input type="checkbox" name="import_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( controller='root', action='edit', lid=data.id )}">View or edit this dataset's attributes and permissions</a>
|
||||
%if data.has_data:
|
||||
<a class="action-button" href="${h.url_for( controller='library', action='download_dataset_from_folder', id=data.id )}">Download this dataset</a>
|
||||
%endif
|
||||
</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 library items, extra info and actions, data "peek"
|
||||
<div id="info${data.id}" class="historyItemBody">
|
||||
<div>${data.blurb}</div>
|
||||
<div>
|
||||
%if data.has_data:
|
||||
%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.visible_children ) > 0:
|
||||
<div>
|
||||
There are ${len( data.visible_children )} secondary datasets.
|
||||
%for idx, child in enumerate( data.visible_children ):
|
||||
${render_dataset( child )}
|
||||
%endfor
|
||||
</div>
|
||||
%endif
|
||||
</div>
|
||||
</%def>
|
||||
@@ -1,32 +1,44 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
|
||||
<%def name="javascripts()">
|
||||
${parent.javascripts()}
|
||||
<script type="text/javascript">
|
||||
%if 'masthead' in refresh_frames:
|
||||
if ( parent.frames && parent.frames.galaxy_masthead ) {
|
||||
parent.frames.galaxy_masthead.location.href="${h.url_for( controller='root', action='masthead')}";
|
||||
}
|
||||
else if ( parent.parent && parent.parent.frames && parent.parent.frames.galaxy_masthead ) {
|
||||
parent.parent.frames.galaxy_masthead.location.href="${h.url_for( controller='root', action='masthead')}";
|
||||
}
|
||||
%endif
|
||||
%if 'history' in refresh_frames:
|
||||
if ( parent.frames && parent.frames.galaxy_history ) {
|
||||
parent.frames.galaxy_history.location.href="${h.url_for( controller='root', action='history')}";
|
||||
}
|
||||
%endif
|
||||
%if 'tools' in refresh_frames:
|
||||
if ( parent.frames && parent.frames.galaxy_tools ) {
|
||||
parent.frames.galaxy_tools.location.href="${h.url_for( controller='root', action='tool_menu')}";
|
||||
}
|
||||
%endif
|
||||
${parent.javascripts()}
|
||||
<script type="text/javascript">
|
||||
%if 'masthead' in refresh_frames:
|
||||
if ( parent.frames && parent.frames.galaxy_masthead ) {
|
||||
parent.frames.galaxy_masthead.location.href="${h.url_for( controller='root', action='masthead')}";
|
||||
}
|
||||
else if ( parent.parent && parent.parent.frames && parent.parent.frames.galaxy_masthead ) {
|
||||
parent.parent.frames.galaxy_masthead.location.href="${h.url_for( controller='root', action='masthead')}";
|
||||
}
|
||||
%endif
|
||||
%if 'history' in refresh_frames:
|
||||
if ( parent.frames && parent.frames.galaxy_history ) {
|
||||
parent.frames.galaxy_history.location.href="${h.url_for( controller='root', action='history')}";
|
||||
if ( parent.force_right_panel ) {
|
||||
parent.force_right_panel( 'show' );
|
||||
}
|
||||
}
|
||||
%endif
|
||||
%if 'tools' in refresh_frames:
|
||||
if ( parent.frames && parent.frames.galaxy_tools ) {
|
||||
parent.frames.galaxy_tools.location.href="${h.url_for( controller='root', action='tool_menu')}";
|
||||
if ( parent.force_left_panel ) {
|
||||
parent.force_left_panel( 'show' );
|
||||
}
|
||||
}
|
||||
%endif
|
||||
|
||||
if ( parent.handle_minwidth_hint )
|
||||
{
|
||||
parent.handle_minwidth_hint( -1 );
|
||||
}
|
||||
</script>
|
||||
if ( parent.handle_minwidth_hint )
|
||||
{
|
||||
parent.handle_minwidth_hint( -1 );
|
||||
}
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<div class="${message_type}message">${message}</div>
|
||||
|
||||
## Render a message
|
||||
<%def name="render_msg( msg, messagetype='done' )">
|
||||
<div class="${messagetype}message">${msg}</div>
|
||||
<br/>
|
||||
</%def>
|
||||
|
||||