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/
|
||||
|
||||
@@ -8,8 +8,12 @@ build description
|
||||
|
||||
import sys
|
||||
import urllib
|
||||
import pkg_resources; pkg_resources.require( "elementtree" )
|
||||
from elementtree import ElementTree
|
||||
if sys.version_info[:2] >= ( 2, 5 ):
|
||||
import xml.etree.ElementTree as ElementTree
|
||||
else:
|
||||
from galaxy import eggs
|
||||
import pkg_resources; pkg_resources.require( "elementtree" )
|
||||
from elementtree import ElementTree
|
||||
|
||||
URL = "http://genome-test.cse.ucsc.edu/cgi-bin/das/dsn"
|
||||
|
||||
|
||||
@@ -5,8 +5,12 @@ Connects to sites and determines which builds are available at each.
|
||||
|
||||
import sys
|
||||
import urllib
|
||||
import pkg_resources; pkg_resources.require( "elementtree" )
|
||||
from elementtree import ElementTree
|
||||
if sys.version_info[:2] >= ( 2, 5 ):
|
||||
import xml.etree.ElementTree as ElementTree
|
||||
else:
|
||||
from galaxy import eggs
|
||||
import pkg_resources; pkg_resources.require( "elementtree" )
|
||||
from elementtree import ElementTree
|
||||
|
||||
sites = ['http://genome.ucsc.edu/cgi-bin/',
|
||||
'http://archaea.ucsc.edu/cgi-bin/',
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
# Edit this line to refer to galaxy's path:
|
||||
GALAXY=/galaxy/path
|
||||
export PYTHONPATH=${GALAXY}/modules:${GALAXY}/eggs:${GALAXY}/lib
|
||||
export PYTHONPATH=${GALAXY}/lib
|
||||
|
||||
# setup directories
|
||||
mkdir ${GALAXY}/tool-data/shared/ucsc/new
|
||||
@@ -57,4 +57,4 @@ then
|
||||
echo "Manual addition was successful."
|
||||
else
|
||||
echo "Manual addition failed" >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -141,6 +141,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>
|
||||
<!--
|
||||
|
||||
@@ -5,6 +5,7 @@ from galaxy.web import security
|
||||
import galaxy.model
|
||||
import galaxy.model.mapping
|
||||
import galaxy.datatypes.registry
|
||||
import galaxy.security
|
||||
|
||||
class UniverseApplication( object ):
|
||||
"""Encapsulates the state of a Universe application"""
|
||||
@@ -26,13 +27,15 @@ 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 )
|
||||
# Security helper
|
||||
self.security = security.SecurityHelper( id_secret=self.config.id_secret )
|
||||
# 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,16 +39,19 @@ 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.cluster_files_directory = os.path.abspath( kwargs.get( "cluster_files_directory", "database/pbs" ) )
|
||||
self.job_working_directory = resolve_path( kwargs.get( "job_working_directory", "database/job_working_directory" ), self.root )
|
||||
self.outputs_to_working_directory = string_as_bool( kwargs.get( 'outputs_to_working_directory', False ) )
|
||||
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 )
|
||||
@@ -58,10 +62,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 )
|
||||
@@ -69,6 +73,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,394 @@
|
||||
"""
|
||||
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 of Action objects
|
||||
"""
|
||||
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_all_dataset_permissions( self, dataset, permissions ):
|
||||
raise "Unimplemented Method"
|
||||
def set_dataset_permission( self, dataset, permission ):
|
||||
raise "Unimplemented Method"
|
||||
def make_dataset_public( self, dataset ):
|
||||
raise "Unimplemented Method"
|
||||
def get_component_associations( self, **kwd ):
|
||||
raise "Unimplemented Method"
|
||||
def components_are_associated( self, **kwd ):
|
||||
return bool( self.get_component_associations( **kwd ) )
|
||||
def convert_permitted_action_strings( self, permitted_action_strings ):
|
||||
"""
|
||||
When getting permitted actions from an untrusted source like a
|
||||
form, ensure that they match our actual permitted actions.
|
||||
"""
|
||||
return filter( lambda x: x is not None, [ self.permitted_actions.get( action_string ) for action_string in permitted_action_strings ] )
|
||||
|
||||
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 ):
|
||||
# bypass_manage_permission is used to change permissions of datasets in a userless history when logging in
|
||||
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 current default permissions for the user
|
||||
for dup in user.default_permissions:
|
||||
dup.delete()
|
||||
dup.flush()
|
||||
# Add the new default permissions for the user
|
||||
for action, roles in permissions.items():
|
||||
if isinstance( action, Action ):
|
||||
action = action.action
|
||||
for dup in [ self.model.DefaultUserPermissions( user, action, role ) for role in roles ]:
|
||||
dup.flush()
|
||||
if history:
|
||||
for history in user.active_histories:
|
||||
"""
|
||||
TODO: instead of user.active_histories, a faster approach would be the following, but we need the
|
||||
history.activatable_datasets mapper corrected so that eagerload can be used.
|
||||
histories = app.model.History.filter( and_( app.model.History.table.c.user_id==user.id,
|
||||
app.model.History.table.c.purged==False ) ) \
|
||||
.options( eagerload( 'activatable_datasets' ) ).all()
|
||||
"""
|
||||
self.history_set_default_permissions( history, permissions=permissions, dataset=dataset, bypass_manage_permission=bypass_manage_permission )
|
||||
def user_get_default_permissions( self, user ):
|
||||
permissions = {}
|
||||
for dup in user.default_permissions:
|
||||
action = self.get_action( dup.action )
|
||||
if action in permissions:
|
||||
permissions[ action ].append( dup.role )
|
||||
else:
|
||||
permissions[ action ] = [ dup.role ]
|
||||
return permissions
|
||||
def history_set_default_permissions( self, history, permissions={}, dataset=False, bypass_manage_permission=False ):
|
||||
# bypass_manage_permission is used to change permissions of datasets in a user-less history when logging in
|
||||
user = history.user
|
||||
if not user:
|
||||
# default permissions on a user-less history are None
|
||||
return None
|
||||
if not permissions:
|
||||
permissions = self.user_get_default_permissions( user )
|
||||
# Delete all of the current default permission for the history
|
||||
for dhp in history.default_permissions:
|
||||
dhp.delete()
|
||||
dhp.flush()
|
||||
# Add the new default permissions for the history
|
||||
for action, roles in permissions.items():
|
||||
if isinstance( action, Action ):
|
||||
action = action.action
|
||||
for dhp in [ self.model.DefaultHistoryPermissions( history, action, role ) for role in roles ]:
|
||||
dhp.flush()
|
||||
if dataset:
|
||||
# Only deal with datasets that are not purged
|
||||
for hda in history.activatable_datasets:
|
||||
dataset = hda.dataset
|
||||
if dataset.library_associations:
|
||||
# Don't change permissions on a dataset associated with a library
|
||||
continue
|
||||
if [ assoc for assoc in dataset.history_associations if assoc.history not in user.histories ]:
|
||||
# Don't change permissions on a dataset associated with a history not owned by the user
|
||||
continue
|
||||
if bypass_manage_permission or self.allow_action( user, self.permitted_actions.DATASET_MANAGE_PERMISSIONS, dataset=dataset ):
|
||||
self.set_all_dataset_permissions( dataset, permissions )
|
||||
def history_get_default_permissions( self, history ):
|
||||
permissions = {}
|
||||
for dhp in history.default_permissions:
|
||||
action = self.get_action( dhp.action )
|
||||
if action in permissions:
|
||||
permissions[ action ].append( dhp.role )
|
||||
else:
|
||||
permissions[ action ] = [ dhp.role ]
|
||||
return permissions
|
||||
def set_all_dataset_permissions( self, dataset, permissions={} ):
|
||||
# Set new permissions on a dataset, eliminating all current permissions
|
||||
# Delete all of the current permissions on the dataset
|
||||
for adra in dataset.actions:
|
||||
adra.delete()
|
||||
adra.flush()
|
||||
# Add the new permissions on the dataset
|
||||
for action, roles in permissions.items():
|
||||
if isinstance( action, Action ):
|
||||
action = action.action
|
||||
for adra in [ self.model.ActionDatasetRoleAssociation( action, dataset, role ) for role in roles ]:
|
||||
adra.flush()
|
||||
def set_dataset_permission( self, dataset, permission={} ):
|
||||
# Set a specific permission on a dataset, leaving all other current permissions on the dataset alone
|
||||
for action, roles in permission.items():
|
||||
if isinstance( action, Action ):
|
||||
action = action.action
|
||||
# Delete the current specific permission on the dataset if one exists
|
||||
for adra in dataset.actions:
|
||||
if adra.action == action:
|
||||
adra.delete()
|
||||
adra.flush()
|
||||
# Add the new specific permission on the dataset
|
||||
for adra in [ self.model.ActionDatasetRoleAssociation( action, dataset, role ) for role in roles ]:
|
||||
adra.flush()
|
||||
def make_dataset_public( self, dataset ):
|
||||
# A dataset is considered public if there are no "access" actions associated with it. Any
|
||||
# other actions ( 'manage permissions', 'edit metadata' ) are irrelevant.
|
||||
for adra in dataset.actions:
|
||||
if adra.action == self.permitted_actions.DATASET_ACCESS.action:
|
||||
adra.delete()
|
||||
adra.flush()
|
||||
def get_dataset_permissions( self, dataset ):
|
||||
if not isinstance( dataset, self.model.Dataset ):
|
||||
dataset = dataset.dataset
|
||||
permissions = {}
|
||||
for adra in dataset.actions:
|
||||
action = self.get_action( adra.action )
|
||||
if action in permissions:
|
||||
permissions[ action ].append( adra.role )
|
||||
else:
|
||||
permissions[ action ] = [ adra.role ]
|
||||
return permissions
|
||||
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_all_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_permission( dataset, { self.permitted_actions.DATASET_ACCESS : [ sharing_role ] } )
|
||||
def set_entity_user_associations( self, users=[], roles=[], groups=[], delete_existing_assocs=True ):
|
||||
for user in users:
|
||||
if delete_existing_assocs:
|
||||
for a in user.non_private_roles + user.groups:
|
||||
a.delete()
|
||||
a.flush()
|
||||
for role in roles:
|
||||
self.associate_components( user=user, role=role )
|
||||
for group in groups:
|
||||
self.associate_components( user=user, group=group )
|
||||
def set_entity_group_associations( self, groups=[], users=[], roles=[], delete_existing_assocs=True ):
|
||||
for group in groups:
|
||||
if delete_existing_assocs:
|
||||
for a in group.roles + group.users:
|
||||
a.delete()
|
||||
a.flush()
|
||||
for role in roles:
|
||||
self.associate_components( group=group, role=role )
|
||||
for user in users:
|
||||
self.associate_components( group=group, user=user )
|
||||
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
|
||||
@@ -1189,7 +1189,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
|
||||
@@ -1208,9 +1208,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
|
||||
@@ -1329,6 +1326,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()
|
||||
@@ -1365,6 +1363,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_all_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
|
||||
|
||||
@@ -73,10 +73,22 @@ 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 ), prefix='url_paste' )
|
||||
except Exception, e:
|
||||
@@ -88,7 +100,7 @@ class UploadToolAction( object ):
|
||||
log.exception( 'failure removing temporary file: %s' % temp_name )
|
||||
return self.upload_empty( trans, job, "Error:", str( e ) )
|
||||
try:
|
||||
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:
|
||||
log.exception( 'exception in add_file using url_paste temp_name %s: %s' % ( str( temp_name ), str( e ) ) )
|
||||
return self.upload_empty( trans, job, "Error:", str( e ) )
|
||||
@@ -134,9 +146,10 @@ class UploadToolAction( object ):
|
||||
log.info("End of tool %s execution for job id %d, memory used increased by %s" % ( tool.id, job.id, m1 ) )
|
||||
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
|
||||
def upload_empty(self, trans, job, err_code, err_msg):
|
||||
data = trans.app.model.HistoryDatasetAssociation( create_dataset=True )
|
||||
trans.app.security_agent.set_all_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
|
||||
@@ -161,12 +174,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 )
|
||||
@@ -236,6 +249,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_all_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:
|
||||
|
||||
@@ -85,6 +85,7 @@ mapped_chars = { '>' :'__gt__',
|
||||
']' :'__cb__',
|
||||
'{' :'__oc__',
|
||||
'}' :'__cc__',
|
||||
'@' : '__at__'
|
||||
}
|
||||
|
||||
def restore_text(text):
|
||||
@@ -123,7 +124,7 @@ class Params:
|
||||
|
||||
Operates on string or list values only (HTTP parameters).
|
||||
|
||||
>>> values = { 'status':'on', 'symbols':[ 'alpha', '<>', '$rm&#@!' ] }
|
||||
>>> values = { 'status':'on', 'symbols':[ 'alpha', '<>', '$rm&#!' ] }
|
||||
>>> par = Params(values)
|
||||
>>> par.status
|
||||
'on'
|
||||
@@ -132,9 +133,9 @@ class Params:
|
||||
>>> par.get('price', 0)
|
||||
0
|
||||
>>> par.symbols # replaces unknown symbols with X
|
||||
['alpha', '__lt____gt__', 'XrmXXX!']
|
||||
['alpha', '__lt____gt__', 'XrmXX!']
|
||||
>>> par.flatten() # flattening to a list
|
||||
[('status', 'on'), ('symbols', 'alpha'), ('symbols', '__lt____gt__'), ('symbols', 'XrmXXX!')]
|
||||
[('status', 'on'), ('symbols', 'alpha'), ('symbols', '__lt____gt__'), ('symbols', 'XrmXX!')]
|
||||
"""
|
||||
|
||||
# HACK: Need top prevent sanitizing certain parameter types. The
|
||||
@@ -264,6 +265,20 @@ 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
|
||||
elif isinstance( item, str ) and item.count( ',' ):
|
||||
return item.split( ',' )
|
||||
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
|
||||
|
||||
|
||||
@@ -57,6 +57,8 @@ def app_factory( global_conf, **kwargs ):
|
||||
# Create the universe WSGI application
|
||||
webapp = galaxy.web.framework.WebApplication( app, session_cookie='galaxysession' )
|
||||
add_controllers( webapp, app )
|
||||
# Force /history to go to /root/history -- needed since the tests assume this
|
||||
webapp.add_route( '/history', controller='root', action='history' )
|
||||
# These two routes handle our simple needs at the moment
|
||||
webapp.add_route( '/async/:tool_id/:data_id/:data_secret', controller='async', action='index', tool_id=None, data_id=None, data_secret=None )
|
||||
webapp.add_route( '/:controller/:action', action='index' )
|
||||
|
||||
@@ -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_all_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,278 @@
|
||||
from galaxy.web.base.controller import *
|
||||
from cgi import escape
|
||||
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
# States for passing messages
|
||||
SUCCESS, INFO, WARNING, ERROR = "done", "info", "warning", "error"
|
||||
|
||||
class HistoryController( BaseController ):
|
||||
|
||||
@web.expose
|
||||
def index( self, trans ):
|
||||
return ""
|
||||
|
||||
@web.expose
|
||||
def list_as_xml( self, trans ):
|
||||
"""
|
||||
XML history list for functional tests
|
||||
"""
|
||||
return trans.fill_template( "/history/list_as_xml.mako" )
|
||||
|
||||
@web.expose
|
||||
@web.require_login( "work with multiple histories" )
|
||||
def list( self, trans, id=[], operation=None, show_deleted = False, **kwd ):
|
||||
"""
|
||||
List all available histories
|
||||
"""
|
||||
# TODO: these two operations need to be updates still
|
||||
if operation:
|
||||
operation = operation.lower()
|
||||
if operation == "share":
|
||||
return self.share( trans, id, **kwd )
|
||||
elif operation == "rename":
|
||||
return self.rename( trans, id, **kwd )
|
||||
# Coerce ids to list
|
||||
if not isinstance( id, list ):
|
||||
id = id.split( "," )
|
||||
# Ensure ids are integers
|
||||
try:
|
||||
history_ids = map( int, id )
|
||||
except:
|
||||
return trans.show_error_message( "Invalid history id" )
|
||||
# Note that this page was loaded
|
||||
trans.log_event( "History id %s available" % str( id ) )
|
||||
# Display no message by default
|
||||
status, message = None, None
|
||||
refresh_history = False
|
||||
# If an operation was provided, load the histories and ensure they all
|
||||
# belong to the current user
|
||||
if history_ids and operation:
|
||||
histories = []
|
||||
for hid in history_ids:
|
||||
history = self.app.model.History.get( hid )
|
||||
if history:
|
||||
# Ensure history is owned by current user
|
||||
if history.user_id != None and trans.user:
|
||||
assert trans.user.id == history.user_id, "History does not belong to current user"
|
||||
histories.append( history )
|
||||
else:
|
||||
log.warn( "Invalid history id '%r' passed to list", hid )
|
||||
operation = operation.lower()
|
||||
if operation == "switch":
|
||||
status, message = self._list_switch( trans, histories )
|
||||
refresh_history = True
|
||||
elif operation == "share":
|
||||
## Caught above for now
|
||||
pass
|
||||
elif operation == "rename":
|
||||
## Caught above for now
|
||||
pass
|
||||
elif operation == "delete":
|
||||
status, message = self._list_delete( trans, histories )
|
||||
elif operation == "undelete":
|
||||
status, message = self._list_undelete( trans, histories )
|
||||
trans.app.model.flush()
|
||||
# Render the list view
|
||||
return trans.fill_template( "/history/list.mako",
|
||||
ids = id,
|
||||
user = trans.user,
|
||||
current_history = trans.history,
|
||||
show_deleted = util.string_as_bool( show_deleted ),
|
||||
refresh_history = refresh_history,
|
||||
message_type = status,
|
||||
message = message
|
||||
)
|
||||
|
||||
def _list_delete( self, trans, histories ):
|
||||
"""Delete histories"""
|
||||
n_deleted = 0
|
||||
deleted_current = False
|
||||
for history in histories:
|
||||
if not history.deleted:
|
||||
# Delete DefaultHistoryPermissions
|
||||
for dhp in history.default_permissions:
|
||||
dhp.delete()
|
||||
dhp.flush()
|
||||
# Mark history as deleted in db
|
||||
history.deleted = True
|
||||
# If deleting the current history, make a new current.
|
||||
if history == trans.history:
|
||||
deleted_current = True
|
||||
trans.new_history()
|
||||
trans.log_event( "History id %d marked as deleted" % history.id )
|
||||
n_deleted += 1
|
||||
status = SUCCESS
|
||||
message_parts = []
|
||||
if n_deleted:
|
||||
message_parts.append( "Deleted %d histories." % n_deleted )
|
||||
if deleted_current:
|
||||
message_parts.append( "Your active history was deleted, a new empty history is now active.")
|
||||
status = INFO
|
||||
return ( status, " ".join( message_parts ) )
|
||||
|
||||
def _list_undelete( self, trans, histories ):
|
||||
"""Undelete histories"""
|
||||
n_undeleted = 0
|
||||
n_already_purged = 0
|
||||
for history in histories:
|
||||
if history.purged:
|
||||
n_already_purged += 1
|
||||
if history.deleted:
|
||||
history.deleted = False
|
||||
n_undeleted += 1
|
||||
trans.log_event( "History id %d marked as undeleted" % history.id )
|
||||
status = SUCCESS
|
||||
message_parts = []
|
||||
if n_undeleted:
|
||||
message_parts.append( "Undeleted %d histories." % n_undeleted )
|
||||
if n_already_purged:
|
||||
message_parts.append( "%d have already been purged and cannot be undeleted." % n_already_purged )
|
||||
status = WARNING
|
||||
return status, "".join( message_parts )
|
||||
|
||||
def _list_switch( self, trans, histories ):
|
||||
"""Switch to a new different history"""
|
||||
new_history = histories[0]
|
||||
galaxy_session = trans.get_galaxy_session()
|
||||
try:
|
||||
association = trans.app.model.GalaxySessionToHistoryAssociation.filter_by( session_id=galaxy_session.id, history_id=new_history.id ).first()
|
||||
except:
|
||||
association = None
|
||||
new_history.add_galaxy_session( galaxy_session, association=association )
|
||||
new_history.flush()
|
||||
trans.set_history( new_history )
|
||||
trans.log_event( "History switched to id: %s, name: '%s'" % (str(new_history.id), new_history.name ) )
|
||||
# No message
|
||||
return None, None
|
||||
|
||||
## These have been moved from 'root' but not cleaned up
|
||||
|
||||
@web.expose
|
||||
@web.require_login( "share histories with other users" )
|
||||
def share( self, trans, id=None, email="", **kwd ):
|
||||
send_to_err = ""
|
||||
if not id:
|
||||
id = trans.get_history().id
|
||||
if not isinstance( id, list ):
|
||||
id = [ id ]
|
||||
histories = []
|
||||
history_names = []
|
||||
for hid in id:
|
||||
histories.append( trans.app.model.History.get( hid ) )
|
||||
history_names.append(histories[-1].name)
|
||||
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( trans.app.model.User.table.c.email==email ).first()
|
||||
params = util.Params( kwd )
|
||||
action = params.get( 'action', None )
|
||||
if 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 'history_share_btn' in kwd or action != 'share':
|
||||
# The user is attempting to share a history whose datasets cannot all be accessed by the other user. In this case,
|
||||
# the user sharing the history can chose to make the datasets public ( action == 'public' ) if he has the authority
|
||||
# to do so, or automatically create a new "sharing role" that allows the user to share his private datasets only with the
|
||||
# desired user ( action == 'private' ).
|
||||
can_change = {}
|
||||
cannot_change = {}
|
||||
for history in histories:
|
||||
for hda in history.activatable_datasets:
|
||||
# Only deal with datasets that have not been purged
|
||||
if not trans.app.security_agent.allow_action( send_to_user,
|
||||
trans.app.security_agent.permitted_actions.DATASET_ACCESS,
|
||||
dataset=hda ):
|
||||
# The user with which we are sharing the history does not have access permission on the current dataset
|
||||
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:
|
||||
# The current user has authority to change permissions on the current dataset because
|
||||
# they have permission to manage permissions on the dataset and the dataset is not associated
|
||||
# with a library.
|
||||
if action == "private":
|
||||
trans.app.security_agent.privately_share_dataset( hda.dataset, users=[ user, send_to_user ] )
|
||||
elif action == "public":
|
||||
trans.app.security_agent.make_dataset_public( hda.dataset )
|
||||
elif history not in can_change:
|
||||
# Build the set of histories / datasets on which the current user has authority
|
||||
# to "manage permissions". This is used in /history/share.mako
|
||||
can_change[ history ] = [ hda ]
|
||||
else:
|
||||
can_change[ history ].append( hda )
|
||||
else:
|
||||
if action in [ "private", "public" ]:
|
||||
# Don't change stuff that the user doesn't have permission to change
|
||||
continue
|
||||
elif history not in cannot_change:
|
||||
# Build the set of histories / datasets on which the current user does
|
||||
# not have authority to "manage permissions". This is used in /history/share.mako
|
||||
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( 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 ) ) )
|
||||
self.app.model.flush()
|
||||
return trans.show_message( "History (%s) has been shared with: %s" % ( ",".join( history_names ),email ) )
|
||||
return trans.fill_template( "/history/share.mako", histories=histories, email=email, send_to_err=send_to_err )
|
||||
|
||||
@web.expose
|
||||
@web.require_login( "rename histories" )
|
||||
def rename( self, trans, id=None, name=None, **kwd ):
|
||||
if trans.app.memory_usage:
|
||||
# Keep track of memory usage
|
||||
m0 = self.app.memory_usage.memory()
|
||||
user = trans.get_user()
|
||||
|
||||
if not isinstance( id, list ):
|
||||
if id != None:
|
||||
id = [ id ]
|
||||
if not isinstance( name, list ):
|
||||
if name != None:
|
||||
name = [ name ]
|
||||
histories = []
|
||||
cur_names = []
|
||||
if not id:
|
||||
if not trans.get_history().user:
|
||||
return trans.show_error_message( "You must save your history before renaming it." )
|
||||
id = [trans.get_history().id]
|
||||
for history_id in id:
|
||||
history = trans.app.model.History.get( history_id )
|
||||
if history and history.user_id == user.id:
|
||||
histories.append(history)
|
||||
cur_names.append(history.name)
|
||||
if not name or len(histories)!=len(name):
|
||||
return trans.fill_template( "/history/rename.mako",histories=histories )
|
||||
change_msg = ""
|
||||
for i in range(len(histories)):
|
||||
if histories[i].user_id == user.id:
|
||||
if name[i] == histories[i].name:
|
||||
change_msg = change_msg + "<p>History: "+cur_names[i]+" is already named: "+name[i]+"</p>"
|
||||
elif name[i] not in [None,'',' ']:
|
||||
name[i] = escape(name[i])
|
||||
histories[i].name = name[i]
|
||||
histories[i].flush()
|
||||
change_msg = change_msg + "<p>History: "+cur_names[i]+" renamed to: "+name[i]+"</p>"
|
||||
trans.log_event( "History renamed: id: %s, renamed to: '%s'" % (str(histories[i].id), name[i] ) )
|
||||
else:
|
||||
change_msg = change_msg + "<p>You must specify a valid name for History: "+cur_names[i]+"</p>"
|
||||
else:
|
||||
change_msg = change_msg + "<p>History: "+cur_names[i]+" does not appear to belong to you.</p>"
|
||||
if self.app.memory_usage:
|
||||
m1 = trans.app.memory_usage.memory( m0, pretty=True )
|
||||
log.info( "End of root/history_rename, memory used increased by %s" % m1 )
|
||||
return trans.show_message( "<p>%s" % change_msg, refresh_frames=['history'] )
|
||||
@@ -0,0 +1,120 @@
|
||||
from galaxy.web.base.controller import *
|
||||
from galaxy.model.orm import *
|
||||
import logging, tempfile, zipfile, tarfile, os, sys
|
||||
|
||||
if sys.version_info[:2] < ( 2, 6 ):
|
||||
zipfile.BadZipFile = zipfile.error
|
||||
if sys.version_info[:2] < ( 2, 5 ):
|
||||
zipfile.LargeZipFile = zipfile.error
|
||||
|
||||
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, default_action=kwd.get( 'default_action', None ) )
|
||||
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" )
|
||||
if not isinstance( import_ids, list ):
|
||||
import_ids = [ import_ids ]
|
||||
p = util.Params( kwd )
|
||||
if not p.action:
|
||||
return trans.show_error_message( "You must select an action to perform on selected datasets" )
|
||||
if p.action == 'add':
|
||||
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'] )
|
||||
else:
|
||||
# Can't use mkstemp - the file must not exist first
|
||||
try:
|
||||
tmpd = tempfile.mkdtemp()
|
||||
tmpf = os.path.join( tmpd, 'library_download.' + p.action )
|
||||
if p.action == 'zip':
|
||||
try:
|
||||
archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED, True )
|
||||
except RuntimeError:
|
||||
log.exception( "Compression error when opening zipfile for library download" )
|
||||
return trans.show_error_message( "ZIP compression is not available in this Python, please notify an administrator" )
|
||||
except (TypeError, zipfile.LargeZipFile):
|
||||
# ZIP64 is only in Python2.5+. Remove TypeError when 2.4 support is dropped
|
||||
log.warning( 'Max zip file size is 2GB, ZIP64 not supported' )
|
||||
archive = zipfile.ZipFile( tmpf, 'w', zipfile.ZIP_DEFLATED )
|
||||
archive.add = lambda x, y: archive.write( x, y.encode('CP437') )
|
||||
elif p.action == 'tgz':
|
||||
try:
|
||||
archive = tarfile.open( tmpf, 'w:gz' )
|
||||
except tarfile.CompressionError:
|
||||
log.exception( "Compression error when opening tarfile for library download" )
|
||||
return trans.show_error_message( "gzip compression is not available in this Python, please notify an administrator" )
|
||||
elif p.action == 'tbz':
|
||||
try:
|
||||
archive = tarfile.open( tmpf, 'w:bz2' )
|
||||
except tarfile.CompressionError:
|
||||
log.exception( "Compression error when opening tarfile for library download" )
|
||||
return trans.show_error_message( "bzip2 compression is not available in this Python, please notify an administrator" )
|
||||
except (OSError, zipfile.BadZipFile, tarfile.ReadError):
|
||||
log.exception( "Unable to create archive for download" )
|
||||
return trans.show_error_message( "Unable to create archive for download, please report this error" )
|
||||
seen = []
|
||||
for id in import_ids:
|
||||
lfda = trans.app.model.LibraryFolderDatasetAssociation.get( id )
|
||||
if not lfda or not trans.app.security_agent.allow_action( trans.user, trans.app.security_agent.permitted_actions.DATASET_ACCESS, dataset = lfda.dataset ):
|
||||
continue
|
||||
path = ""
|
||||
parent_folder = lfda.folder
|
||||
while parent_folder is not None:
|
||||
path = os.path.join( parent_folder.name, path )
|
||||
if parent_folder.parent is None:
|
||||
path = os.path.join( parent_folder.library_root[0].name, path )
|
||||
parent_folder = parent_folder.parent
|
||||
path += lfda.name
|
||||
while path in seen:
|
||||
path += '_'
|
||||
seen.append( path )
|
||||
try:
|
||||
archive.add( lfda.dataset.file_name, path )
|
||||
except IOError:
|
||||
log.exception( "Unable to write to temporary library download archive" )
|
||||
return trans.show_error_message( "Unable to create archive for download, please report this error" )
|
||||
archive.close()
|
||||
tmpfh = open( tmpf )
|
||||
# clean up now
|
||||
try:
|
||||
os.unlink( tmpf )
|
||||
os.rmdir( tmpd )
|
||||
except OSError:
|
||||
log.exception( "Unable to remove temporary library download archive and directory" )
|
||||
return trans.show_error_message( "Unable to create archive for download, please report this error" )
|
||||
trans.response.headers[ "Content-Disposition" ] = "attachment; filename=GalaxyLibraryFiles.%s" % kwd['action']
|
||||
return tmpfh
|
||||
@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_all_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 )
|
||||
@@ -324,87 +371,13 @@ class RootController( BaseController ):
|
||||
"""Displays a list of history related actions"""
|
||||
return trans.fill_template( "/history/options.mako",
|
||||
user = trans.get_user(), history = trans.get_history() )
|
||||
|
||||
|
||||
@web.expose
|
||||
def history_delete( self, trans, id=None, **kwd):
|
||||
"""Deletes a list of histories, ensures that histories are owned by current user"""
|
||||
if trans.app.memory_usage:
|
||||
# Keep track of memory usage
|
||||
m0 = self.app.memory_usage.memory()
|
||||
history_names = []
|
||||
if id:
|
||||
if isinstance( id, list ):
|
||||
history_ids = id
|
||||
else:
|
||||
history_ids = [ id ]
|
||||
user = trans.get_user()
|
||||
for hid in history_ids:
|
||||
try:
|
||||
int( hid )
|
||||
except:
|
||||
return trans.show_message( "Invalid history: %s" % str( hid ) )
|
||||
history = self.app.model.History.get( hid )
|
||||
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)
|
||||
history.deleted = True
|
||||
# If deleting the current history, make a new current.
|
||||
if history == trans.get_history():
|
||||
trans.new_history()
|
||||
else:
|
||||
return trans.show_message( "Not able to find history %s" % str( hid ) )
|
||||
self.app.model.flush()
|
||||
trans.log_event( "History id %s marked as deleted" % str(hid) )
|
||||
else:
|
||||
return trans.show_message( "You must select at least one history to delete." )
|
||||
if self.app.memory_usage:
|
||||
m1 = trans.app.memory_usage.memory( m0, pretty=True )
|
||||
log.info( "End of root/history_delete, memory used increased by %s" % m1 )
|
||||
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"""
|
||||
if trans.app.memory_usage:
|
||||
# Keep track of memory usage
|
||||
m0 = self.app.memory_usage.memory()
|
||||
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." )
|
||||
if self.app.memory_usage:
|
||||
m1 = trans.app.memory_usage.memory( m0, pretty=True )
|
||||
log.info( "End of root/history_undelete, memory used increased by %s" % m1 )
|
||||
return self.history_available( trans, id=','.join( id ), show_deleted=True, ok_msg = ok_msg, error_msg = " ".join( errors ) )
|
||||
def history_delete( self, trans, id ):
|
||||
"""
|
||||
Backward compatibility with check_galaxy script.
|
||||
"""
|
||||
return trans.webapp.controllers['history'].list( trans, id, operation='delete' )
|
||||
|
||||
@web.expose
|
||||
def clear_history( self, trans ):
|
||||
@@ -422,72 +395,6 @@ class RootController( BaseController ):
|
||||
m1 = trans.app.memory_usage.memory( m0, pretty=True )
|
||||
log.info( "End of root/clear_history, memory used increased by %s" % m1 )
|
||||
trans.response.send_redirect( url_for("/index" ) )
|
||||
|
||||
@web.expose
|
||||
@web.require_login( "share histories with other users" )
|
||||
def history_share( self, trans, id=None, email="", **kwd ):
|
||||
if trans.app.memory_usage:
|
||||
# Keep track of memory usage
|
||||
m0 = self.app.memory_usage.memory()
|
||||
send_to_err = ""
|
||||
if not id:
|
||||
id = trans.get_history().id
|
||||
if not isinstance( id, list ):
|
||||
id = [ id ]
|
||||
histories = []
|
||||
history_names = []
|
||||
for hid in id:
|
||||
histories.append( trans.app.model.History.get( hid ) )
|
||||
history_names.append(histories[-1].name)
|
||||
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()
|
||||
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:
|
||||
for history in histories:
|
||||
new_history = history.copy()
|
||||
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)) )
|
||||
self.app.model.flush()
|
||||
return trans.show_message( "History (%s) has been shared with: %s" % (",".join(history_names),email) )
|
||||
if self.app.memory_usage:
|
||||
m1 = trans.app.memory_usage.memory( m0, pretty=True )
|
||||
log.info( "End of root/history_share, memory used increased by %s" % m1 )
|
||||
return trans.fill_template( "/history/share.mako", histories=histories, email=email, send_to_err=send_to_err)
|
||||
|
||||
@web.expose
|
||||
@web.require_login( "work with multiple histories" )
|
||||
def history_available( self, trans, id=[], do_operation = "view", show_deleted = False, ok_msg = "", error_msg="", as_xml=False, **kwd ):
|
||||
"""
|
||||
List all available histories
|
||||
"""
|
||||
if trans.app.memory_usage:
|
||||
# Keep track of memory usage
|
||||
m0 = self.app.memory_usage.memory()
|
||||
if as_xml:
|
||||
trans.response.set_content_type('text/xml')
|
||||
return trans.fill_template( "/history/list_as_xml.mako" )
|
||||
if not isinstance( id, list ):
|
||||
id = id.split( "," )
|
||||
trans.log_event( "History id %s available" % str( id ) )
|
||||
history_operations = dict( share=self.history_share, rename=self.history_rename, delete=self.history_delete, undelete=self.history_undelete )
|
||||
if self.app.memory_usage:
|
||||
m1 = trans.app.memory_usage.memory( m0, pretty=True )
|
||||
log.info( "End of root/history_available, memory used increased by %s" % m1 )
|
||||
if do_operation in history_operations:
|
||||
return history_operations[do_operation]( trans, id=id, show_deleted=show_deleted, ok_msg=ok_msg, error_msg=error_msg, **kwd )
|
||||
return trans.fill_template( "/history/list.mako", ids=id,
|
||||
user=trans.get_user(),
|
||||
current_history=trans.get_history(),
|
||||
show_deleted=util.string_as_bool( show_deleted ),
|
||||
ok_msg=ok_msg, error_msg=error_msg )
|
||||
|
||||
|
||||
|
||||
@web.expose
|
||||
def history_import( self, trans, id=None, confirm=False, **kwd ):
|
||||
@@ -505,7 +412,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()
|
||||
@@ -544,34 +451,6 @@ class RootController( BaseController ):
|
||||
Warning! If you import this history, you will lose your current
|
||||
history. Click <a href="%s">here</a> to confirm.
|
||||
""" % web.url_for( id=id, confirm=True ) )
|
||||
|
||||
@web.expose
|
||||
@web.require_login( "switch histories" )
|
||||
def history_switch( self, trans, id=None ):
|
||||
if not id:
|
||||
return trans.response.send_redirect( web.url_for( action='history_available' ) )
|
||||
else:
|
||||
if trans.app.memory_usage:
|
||||
# Keep track of memory usage
|
||||
m0 = self.app.memory_usage.memory()
|
||||
new_history = trans.app.model.History.get( id )
|
||||
if new_history:
|
||||
galaxy_session = trans.get_galaxy_session()
|
||||
try:
|
||||
association = trans.app.model.GalaxySessionToHistoryAssociation.filter_by( session_id=galaxy_session.id, history_id=new_history.id ).first()
|
||||
except:
|
||||
association = None
|
||||
new_history.add_galaxy_session( galaxy_session, association=association )
|
||||
new_history.flush()
|
||||
trans.set_history( new_history )
|
||||
trans.log_event( "History switched to id: %s, name: '%s'" % (str(new_history.id), new_history.name ) )
|
||||
if self.app.memory_usage:
|
||||
m1 = trans.app.memory_usage.memory( m0, pretty=True )
|
||||
log.info( "End of root/history_switch, memory used increased by %s" % m1 )
|
||||
return trans.show_message( "History switched to: %s" % new_history.name,
|
||||
refresh_frames=['history'])
|
||||
else:
|
||||
return trans.show_error_message( "History not found" )
|
||||
|
||||
@web.expose
|
||||
def history_new( self, trans ):
|
||||
@@ -586,54 +465,7 @@ class RootController( BaseController ):
|
||||
return trans.show_message( "New history created", refresh_frames = ['history'] )
|
||||
|
||||
@web.expose
|
||||
@web.require_login( "renames histories" )
|
||||
def history_rename( self, trans, id=None, name=None, **kwd ):
|
||||
if trans.app.memory_usage:
|
||||
# Keep track of memory usage
|
||||
m0 = self.app.memory_usage.memory()
|
||||
user = trans.get_user()
|
||||
|
||||
if not isinstance( id, list ):
|
||||
if id != None:
|
||||
id = [ id ]
|
||||
if not isinstance( name, list ):
|
||||
if name != None:
|
||||
name = [ name ]
|
||||
histories = []
|
||||
cur_names = []
|
||||
if not id:
|
||||
if not trans.get_history().user:
|
||||
return trans.show_error_message( "You must save your history before renaming it." )
|
||||
id = [trans.get_history().id]
|
||||
for history_id in id:
|
||||
history = trans.app.model.History.get( history_id )
|
||||
if history and history.user_id == user.id:
|
||||
histories.append(history)
|
||||
cur_names.append(history.name)
|
||||
if not name or len(histories)!=len(name):
|
||||
return trans.fill_template( "/history/rename.mako",histories=histories )
|
||||
change_msg = ""
|
||||
for i in range(len(histories)):
|
||||
if histories[i].user_id == user.id:
|
||||
if name[i] == histories[i].name:
|
||||
change_msg = change_msg + "<p>History: "+cur_names[i]+" is already named: "+name[i]+"</p>"
|
||||
elif name[i] not in [None,'',' ']:
|
||||
name[i] = escape(name[i])
|
||||
histories[i].name = name[i]
|
||||
histories[i].flush()
|
||||
change_msg = change_msg + "<p>History: "+cur_names[i]+" renamed to: "+name[i]+"</p>"
|
||||
trans.log_event( "History renamed: id: %s, renamed to: '%s'" % (str(histories[i].id), name[i] ) )
|
||||
else:
|
||||
change_msg = change_msg + "<p>You must specify a valid name for History: "+cur_names[i]+"</p>"
|
||||
else:
|
||||
change_msg = change_msg + "<p>History: "+cur_names[i]+" does not appear to belong to you.</p>"
|
||||
if self.app.memory_usage:
|
||||
m1 = trans.app.memory_usage.memory( m0, pretty=True )
|
||||
log.info( "End of root/history_rename, memory used increased by %s" % m1 )
|
||||
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 +473,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_all_dataset_permissions( data.dataset, permissions )
|
||||
data.flush()
|
||||
data_file = open( data.file_name, "wb" )
|
||||
file_data.file.seek( 0 )
|
||||
@@ -660,9 +498,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 +547,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 +555,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 +606,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,6 +155,11 @@ class User( BaseController ):
|
||||
user = trans.app.model.User( email=email )
|
||||
user.set_password_cleartext( password )
|
||||
user.flush()
|
||||
trans.app.security_agent.create_private_user_role( user )
|
||||
# We set default user permissions, before we log in and set the default history permissions
|
||||
trans.app.security_agent.user_set_default_permissions( user )
|
||||
# The handle_user_login() method has a call to the history_set_default_permissions() method
|
||||
# (needed when logging in with a history), user needs to have default permissions set before logging in
|
||||
trans.handle_user_login( user )
|
||||
trans.log_event( "User created a new account" )
|
||||
trans.log_event( "User logged in" )
|
||||
@@ -139,9 +169,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 +178,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 +193,34 @@ 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 ]
|
||||
action = trans.app.security_agent.get_action( v.action ).action
|
||||
permissions[ 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) ) ) )
|
||||
|
||||
@@ -507,8 +534,8 @@ class WorkflowController( BaseController ):
|
||||
elif isinstance( input, Conditional ):
|
||||
values = input_values[ input.name ]
|
||||
current = values["__current_case__"]
|
||||
prefix = prefix + "|" + input.name
|
||||
visitor( input.cases[current].inputs, values, prefix )
|
||||
new_prefix = prefix + input.name + "|"
|
||||
visitor( input.cases[current].inputs, values, new_prefix )
|
||||
else:
|
||||
if isinstance( input, DataToolParameter ):
|
||||
prefixed_name = prefix + input.name
|
||||
|
||||
@@ -4,7 +4,7 @@ Galaxy web application framework
|
||||
|
||||
import pkg_resources
|
||||
|
||||
import os, sys, time
|
||||
import os, sys, time, socket
|
||||
pkg_resources.require( "Cheetah" )
|
||||
from Cheetah.Template import Template
|
||||
import base
|
||||
@@ -32,6 +32,17 @@ log = logging.getLogger( __name__ )
|
||||
|
||||
url_for = base.routes.url_for
|
||||
|
||||
UCSC_SERVERS = (
|
||||
'hgw1.cse.ucsc.edu',
|
||||
'hgw2.cse.ucsc.edu',
|
||||
'hgw3.cse.ucsc.edu',
|
||||
'hgw4.cse.ucsc.edu',
|
||||
'hgw5.cse.ucsc.edu',
|
||||
'hgw6.cse.ucsc.edu',
|
||||
'hgw7.cse.ucsc.edu',
|
||||
'hgw8.cse.ucsc.edu',
|
||||
)
|
||||
|
||||
def expose( func ):
|
||||
"""
|
||||
Decorator: mark a function as 'exposed' and thus web accessible
|
||||
@@ -60,6 +71,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 ):
|
||||
@@ -118,6 +142,8 @@ class UniverseWebTransaction( base.DefaultWebTransaction ):
|
||||
self.workflow_building_mode = False
|
||||
# Always have a valid galaxy session
|
||||
self.__ensure_valid_session( session_cookie )
|
||||
if self.app.config.require_login:
|
||||
self.__ensure_logged_in_user( environ )
|
||||
@property
|
||||
def sa_session( self ):
|
||||
"""
|
||||
@@ -223,8 +249,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",
|
||||
@@ -257,6 +282,28 @@ 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' )
|
||||
)
|
||||
display_as = url_for( controller='root', action='display_as' )
|
||||
if self.galaxy_session.user is None:
|
||||
if self.app.config.ucsc_display_sites and self.request.path == display_as:
|
||||
try:
|
||||
host = socket.gethostbyaddr( self.environ[ 'REMOTE_ADDR' ] )[0]
|
||||
except( socket.error, socket.herror, socket.gaierror, socket.timeout ):
|
||||
host = None
|
||||
if host in UCSC_SERVERS:
|
||||
return
|
||||
if 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
|
||||
@@ -282,16 +329,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, name='galaxysession' ):
|
||||
"""
|
||||
@@ -304,20 +351,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 ] )
|
||||
# This method is not called from the Galaxy reports, so the cookie will always be galaxysession
|
||||
self.__update_session_cookie( name='galaxysession' )
|
||||
def handle_user_logout( self ):
|
||||
@@ -373,6 +425,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
|
||||
@@ -385,7 +439,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
|
||||
@@ -431,12 +491,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.
|
||||
@@ -473,8 +533,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 )
|
||||
@@ -485,13 +545,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 ):
|
||||
"""
|
||||
@@ -511,3 +572,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.sendmail_path = kwargs.get('sendmail_path',"/usr/sbin/sendmail")
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -208,8 +208,8 @@ class ToolModule( object ):
|
||||
values = input_values[ input.name ]
|
||||
current = values["__current_case__"]
|
||||
label_prefix = label_prefix
|
||||
name_prefix = name_prefix + "|" + input.name
|
||||
visitor( input.cases[current].inputs, values, name_prefix, label_prefix )
|
||||
new_name_prefix = name_prefix + input.name + "|"
|
||||
visitor( input.cases[current].inputs, values, new_name_prefix, label_prefix )
|
||||
else:
|
||||
if isinstance( input, DataToolParameter ):
|
||||
data_inputs.append( dict( name=name_prefix+input.name, label=label_prefix+input.label, extensions=input.extensions ) )
|
||||
|
||||
@@ -46,6 +46,9 @@ use_lint = false
|
||||
# NEVER enable this on a public site (even test or QA)
|
||||
# use_interactive = true
|
||||
|
||||
# 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,230 @@
|
||||
#!/usr/bin/env python
|
||||
#Dan Blankenberg
|
||||
"""
|
||||
BACKUP YOUR DATABASE BEFORE RUNNING THIS SCRIPT!
|
||||
|
||||
This script has been tested with Postgres, SQLite, and MySQL databases.
|
||||
|
||||
This script 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 they are already set.
|
||||
Due to limitations of SQLite this script is unable to add foreign keys to existing SQLite tables (foreign keys are
|
||||
ignored by SQLite anyway).
|
||||
|
||||
REMEMBER TO BACKUP YOUR DATABASE BEFORE RUNNING THIS SCRIPT!
|
||||
"""
|
||||
|
||||
import sys, os, ConfigParser, tempfile
|
||||
import galaxy.app, galaxy.model
|
||||
import sqlalchemy
|
||||
from galaxy.model.orm import *
|
||||
|
||||
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 do the following:"
|
||||
print "1) Create new tables that do not exist in a pre-security/library database"
|
||||
print "2) Alter existing tables as necessary ( add new columns, indexes, etc )"
|
||||
print "3) Create a private role for each user"
|
||||
print "4) Set default permissions for each user ( set as 'manage permissions' and associated with the user's private role )"
|
||||
print "5) Set default permissions for each user's history that has not been purged ( set as 'manage permissions' and associated with the user's private role )"
|
||||
print "6) Set permissions on all appropriate datasets ( in each user's history ) that have not been purged ( set as 'manage permissions' and associated with the user's private role )"
|
||||
print
|
||||
print "*** It is critically important to backup your database before you continue. ***"
|
||||
print
|
||||
print "If you have backed up your database and would like to run this script, enter '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
|
||||
# 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:
|
||||
# default dialect is sqlite
|
||||
dialect = "sqlite"
|
||||
|
||||
# Now we alter existing 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 throw
|
||||
# exceptions )
|
||||
|
||||
# galaxy_user table - must be altered differently depending on if we are using sqlite or not
|
||||
if dialect == "sqlite":
|
||||
try:
|
||||
# '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?)
|
||||
app.model.session.execute( "ALTER TABLE 'galaxy_user' ADD COLUMN 'deleted' BOOLEAN default 0" )
|
||||
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 ) )
|
||||
|
||||
# history_dataset_association table - these alters are the same, regardless if we are using sqlite
|
||||
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 ) )
|
||||
|
||||
# metadata_file 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 ) )
|
||||
|
||||
|
||||
# Create indexes for new columns in the galaxy_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 ) )
|
||||
|
||||
|
||||
# Shutdown the app
|
||||
app.shutdown()
|
||||
del app
|
||||
print
|
||||
print "Columns added to tables, restarting app, with database_create_tables=True"
|
||||
|
||||
# Restart the app, this time with create_tables == True
|
||||
configuration['database_create_tables'] = True
|
||||
app = galaxy.app.UniverseApplication( global_conf = ini_file, **configuration )
|
||||
|
||||
# Add foreign key constraints as necessary for new 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:
|
||||
# SQLite ignores ( but parses on initial table creation ) foreign key constraints anyway
|
||||
# See: http://www.sqlite.org/omitted.html (there is some way to set up behavior using triggers)
|
||||
print_warning( "Adding foreign key constraints to table is not supported in SQLite." )
|
||||
|
||||
print "creating private roles and setting defaults for existing users and their histories and datasets"
|
||||
security_agent = app.security_agent
|
||||
# For each user:
|
||||
# 1. make sure they have a private role
|
||||
# 2. set DefaultUserPermissions
|
||||
# 3. set DefaultHisstoryPermissions on existing histories
|
||||
# 4. set ActionDatasetRoleAssociations on each history's activatable_datasets
|
||||
default_user_action = security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS.action
|
||||
|
||||
for user in app.model.User.query().all():
|
||||
print
|
||||
print "################"
|
||||
print "Setting up user %s." % user.email
|
||||
private_role = security_agent.get_private_user_role( user )
|
||||
if not private_role:
|
||||
security_agent.create_private_user_role( user )
|
||||
print "Created private role for %s" % user.email
|
||||
else:
|
||||
print_warning( "%s already has a private role, re-setting defaults anyway" % user.email )
|
||||
print "Setting DefaultUserPermissions for user %s" % user.email
|
||||
# Delete all of the current default permissions for the user
|
||||
for dup in user.default_permissions:
|
||||
dup.delete()
|
||||
dup.flush()
|
||||
# Add the new default permissions for the user
|
||||
dup = app.model.DefaultUserPermissions( user, default_user_action, private_role )
|
||||
dup.flush()
|
||||
# Set DefaultHistoryPermissions on all of the user's active histories and associated datasets
|
||||
# TODO: fix mapping for history.activatable_datasets so it doesn't throw an exception on the
|
||||
# following query when eagerloading activatable_datasets. Fix all of the queries below that
|
||||
# call history.active_datasets to be history.activatable_datasets when this works.
|
||||
histories = app.model.History.filter( and_( app.model.History.table.c.user_id==user.id,
|
||||
app.model.History.table.c.purged==False ) ) \
|
||||
.options( eagerload( 'active_datasets' ) ).all()
|
||||
print "Setting DefaultHistoryPermissions for %d un-purged histories associated with %s" % ( len( histories ), user.email )
|
||||
for history in histories:
|
||||
# Delete all of the current default permissions for the history
|
||||
for dhp in history.default_permissions:
|
||||
dhp.delete()
|
||||
dhp.flush()
|
||||
# Add the new default permissions for the history
|
||||
dhp = app.model.DefaultHistoryPermissions( history, default_user_action, private_role )
|
||||
dhp.flush()
|
||||
print "Setting ActionDatasetRoleAssociations for %d un-purged datasets in history %d" % ( len( history.active_datasets ), history.id )
|
||||
# Set the permissions on the current history's datasets that are not purged
|
||||
for hda in history.active_datasets:
|
||||
dataset = hda.dataset
|
||||
if dataset.library_associations:
|
||||
# Don't change permissions on a dataset associated with a library
|
||||
continue
|
||||
if [ assoc for assoc in dataset.history_associations if assoc.history not in user.histories ]:
|
||||
# Don't change permissions on a dataset associated with a history not owned by the user
|
||||
continue
|
||||
# Delete all of the current permissions on the dataset
|
||||
for adra in dataset.actions:
|
||||
adra.delete()
|
||||
adra.flush()
|
||||
# Add the new permissions on the dataset
|
||||
adra = app.model.ActionDatasetRoleAssociation( default_user_action, dataset, private_role )
|
||||
adra.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 |
@@ -142,6 +142,7 @@ div.form-row label
|
||||
|
||||
select, input, textarea
|
||||
{
|
||||
font: inherit;
|
||||
font-size: 115%;
|
||||
}
|
||||
|
||||
@@ -152,7 +153,7 @@ select, textarea, input[type="text"], input[type="file"]
|
||||
|
||||
/* Messages */
|
||||
|
||||
.errormessage, .warningmessage, .donemessage, .infomessage, .welcomeBlue, .welcomeRed
|
||||
.errormessage, .warningmessage, .donemessage, .infomessage, .welcomeBlue, .welcomeRed , .screencastBox, .yellowbox, .redbox, .bluebox, .greenbox
|
||||
{
|
||||
padding: 10px;
|
||||
padding-left: 52px;
|
||||
@@ -201,6 +202,85 @@ select, textarea, input[type="text"], input[type="file"]
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.screencastBox
|
||||
{
|
||||
padding-left: 10px;
|
||||
border-color: #AAAA66;
|
||||
background-color: #FFFFCC;
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.redbox
|
||||
{
|
||||
border: none;
|
||||
padding: 10px;
|
||||
border-color: #000000;
|
||||
background-color: #FF6666;
|
||||
background-image: none;
|
||||
border-right-width: 1px;
|
||||
border-right-style: dotted;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: dotted;
|
||||
margin-top: 5px;
|
||||
min-height: 32px;
|
||||
|
||||
}
|
||||
|
||||
.yellowbox
|
||||
{
|
||||
border: none;
|
||||
padding: 10px;
|
||||
border-color: #000000;
|
||||
background-color: #FFCC00;
|
||||
background-image: none;
|
||||
border-right-width: 1px;
|
||||
border-right-style: dotted;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: dotted;
|
||||
margin-top: 5px;
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
.bluebox
|
||||
{
|
||||
border: none;
|
||||
padding: 10px;
|
||||
border-color: #000000;
|
||||
background-color: #6666FF;
|
||||
background-image: none;
|
||||
border-right-width: 1px;
|
||||
border-right-style: dotted;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: dotted;
|
||||
margin-top: 5px;
|
||||
color: #FFFFFF;
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
.greenbox
|
||||
{
|
||||
border: none;
|
||||
padding: 10px;
|
||||
border-color: #000000;
|
||||
background-color: #00CC00;
|
||||
background-image: none;
|
||||
border-right-width: 1px;
|
||||
border-right-style: dotted;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: dotted;
|
||||
margin-top: 5px;
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
.redbox li, .yellowbox li, .bluebox li, .greenbox li {
|
||||
list-style: disc;
|
||||
text-transform: none;
|
||||
list-style-position: inside;
|
||||
list-style-image: none;
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
|
||||
.errormessagesmall, .warningmessagesmall, .donemessagesmall, .infomessagesmall
|
||||
{
|
||||
padding: 5px;
|
||||
@@ -283,6 +363,11 @@ table.colored tr
|
||||
background: $table_row_bg;
|
||||
}
|
||||
|
||||
table.colored tr.odd_row
|
||||
{
|
||||
background: $odd_row_bg;
|
||||
}
|
||||
|
||||
div.debug
|
||||
{
|
||||
margin: 10px;
|
||||
@@ -350,6 +435,40 @@ 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;
|
||||
}
|
||||
|
||||
## State colors
|
||||
|
||||
.state-color-queued {
|
||||
border-color: $history_queued_border;
|
||||
background: $history_queued_bg;
|
||||
}
|
||||
|
||||
.state-color-ok {
|
||||
border-color: $history_ok_border;
|
||||
background: $history_ok_bg;
|
||||
}
|
||||
|
||||
.state-color-error {
|
||||
border-color: $history_error_border;
|
||||
background: $history_error_bg;
|
||||
}
|
||||
|
||||
.state-color-running {
|
||||
border-color: $history_running_border;
|
||||
background: $history_running_bg;
|
||||
}
|
||||
|
||||
## Button styles
|
||||
|
||||
.action-button {
|
||||
background: #eeeeee;
|
||||
color: #333;
|
||||
@@ -365,6 +484,9 @@ ul.toolParameterExpandableCollapsable
|
||||
-moz-border-radius: 0.5em;
|
||||
-webkit-border-radius: 0.5em;
|
||||
border-radius: 0.5em;
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.action-button > * {
|
||||
@@ -373,13 +495,15 @@ ul.toolParameterExpandableCollapsable
|
||||
|
||||
.action-button:hover {
|
||||
color: black;
|
||||
background: #aaaaaa;
|
||||
background: #dddddd;
|
||||
}
|
||||
.action-button:active {
|
||||
color: white;
|
||||
background: #aaaaaa;
|
||||
}
|
||||
|
||||
## Popup menu styles
|
||||
|
||||
div.popupmenu {
|
||||
display: none;
|
||||
background: #eeeeee;
|
||||
@@ -394,6 +518,9 @@ div.popupmenu {
|
||||
-moz-border-radius: 0.5em;
|
||||
-webkit-border-radius: 0.5em;
|
||||
border-radius: 0.5em;
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
div.popupmenu-item {
|
||||
@@ -416,4 +543,40 @@ div.popupmenu-item:hover {
|
||||
|
||||
.popup-arrow:hover {
|
||||
color: black;
|
||||
}
|
||||
|
||||
div.permissionContainer {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
## Data grid style
|
||||
|
||||
.grid {
|
||||
padding-top: 1em;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
.grid tbody td {
|
||||
border-top: solid #DDDDDD 1px;
|
||||
border-bottom: solid #DDDDDD 1px;
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
.grid thead th {
|
||||
background: $table_header_bg;
|
||||
background-image: url(form_title_bg.png);
|
||||
background-repeat: repeat-x;
|
||||
background-position: top;
|
||||
border-top: solid $table_border 1px;
|
||||
border-bottom: solid $table_border 1px;
|
||||
padding: 0.5em 1em;
|
||||
text-align: left;
|
||||
}
|
||||
.grid tfoot td {
|
||||
background-color: #F8F8F8;
|
||||
border-top: solid #DDDDDD 1px;
|
||||
border-bottom: solid #DDDDDD 1px;
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
.grid .current {
|
||||
background-color: #EEEEFF;
|
||||
}
|
||||
@@ -16,6 +16,7 @@ img
|
||||
border: 0;
|
||||
}
|
||||
|
||||
|
||||
a:link, a:visited, a:active
|
||||
{
|
||||
color: #303030;
|
||||
@@ -141,6 +142,7 @@ div.form-row label
|
||||
|
||||
select, input, textarea
|
||||
{
|
||||
font: inherit;
|
||||
font-size: 115%;
|
||||
}
|
||||
|
||||
@@ -151,7 +153,7 @@ select, textarea, input[type="text"], input[type="file"]
|
||||
|
||||
/* Messages */
|
||||
|
||||
.errormessage, .warningmessage, .donemessage, .infomessage, .welcomeBlue, .welcomeRed
|
||||
.errormessage, .warningmessage, .donemessage, .infomessage, .welcomeBlue, .welcomeRed , .screencastBox, .yellowbox, .redbox, .bluebox, .greenbox
|
||||
{
|
||||
padding: 10px;
|
||||
padding-left: 52px;
|
||||
@@ -200,6 +202,85 @@ select, textarea, input[type="text"], input[type="file"]
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.screencastBox
|
||||
{
|
||||
padding-left: 10px;
|
||||
border-color: #AAAA66;
|
||||
background-color: #FFFFCC;
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.redbox
|
||||
{
|
||||
border: none;
|
||||
padding: 10px;
|
||||
border-color: #000000;
|
||||
background-color: #FF6666;
|
||||
background-image: none;
|
||||
border-right-width: 1px;
|
||||
border-right-style: dotted;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: dotted;
|
||||
margin-top: 5px;
|
||||
min-height: 32px;
|
||||
|
||||
}
|
||||
|
||||
.yellowbox
|
||||
{
|
||||
border: none;
|
||||
padding: 10px;
|
||||
border-color: #000000;
|
||||
background-color: #FFCC00;
|
||||
background-image: none;
|
||||
border-right-width: 1px;
|
||||
border-right-style: dotted;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: dotted;
|
||||
margin-top: 5px;
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
.bluebox
|
||||
{
|
||||
border: none;
|
||||
padding: 10px;
|
||||
border-color: #000000;
|
||||
background-color: #6666FF;
|
||||
background-image: none;
|
||||
border-right-width: 1px;
|
||||
border-right-style: dotted;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: dotted;
|
||||
margin-top: 5px;
|
||||
color: #FFFFFF;
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
.greenbox
|
||||
{
|
||||
border: none;
|
||||
padding: 10px;
|
||||
border-color: #000000;
|
||||
background-color: #00CC00;
|
||||
background-image: none;
|
||||
border-right-width: 1px;
|
||||
border-right-style: dotted;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: dotted;
|
||||
margin-top: 5px;
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
.redbox li, .yellowbox li, .bluebox li, .greenbox li {
|
||||
list-style: disc;
|
||||
text-transform: none;
|
||||
list-style-position: inside;
|
||||
list-style-image: none;
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
|
||||
.errormessagesmall, .warningmessagesmall, .donemessagesmall, .infomessagesmall
|
||||
{
|
||||
padding: 5px;
|
||||
@@ -282,6 +363,11 @@ table.colored tr
|
||||
background: white;
|
||||
}
|
||||
|
||||
table.colored tr.odd_row
|
||||
{
|
||||
background: #DADFEF;
|
||||
}
|
||||
|
||||
div.debug
|
||||
{
|
||||
margin: 10px;
|
||||
@@ -293,7 +379,7 @@ div.debug
|
||||
|
||||
div.odd_row
|
||||
{
|
||||
background: #FFFF99;
|
||||
background: #DADFEF;
|
||||
}
|
||||
|
||||
#footer {
|
||||
@@ -348,6 +434,38 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
.state-color-queued {
|
||||
border-color: #888888;
|
||||
background: #EEEEEE;
|
||||
}
|
||||
|
||||
.state-color-ok {
|
||||
border-color: #66AA66;
|
||||
background: #CCFFCC;
|
||||
}
|
||||
|
||||
.state-color-error {
|
||||
border-color: #AA6666;
|
||||
background: #FFCCCC;
|
||||
}
|
||||
|
||||
.state-color-running {
|
||||
border-color: #AAAA66;
|
||||
background: #FFFFCC;
|
||||
}
|
||||
|
||||
|
||||
.action-button {
|
||||
background: #eeeeee;
|
||||
color: #333;
|
||||
@@ -363,6 +481,9 @@ ul.toolParameterExpandableCollapsable
|
||||
-moz-border-radius: 0.5em;
|
||||
-webkit-border-radius: 0.5em;
|
||||
border-radius: 0.5em;
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.action-button > * {
|
||||
@@ -371,13 +492,14 @@ ul.toolParameterExpandableCollapsable
|
||||
|
||||
.action-button:hover {
|
||||
color: black;
|
||||
background: #aaaaaa;
|
||||
background: #dddddd;
|
||||
}
|
||||
.action-button:active {
|
||||
color: white;
|
||||
background: #aaaaaa;
|
||||
}
|
||||
|
||||
|
||||
div.popupmenu {
|
||||
display: none;
|
||||
background: #eeeeee;
|
||||
@@ -392,6 +514,9 @@ div.popupmenu {
|
||||
-moz-border-radius: 0.5em;
|
||||
-webkit-border-radius: 0.5em;
|
||||
border-radius: 0.5em;
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
div.popupmenu-item {
|
||||
@@ -414,4 +539,39 @@ div.popupmenu-item:hover {
|
||||
|
||||
.popup-arrow:hover {
|
||||
color: black;
|
||||
}
|
||||
|
||||
div.permissionContainer {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
|
||||
.grid {
|
||||
padding-top: 1em;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
.grid tbody td {
|
||||
border-top: solid #DDDDDD 1px;
|
||||
border-bottom: solid #DDDDDD 1px;
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
.grid thead th {
|
||||
background: #ebd9b2;
|
||||
background-image: url(form_title_bg.png);
|
||||
background-repeat: repeat-x;
|
||||
background-position: top;
|
||||
border-top: solid #d8b365 1px;
|
||||
border-bottom: solid #d8b365 1px;
|
||||
padding: 0.5em 1em;
|
||||
text-align: left;
|
||||
}
|
||||
.grid tfoot td {
|
||||
background-color: #F8F8F8;
|
||||
border-top: solid #DDDDDD 1px;
|
||||
border-bottom: solid #DDDDDD 1px;
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
.grid .current {
|
||||
background-color: #EEEEFF;
|
||||
}
|
||||
@@ -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,71 @@
|
||||
.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;
|
||||
}
|
||||
|
||||
span.expandLink {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -118,35 +131,9 @@ body
|
||||
right: 0px;
|
||||
z-index: 200;
|
||||
}
|
||||
td.panel-header
|
||||
{
|
||||
background: #f5f5f5;
|
||||
background-image: url(panel_header_bg.png);
|
||||
background-position: top center;
|
||||
background-repeat: repeat-x;
|
||||
padding: 0;
|
||||
border-bottom: solid #999 1px;
|
||||
}
|
||||
div.panel-header
|
||||
{
|
||||
margin: 0;
|
||||
padding: 4px;
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table.column-layout
|
||||
{
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.unified-panel-header {
|
||||
height: 2em;
|
||||
overflow: hidden;
|
||||
z-index: 1000;
|
||||
background: #cccccc;
|
||||
background-image: url(panel_header_bg.png);
|
||||
@@ -159,19 +146,21 @@ table.column-layout
|
||||
padding-left: 10px;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
|
||||
.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,50 +186,14 @@ div.unified-panel-body {
|
||||
color: black;
|
||||
background: #aaaaaa;
|
||||
}
|
||||
|
||||
.panel-header-button:active {
|
||||
color: white;
|
||||
background: #aaaaaa;
|
||||
}
|
||||
|
||||
div.popupmenu {
|
||||
display: none;
|
||||
background: #eee;
|
||||
position: fixed;
|
||||
z-index: 20000;
|
||||
background: #cccccc;
|
||||
background-image: url(panel_header_bg.png);
|
||||
background-position: top center;
|
||||
background-repeat: repeat-x;
|
||||
border: solid #999 1px;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
|
||||
div.popupmenu-top {
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
div.popupmenu-top-inner {
|
||||
height: 10px;
|
||||
width: 20px;
|
||||
margin-top: -13px;
|
||||
margin-left: auto;
|
||||
margin-right: 20px;
|
||||
background: url(popupmenu_callout_top.png);
|
||||
}
|
||||
|
||||
div.popupmenu-item {
|
||||
padding: 3px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.popupmenu-item:hover {
|
||||
background: #AAAAEE;
|
||||
}
|
||||
|
||||
#overlay {
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
top: 0; left: 0; width: 100%; height: 100%;
|
||||
z-index: 20000;
|
||||
}
|
||||
@@ -271,6 +224,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,71 @@
|
||||
.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;
|
||||
}
|
||||
|
||||
span.expandLink {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#from galaxy import eggs
|
||||
#import pkg_resources
|
||||
#pkg_resources.require("Cheetah")
|
||||
|
||||
import sys, string, os.path
|
||||
from galaxy import eggs
|
||||
#from galaxy import eggs
|
||||
import pkg_resources
|
||||
pkg_resources.require( "Cheetah" )
|
||||
|
||||
@@ -18,8 +22,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" ) ]
|
||||
@@ -69,7 +73,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
|
||||
@@ -79,3 +84,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;
|
||||
@@ -118,31 +131,6 @@ body
|
||||
right: 0px;
|
||||
z-index: 200;
|
||||
}
|
||||
td.panel-header
|
||||
{
|
||||
background: ${panel_header_bg_top};
|
||||
background-image: url(panel_header_bg.png);
|
||||
background-position: top center;
|
||||
background-repeat: repeat-x;
|
||||
padding: 0;
|
||||
border-bottom: solid ${layout_border} 1px;
|
||||
}
|
||||
div.panel-header
|
||||
{
|
||||
margin: 0;
|
||||
padding: 4px;
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table.column-layout
|
||||
{
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.unified-panel-header {
|
||||
height: 2em;
|
||||
@@ -158,6 +146,9 @@ table.column-layout
|
||||
padding-left: 10px;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.unified-panel-header-inner {
|
||||
@@ -201,43 +192,6 @@ div.unified-panel-body {
|
||||
background: #aaaaaa;
|
||||
}
|
||||
|
||||
div.popupmenu {
|
||||
display: none;
|
||||
background: #eee;
|
||||
position: fixed;
|
||||
z-index: 20000;
|
||||
background: ${panel_header_bg_bottom};
|
||||
background-image: url(panel_header_bg.png);
|
||||
background-position: top center;
|
||||
background-repeat: repeat-x;
|
||||
border: solid ${layout_border} 1px;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
|
||||
div.popupmenu-top {
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
div.popupmenu-top-inner {
|
||||
height: 10px;
|
||||
width: 20px;
|
||||
margin-top: -13px;
|
||||
margin-left: auto;
|
||||
margin-right: 20px;
|
||||
background: url(popupmenu_callout_top.png);
|
||||
}
|
||||
|
||||
div.popupmenu-item {
|
||||
padding: 3px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.popupmenu-item:hover {
|
||||
background: ${layout_hover};
|
||||
}
|
||||
|
||||
#overlay {
|
||||
position: fixed;
|
||||
top: 0; left: 0; width: 100%; height: 100%;
|
||||
@@ -268,4 +222,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;
|
||||
}
|
||||
@@ -1,3 +1,19 @@
|
||||
$.fn.makeAbsolute = function(rebase) {
|
||||
return this.each(function() {
|
||||
var el = $(this);
|
||||
var pos = el.position();
|
||||
el.css({
|
||||
position: "absolute",
|
||||
marginLeft: 0, marginTop: 0,
|
||||
top: pos.top, left: pos.left,
|
||||
right: $(window).width() - ( pos.left + el.width() )
|
||||
});
|
||||
if (rebase) {
|
||||
el.remove().appendTo("body");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
jQuery(document).ready( function() {
|
||||
// Links with confirmation
|
||||
jQuery( "a[@confirm]" ).click( function() {
|
||||
|
||||
@@ -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.name}</li>
|
||||
%endfor
|
||||
</ul>
|
||||
%if not anchored:
|
||||
<a name="${curr_anchor}"></a>
|
||||
<div style="float: right;"><a href="#TOP">top</a></div>
|
||||
%endif
|
||||
</td>
|
||||
</tr>
|
||||
</%def>
|
||||
|
||||
<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.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( 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.name.upper().startswith( curr_anchor ):
|
||||
<% anchored = False %>
|
||||
%endif
|
||||
%if render_quick_find and role.name.upper().startswith( curr_anchor ):
|
||||
%if not anchored:
|
||||
${render_row( role, groups, users, ctr, anchored, curr_anchor )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_row( role, groups, users, ctr, anchored, curr_anchor )}
|
||||
%endif
|
||||
%elif render_quick_find:
|
||||
%for anchor in anchors[ anchor_loc: ]:
|
||||
%if role.name.upper().startswith( anchor ):
|
||||
%if not anchored:
|
||||
<% curr_anchor = anchor %>
|
||||
${render_row( role, groups, users, ctr, anchored, curr_anchor )}
|
||||
<% anchored = True %>
|
||||
%else:
|
||||
${render_row( role, groups, users, ctr, anchored, curr_anchor )}
|
||||
%endif
|
||||
<%
|
||||
anchor_loc = anchors.index( anchor )
|
||||
break
|
||||
%>
|
||||
%endif
|
||||
%endfor
|
||||
%else:
|
||||
${render_row( role, groups, users, ctr, True, '' )}
|
||||
%endif
|
||||
%endfor
|
||||
</table>
|
||||
%endif
|
||||
@@ -0,0 +1,83 @@
|
||||
<%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() {
|
||||
$('#roles_add_button').click(function() {
|
||||
return !$('#out_roles option:selected').remove().appendTo('#in_roles');
|
||||
});
|
||||
$('#roles_remove_button').click(function() {
|
||||
return !$('#in_roles option:selected').remove().appendTo('#out_roles');
|
||||
});
|
||||
$('#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');
|
||||
});
|
||||
$('form#associate_group_role_user').submit(function() {
|
||||
$('#in_roles option').each(function(i) {
|
||||
$(this).attr("selected", "selected");
|
||||
});
|
||||
$('#in_users option').each(function(i) {
|
||||
$(this).attr("selected", "selected");
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">Group '${group.name}'</div>
|
||||
<div class="toolFormBody">
|
||||
<form name="associate_group_role_user" id="associate_group_role_user" action="${h.url_for( action='group', group_id=group.id )}" method="post" >
|
||||
<div class="form-row">
|
||||
<div style="float: left; margin-right: 10px;">
|
||||
Roles associated with '${group.name}'<br/>
|
||||
${render_select( "in_roles", in_roles )}<br/>
|
||||
<input type="submit" id="roles_remove_button" value=">>"/>
|
||||
</div>
|
||||
<div>
|
||||
Roles not associated with '${group.name}'<br/>
|
||||
${render_select( "out_roles", out_roles )}<br/>
|
||||
<input type="submit" id="roles_add_button" value="<<"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div style="float: left; margin-right: 10px;">
|
||||
Users associated with '${group.name}'<br/>
|
||||
${render_select( "in_users", in_users )}<br/>
|
||||
<input type="submit" id="users_remove_button" value=">>"/>
|
||||
</div>
|
||||
<div>
|
||||
Users not associated with '${group.name}'<br/>
|
||||
${render_select( "out_users", out_users )}<br/>
|
||||
<input type="submit" id="users_add_button" value="<<"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<input type="submit" name="group_roles_users_edit_button" value="Save"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,86 @@
|
||||
<%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() {
|
||||
$('#roles_add_button').click(function() {
|
||||
return !$('#out_roles option:selected').remove().appendTo('#in_roles');
|
||||
});
|
||||
$('#roles_remove_button').click(function() {
|
||||
return !$('#in_roles option:selected').remove().appendTo('#out_roles');
|
||||
});
|
||||
$('#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');
|
||||
});
|
||||
$('form#associate_group_role_user').submit(function() {
|
||||
$('#in_roles option').each(function(i) {
|
||||
$(this).attr("selected", "selected");
|
||||
});
|
||||
$('#in_users option').each(function(i) {
|
||||
$(this).attr("selected", "selected");
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">Create Role</div>
|
||||
<div class="toolFormBody">
|
||||
<form name="associate_group_role_user" id="associate_group_role_user" action="${h.url_for( action='create_group' )}" method="post" >
|
||||
<div class="form-row">
|
||||
Name: <input name="name" type="textfield" value="" size=40"/>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div style="float: left; margin-right: 10px;">
|
||||
Groups associated with new role<br/>
|
||||
${render_select( "in_roles", in_roles )}<br/>
|
||||
<input type="submit" id="roles_remove_button" value=">>"/>
|
||||
</div>
|
||||
<div>
|
||||
Groups not associated with new role<br/>
|
||||
${render_select( "out_roles", out_roles )}<br/>
|
||||
<input type="submit" id="roles_add_button" value="<<"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div style="float: left; margin-right: 10px;">
|
||||
Users associated with new role<br/>
|
||||
${render_select( "in_users", in_users )}<br/>
|
||||
<input type="submit" id="users_remove_button" value=">>"/>
|
||||
</div>
|
||||
<div>
|
||||
Users not associated with new role<br/>
|
||||
${render_select( "out_users", out_users )}<br/>
|
||||
<input type="submit" id="users_add_button" value="<<"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<input type="submit" name="create_group_button" value="Save"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -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,134 @@
|
||||
<%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', group_id=group.id )}">Change associated users and 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>
|
||||
<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', group_id=group.id, user_id=user.id )}">Remove user from group</a>
|
||||
</div>
|
||||
</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.name}</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>
|
||||
%else:
|
||||
${role.name}
|
||||
%endif
|
||||
</li>
|
||||
%endfor
|
||||
</ul>
|
||||
%if not anchored:
|
||||
<a name="${curr_anchor}"></a>
|
||||
<div style="float: right;"><a href="#TOP">top</a></div>
|
||||
%endif
|
||||
</td>
|
||||
</tr>
|
||||
</%def>
|
||||
|
||||
<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,87 @@
|
||||
<%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() {
|
||||
$('#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');
|
||||
});
|
||||
$('#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');
|
||||
});
|
||||
$('form#associate_role_group_user').submit(function() {
|
||||
$('#in_groups option').each(function(i) {
|
||||
$(this).attr("selected", "selected");
|
||||
});
|
||||
$('#in_users option').each(function(i) {
|
||||
$(this).attr("selected", "selected");
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
%if msg:
|
||||
${render_msg( msg, messagetype )}
|
||||
%endif
|
||||
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">Create Role</div>
|
||||
<div class="toolFormBody">
|
||||
<form name="associate_role_group_user" id="associate_role_group_user" action="${h.url_for( action='create_role' )}" method="post" >
|
||||
<div class="form-row">
|
||||
Name: <input name="name" type="textfield" value="" size=40"/>
|
||||
Description: <input name="description" type="textfield" value="" size=40"/>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div style="float: left; margin-right: 10px;">
|
||||
Groups associated with new role<br/>
|
||||
${render_select( "in_groups", in_groups )}<br/>
|
||||
<input type="submit" id="groups_remove_button" value=">>"/>
|
||||
</div>
|
||||
<div>
|
||||
Groups not associated with new role<br/>
|
||||
${render_select( "out_groups", out_groups )}<br/>
|
||||
<input type="submit" id="groups_add_button" value="<<"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div style="float: left; margin-right: 10px;">
|
||||
Users associated with new role<br/>
|
||||
${render_select( "in_users", in_users )}<br/>
|
||||
<input type="submit" id="users_remove_button" value=">>"/>
|
||||
</div>
|
||||
<div>
|
||||
Users not associated with new role<br/>
|
||||
${render_select( "out_users", out_users )}<br/>
|
||||
<input type="submit" id="users_add_button" value="<<"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<input type="submit" name="create_role_button" value="Save"/>
|
||||
</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">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>
|
||||
<a href="${h.url_for( controller='admin', action='role', role_id=role.id )}">${role.name}</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='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,83 @@
|
||||
<%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() {
|
||||
$('#roles_add_button').click(function() {
|
||||
return !$('#out_roles option:selected').remove().appendTo('#in_roles');
|
||||
});
|
||||
$('#roles_remove_button').click(function() {
|
||||
return !$('#in_roles option:selected').remove().appendTo('#out_roles');
|
||||
});
|
||||
$('#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_user_role_group').submit(function() {
|
||||
$('#in_roles 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">User '${user.email}'</div>
|
||||
<div class="toolFormBody">
|
||||
<form name="associate_user_role_group" id="associate_user_role_group" action="${h.url_for( action='user', user_id=user.id )}" method="post" >
|
||||
<div class="form-row">
|
||||
<div style="float: left; margin-right: 10px;">
|
||||
Roles associated with '${user.email}'<br/>
|
||||
${render_select( "in_roles", in_roles )}<br/>
|
||||
<input type="submit" id="roles_remove_button" value=">>"/>
|
||||
</div>
|
||||
<div>
|
||||
Roles not associated with '${user.email}'<br/>
|
||||
${render_select( "out_roles", out_roles )}<br/>
|
||||
<input type="submit" id="roles_add_button" value="<<"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div style="float: left; margin-right: 10px;">
|
||||
Groups associated with '${user.email}'<br/>
|
||||
${render_select( "in_groups", in_groups )}<br/>
|
||||
<input type="submit" id="groups_remove_button" value=">>"/>
|
||||
</div>
|
||||
<div>
|
||||
Groups not associated with '${user.email}'<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="user_roles_groups_edit_button" value="Save"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,134 @@
|
||||
<%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( controller='admin', action='user', user_id=user.id )}">Change associated groups and 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>
|
||||
<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_group_from_user', user_id=user.id, group_id=group.id )}">Remove group from user</a>
|
||||
</div>
|
||||
</li>
|
||||
%endfor
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
%for role in roles:
|
||||
<li>
|
||||
<a href="${h.url_for( controller='admin', action='role', role_id=role.id )}">${role.name}</a>
|
||||
<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_user', user_id=user.id, role_id=role.id )}">Remove role from user</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>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,64 @@
|
||||
## 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>
|
||||
</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.name}</option>
|
||||
%endfor
|
||||
</select>
|
||||
</div>
|
||||
<div class="toolParamHelp" style="clear: both;">
|
||||
Multi-select list - hold the appropriate key while clicking to select multiple roles. More restrictions can be applied after the upload is complete. Selecting no roles makes a dataset public.
|
||||
</div>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
<div class="form-row">
|
||||
<input type="submit" class="primary-button" name="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,37 @@
|
||||
<%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 key, val in toolbox.tool_panel.items():
|
||||
%if key.startswith( 'tool' ):
|
||||
<option value="${val.id}">${val.name}</option>
|
||||
%elif key.startswith( 'section' ):
|
||||
<optgroup label="${val.name}">
|
||||
<% section = val %>
|
||||
%for section_key, section_val in section.elems.items():
|
||||
%if section_key.startswith( 'tool' ):
|
||||
<option value="${section_val.id}">${section_val.name}</option>
|
||||
%endif
|
||||
%endfor
|
||||
%endif
|
||||
%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,39 +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 key, val in toolbox.tool_panel.items():
|
||||
%if key.startswith( 'tool' ):
|
||||
<option value="${val.id}">${val.name}</option>
|
||||
%elif key.startswith( 'section' ):
|
||||
<optgroup label="${val.name}">
|
||||
<% section = val %>
|
||||
%for section_key, section_val in section.elems.items():
|
||||
%if section_key.startswith( 'tool' ):
|
||||
<option value="${section_val.id}">${section_val.name}</option>
|
||||
%endif
|
||||
%endfor
|
||||
%endif
|
||||
%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.name}</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.name}</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.name}</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>
|
||||
|
||||
@@ -1,65 +1,201 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%def name="title()">Your saved histories</%def>
|
||||
|
||||
%if error_msg:
|
||||
<p>
|
||||
<div class="errormessage">${error_msg}</div>
|
||||
<div style="clear: both"></div>
|
||||
</p>
|
||||
%endif
|
||||
%if ok_msg:
|
||||
<p>
|
||||
<div class="donemessage">${ok_msg}</div>
|
||||
<div style="clear: both"></div>
|
||||
</p>
|
||||
%if message:
|
||||
<p>
|
||||
<div class="${message_type}message transient-message">${message}</div>
|
||||
<div style="clear: both"></div>
|
||||
</p>
|
||||
%endif
|
||||
|
||||
%if user.histories:
|
||||
<h1 style="margin-bottom:0px;">Stored Histories</h1>
|
||||
%if show_deleted:
|
||||
<div><a href="${h.url_for( action='history_available', id=','.join( ids ), show_deleted=False )}">hide deleted</a></div>
|
||||
%else:
|
||||
<div><a href="${h.url_for( action='history_available', id=','.join( ids ), show_deleted=True )}">show deleted</a></div>
|
||||
%endif
|
||||
<form name="history_actions" action="${h.url_for( action='history_available')}" method="post" >
|
||||
<table class="colored" border="0" cellspacing="0" cellpadding="0" width="100%">
|
||||
<tr class="header" align="center"><td>Name</td><td>Size</td><td>Last modified</td><td>Actions</td></tr>
|
||||
%for history in user.histories:
|
||||
%if ( show_deleted and not history.purged ) or not( history.deleted ):
|
||||
<tr>
|
||||
<td>
|
||||
<input type=checkbox name="id" value="${history.id}"
|
||||
%if str(history.id) in ids:
|
||||
checked
|
||||
%endif
|
||||
>${history.name}
|
||||
%if history == trans.get_history():
|
||||
(current history)
|
||||
%endif
|
||||
</td>
|
||||
<td>${len(history.active_datasets)}</td>
|
||||
<td>${str(history.update_time)[:19]}</td>
|
||||
<td>
|
||||
%if not history.deleted:
|
||||
<a href="${h.url_for( action='history_rename', id=history.id )}">rename</a><br />
|
||||
<a href="${h.url_for( action='history_switch', id=history.id )}">switch to</a><br />
|
||||
<a href="${h.url_for( action='history_delete', id=history.id )}" confirm="Are you sure you want to delete this history?">delete</a><br />
|
||||
%else:
|
||||
<a href="${h.url_for( action='history_undelete', id=history.id )}">undelete</a><br />
|
||||
%endif
|
||||
</td>
|
||||
</tr>
|
||||
<%def name="javascripts()">
|
||||
${parent.javascripts()}
|
||||
<script type="text/javascript">
|
||||
%if refresh_history:
|
||||
if ( parent.frames && parent.frames.galaxy_history ) {
|
||||
parent.frames.galaxy_history.location.href="${h.url_for( controller='root', action='history')}";
|
||||
}
|
||||
%endif
|
||||
%endfor
|
||||
<tr><th colspan="100%">Action</th></tr>
|
||||
<tr><td colspan="100%" align="center"><input type="radio" name="do_operation" value="share" checked>Share <input type="radio" name="do_operation" value="rename">Rename <input type="radio" name="do_operation" value="delete">Delete
|
||||
%if show_deleted:
|
||||
<input type="radio" name="do_operation" value="undelete">Undelete
|
||||
%endif
|
||||
</td></tr>
|
||||
<tr><td colspan="100%" align="center"><input type="submit" name="submit" value="Perform Action"></td></tr>
|
||||
</table>
|
||||
|
||||
## TODO: generalize and move into galaxy.base.js
|
||||
$(function() {
|
||||
$(".grid").each( function() {
|
||||
var grid = this;
|
||||
var checkboxes = $(this).find("input.grid-row-select-checkbox");
|
||||
var update = $(this).find( "span.grid-selected-count" );
|
||||
$(checkboxes).each( function() {
|
||||
$(this).change( function() {
|
||||
var n = $(checkboxes).filter("[checked]").size();
|
||||
update.text( n );
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="stylesheets()">
|
||||
<link href="${h.url_for('/static/style/base.css')}" rel="stylesheet" type="text/css" />
|
||||
<style>
|
||||
.count-box {
|
||||
width: 1.1em;
|
||||
float: left;
|
||||
padding: 5px;
|
||||
margin-right: 5px;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
text-align: center;
|
||||
}
|
||||
.count-box-dummy {
|
||||
width: 1.1em;
|
||||
float: left;
|
||||
padding: 5px;
|
||||
margin-right: 5px;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
background: transparent;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</%def>
|
||||
|
||||
|
||||
<h2>Stored Histories</h2>
|
||||
|
||||
<ul class="manage-table-actions">
|
||||
<li>
|
||||
%if show_deleted:
|
||||
<a href="${h.url_for( show_deleted=False )}" class="action-button">Hide deleted</a></div>
|
||||
%else:
|
||||
<a href="${h.url_for( show_deleted=True )}" class="action-button">Show deleted</a></div>
|
||||
%endif
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<form name="history_actions" action="${h.url_for()}" method="post" >
|
||||
%if show_deleted:
|
||||
<input type="hidden" name="show_deleted" value="true">
|
||||
%endif
|
||||
|
||||
<table class="grid">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="1.5em"></th>
|
||||
<th>Name (click to activate)</th>
|
||||
<th>Datasets (by state)</th>
|
||||
<th>Status</th>
|
||||
<th>Last update</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
%for i, history in enumerate( user.histories ):
|
||||
|
||||
%if ( show_deleted and not history.purged ) or not( history.deleted ):
|
||||
|
||||
<tr class="${["","current"][ history == trans.get_history() ]}">
|
||||
|
||||
<td><input type="checkbox" name="id" value="${history.id}" class="grid-row-select-checkbox"></td>
|
||||
|
||||
<td>
|
||||
%if not history.deleted:
|
||||
<a href="${h.url_for( operation="switch", id=history.id, show_deleted=show_deleted )}">${history.name}</a>
|
||||
<a id="h-${i}-popup" class="popup-arrow" style="display: none;">▼</a>
|
||||
%else:
|
||||
${history.name} <a id="h-${i}-popup" class="popup-arrow" style="display: none;">▼</a>
|
||||
%endif
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
<%
|
||||
total_ok = sum( 1 for d in history.active_datasets if d.state == 'ok' )
|
||||
total_running = sum( 1 for d in history.active_datasets if d.state == 'running' )
|
||||
total_queued = sum( 1 for d in history.active_datasets if d.state == 'queued' )
|
||||
total_error = sum( 1 for d in history.active_datasets if d.state == 'error' )
|
||||
%>
|
||||
|
||||
%if total_ok:
|
||||
<div class="count-box state-color-ok">${total_ok}</div>
|
||||
%else:
|
||||
<div class="count-box-dummy"></div>
|
||||
%endif
|
||||
|
||||
%if total_error:
|
||||
<div class="count-box state-color-error">${total_error}</div
|
||||
%else:
|
||||
<div class="count-box-dummy"></div>
|
||||
%endif
|
||||
|
||||
%if total_running:
|
||||
<div class="count-box state-color-running">${total_running}</div>
|
||||
%else:
|
||||
<div class="count-box-dummy"></div>
|
||||
%endif
|
||||
|
||||
%if total_queued:
|
||||
<div class="count-box state-color-queued">${total_queued}</div>
|
||||
%else:
|
||||
<div class="count-box-dummy"></div>
|
||||
%endif
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
%if history == trans.get_history():
|
||||
active
|
||||
%endif
|
||||
%if history.deleted:
|
||||
deleted
|
||||
%endif
|
||||
</td>
|
||||
|
||||
<td>${h.date.distance_of_time_in_words( history.update_time, h.date.datetime.utcnow() )}</td>
|
||||
|
||||
<td>
|
||||
<div popupmenu="h-${i}-popup">
|
||||
|
||||
%if not history.deleted:
|
||||
<a class="action-button" href="${h.url_for( operation='rename', id=history.id, show_deleted=show_deleted )}">rename</a> <br />
|
||||
<a class="action-button" href="${h.url_for( operation='switch', id=history.id, show_deleted=show_deleted )}">switch to</a> <br />
|
||||
<a class="action-button" href="${h.url_for( operation='delete', id=history.id , show_deleted=show_deleted)}" confirm="Are you sure you want to delete this history?">delete</a> <br />
|
||||
%else:
|
||||
<a class="action-button" href="${h.url_for( operation='undelete', id=history.id, show_deleted=show_deleted )}">undelete</a><br />
|
||||
%endif
|
||||
|
||||
</div>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
%endif
|
||||
|
||||
%endfor
|
||||
|
||||
</tbody>
|
||||
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td colspan="6">
|
||||
For <span class="grid-selected-count"></span> selected histories:
|
||||
<input type="submit" name="operation" value="Share" class="action-button">
|
||||
<input type="submit" name="operation" value="Rename" class="action-button">
|
||||
<input type="submit" name="operation" value="Delete" class="action-button">
|
||||
%if show_deleted:
|
||||
<input type="submit" name="operation" value="Undelete" class="action-button">
|
||||
%endif
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
|
||||
</table>
|
||||
|
||||
</form>
|
||||
%else:
|
||||
You have no stored histories
|
||||
%endif
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -11,15 +11,16 @@
|
||||
|
||||
<ul>
|
||||
%if user:
|
||||
<li><a href="${h.url_for( controller='root', action='history_rename', id=history.id )}" target="galaxy_main">Rename</a> current history (stored as "${history.name}")</li>
|
||||
<li><a href="${h.url_for( controller='root', action='history_available')}" target="galaxy_main">List</a> previously stored histories</li>
|
||||
<li><a href="${h.url_for( controller='history', action='rename', id=history.id )}" target="galaxy_main">Rename</a> current history (stored as "${history.name}")</li>
|
||||
<li><a href="${h.url_for( controller='history', action='list')}" target="galaxy_main">List</a> previously stored histories</li>
|
||||
%if len( history.active_datasets ) > 0:
|
||||
<li><a href="${h.url_for( controller='root', action='history_new' )}">Create</a> a new empty history</li>
|
||||
%endif
|
||||
<li><a href="${h.url_for( controller='workflow', action='build_from_current_history' )}">Construct workflow</a> from the current history</li>
|
||||
<li><a href="${h.url_for( controller='root', action='history_share' )}" target="galaxy_main">Share</a> current history</div>
|
||||
<li><a href="${h.url_for( controller='history', action='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>
|
||||
<li><a href="${h.url_for( controller='history', action='list', operation="delete", id=history.id )}" confirm="Are you sure you want to delete the current history?">Delete</a> current history</div>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -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
|
||||