root/galaxy-central/lib/galaxy/web/framework/__init__.py @ 2

リビジョン 2, 37.3 KB (コミッタ: hatakeyama, 14 年 前)

import galaxy-central

行番号 
1"""
2Galaxy web application framework
3"""
4
5import pkg_resources
6
7import os, sys, time, socket, random, string
8pkg_resources.require( "Cheetah" )
9from Cheetah.Template import Template
10import base
11import pickle
12from galaxy import util
13from galaxy.util.json import to_json_string, from_json_string
14
15pkg_resources.require( "simplejson" )
16import simplejson
17
18import helpers
19
20pkg_resources.require( "PasteDeploy" )
21from paste.deploy.converters import asbool
22
23pkg_resources.require( "Mako" )
24import mako.template
25import mako.lookup
26import mako.runtime
27
28pkg_resources.require( "Babel" )
29from babel.support import Translations
30from babel import Locale
31
32pkg_resources.require( "SQLAlchemy >= 0.4" )
33from sqlalchemy import and_
34from sqlalchemy.orm.exc import NoResultFound
35
36pkg_resources.require( "pexpect" )
37pkg_resources.require( "amqplib" )
38
39import logging
40log = logging.getLogger( __name__ )
41
42url_for = base.routes.url_for
43
44UCSC_SERVERS = (
45    'hgw1.cse.ucsc.edu',
46    'hgw2.cse.ucsc.edu',
47    'hgw3.cse.ucsc.edu',
48    'hgw4.cse.ucsc.edu',
49    'hgw5.cse.ucsc.edu',
50    'hgw6.cse.ucsc.edu',
51    'hgw7.cse.ucsc.edu',
52    'hgw8.cse.ucsc.edu',
53)
54
55def expose( func ):
56    """
57    Decorator: mark a function as 'exposed' and thus web accessible
58    """
59    func.exposed = True
60    return func
61   
62def json( func ):
63    def decorator( self, trans, *args, **kwargs ):
64        trans.response.set_content_type( "text/javascript" )
65        return simplejson.dumps( func( self, trans, *args, **kwargs ) )
66    if not hasattr(func, '_orig'):
67        decorator._orig = func
68    decorator.exposed = True
69    return decorator
70
71def json_pretty( func ):
72    def decorator( self, trans, *args, **kwargs ):
73        trans.response.set_content_type( "text/javascript" )
74        return simplejson.dumps( func( self, trans, *args, **kwargs ), indent=4, sort_keys=True )
75    if not hasattr(func, '_orig'):
76        decorator._orig = func
77    decorator.exposed = True
78    return decorator
79
80def require_login( verb="perform this action", use_panels=False, webapp='galaxy' ):
81    def argcatcher( func ):
82        def decorator( self, trans, *args, **kwargs ):
83            if trans.get_user():
84                return func( self, trans, *args, **kwargs )
85            else:
86                return trans.show_error_message(
87                    'You must be <a target="_top" href="%s">logged in</a> to %s</div>.'
88                    % ( url_for( controller='user', action='login', webapp=webapp ), verb ), use_panels=use_panels )     
89        return decorator
90    return argcatcher
91   
92def expose_api( func ):
93    def decorator( self, trans, *args, **kwargs ):
94        def error( environ, start_response ):
95            start_response( error_status, [('Content-type', 'text/plain')] )
96            return error_message
97        error_status = '403 Forbidden'
98        if 'key' not in kwargs:
99            error_message = 'No API key provided with request, please consult the API documentation.'
100            return error
101        try:
102            provided_key = trans.sa_session.query( trans.app.model.APIKeys ).filter( trans.app.model.APIKeys.table.c.key == kwargs['key'] ).one()
103        except NoResultFound:
104            error_message = 'Provided API key is not valid.'
105            return error
106        newest_key = provided_key.user.api_keys[0]
107        if newest_key.key != provided_key.key:
108            error_message = 'Provided API key has expired.'
109            return error
110        if trans.request.body:
111            try:
112                payload = util.recursively_stringify_dictionary_keys( simplejson.loads( trans.request.body ) )
113                kwargs['payload'] = payload
114            except ValueError:
115                error_status = '400 Bad Request'
116                error_message = 'Your request did not appear to be valid JSON, please consult the API documentation'
117                return error
118        trans.response.set_content_type( "application/json" )
119        trans.set_user( provided_key.user )
120        if trans.debug:
121            return simplejson.dumps( func( self, trans, *args, **kwargs ), indent=4, sort_keys=True )
122        else:
123            return simplejson.dumps( func( self, trans, *args, **kwargs ) )
124    if not hasattr(func, '_orig'):
125        decorator._orig = func
126    decorator.exposed = True
127    return decorator
128
129def require_admin( func ):
130    def decorator( self, trans, *args, **kwargs ):
131        admin_users = trans.app.config.get( "admin_users", "" ).split( "," )
132        if not admin_users:
133            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." )
134        user = trans.get_user()
135        if not user:
136            return trans.show_error_message( "You must be logged in as an administrator to access this feature." )
137        if not user.email in admin_users:
138            return trans.show_error_message( "You must be an administrator to access this feature." )
139        return func( self, trans, *args, **kwargs )
140    return decorator
141
142NOT_SET = object()
143
144class MessageException( Exception ):
145    """
146    Exception to make throwing errors from deep in controllers easier
147    """
148    def __init__( self, err_msg, type="info" ):
149        self.err_msg = err_msg
150        self.type = type
151       
152def error( message ):
153    raise MessageException( message, type='error' )
154
155def form( *args, **kwargs ):
156    return FormBuilder( *args, **kwargs )
157   
158class WebApplication( base.WebApplication ):
159    def __init__( self, galaxy_app, session_cookie='galaxysession' ):
160        base.WebApplication.__init__( self )
161        self.set_transaction_factory( lambda e: self.transaction_chooser( e, galaxy_app, session_cookie ) )
162        # Mako support
163        self.mako_template_lookup = mako.lookup.TemplateLookup(
164            directories = [ galaxy_app.config.template_path ] ,
165            module_directory = galaxy_app.config.template_cache,
166            collection_size = 500,
167            output_encoding = 'utf-8' )
168        # Security helper
169        self.security = galaxy_app.security
170    def handle_controller_exception( self, e, trans, **kwargs ):
171        if isinstance( e, MessageException ):
172            return trans.show_message( e.err_msg, e.type )
173    def make_body_iterable( self, trans, body ):
174        if isinstance( body, FormBuilder ):
175            body = trans.show_form( body )
176        return base.WebApplication.make_body_iterable( self, trans, body )
177    def transaction_chooser( self, environ, galaxy_app, session_cookie ):
178        if 'is_api_request' in environ:
179            return GalaxyWebAPITransaction( environ, galaxy_app, self )
180        else:
181            return GalaxyWebUITransaction( environ, galaxy_app, self, session_cookie )
182   
183class GalaxyWebTransaction( base.DefaultWebTransaction ):
184    """
185    Encapsulates web transaction specific state for the Galaxy application
186    (specifically the user's "cookie" session and history)
187    """
188    def __init__( self, environ, app, webapp ):
189        self.app = app
190        self.webapp = webapp
191        self.security = webapp.security
192        base.DefaultWebTransaction.__init__( self, environ )
193        self.setup_i18n()
194        self.sa_session.expunge_all()
195        self.debug = asbool( self.app.config.get( 'debug', False ) )
196        # Flag indicating whether we are in workflow building mode (means
197        # that the current history should not be used for parameter values
198        # and such).
199        self.workflow_building_mode = False
200    def setup_i18n( self ):
201        if 'HTTP_ACCEPT_LANGUAGE' in self.environ:
202            # locales looks something like: ['en', 'en-us;q=0.7', 'ja;q=0.3']
203            locales = self.environ['HTTP_ACCEPT_LANGUAGE'].split( ',' )
204            locales = [ Locale.parse(l.split( ';' )[0], sep='-').language for l in locales ]
205        else:
206            # Default to English
207            locales = 'en'
208        t = Translations.load( dirname='locale', locales=locales, domain='ginga' )
209        self.template_context.update ( dict( _=t.ugettext, n_=t.ugettext, N_=t.ungettext ) )
210    @property
211    def sa_session( self ):
212        """
213        Returns a SQLAlchemy session -- currently just gets the current
214        session from the threadlocal session context, but this is provided
215        to allow migration toward a more SQLAlchemy 0.4 style of use.
216        """
217        return self.app.model.context.current
218    def log_action( self, user=None, action=None, context=None, params=None):
219        """
220        Application-level logging of user actions.
221        """
222        if self.app.config.log_actions:
223            action = self.app.model.UserAction(action=action, context=context, params=unicode( to_json_string( params ) ) )
224            try:
225                if user:
226                    action.user = user
227                else:
228                    action.user = self.user
229            except:
230                action.user = None
231            try:
232                action.session_id = self.galaxy_session.id   
233            except:
234                action.session_id = None
235            self.sa_session.add( action )
236            self.sa_session.flush()
237    def log_event( self, message, tool_id=None, **kwargs ):
238        """
239        Application level logging. Still needs fleshing out (log levels and such)
240        Logging events is a config setting - if False, do not log.
241        """
242        if self.app.config.log_events:
243            event = self.app.model.Event()
244            event.tool_id = tool_id
245            try:
246                event.message = message % kwargs
247            except:
248                event.message = message
249            try:
250                event.history = self.get_history()
251            except:
252                event.history = None
253            try:
254                event.history_id = self.history.id
255            except:
256                event.history_id = None
257            try:
258                event.user = self.user
259            except:
260                event.user = None
261            try:
262                event.session_id = self.galaxy_session.id   
263            except:
264                event.session_id = None
265            self.sa_session.add( event )
266            self.sa_session.flush()
267    def get_cookie( self, name='galaxysession' ):
268        """Convenience method for getting a session cookie"""
269        try:
270            # If we've changed the cookie during the request return the new value
271            if name in self.response.cookies:
272                return self.response.cookies[name].value
273            else:
274                return self.request.cookies[name].value
275        except:
276            return None
277    def set_cookie( self, value, name='galaxysession', path='/', age=90, version='1' ):
278        """Convenience method for setting a session cookie"""
279        # The galaxysession cookie value must be a high entropy 128 bit random number encrypted
280        # using a server secret key.  Any other value is invalid and could pose security issues.
281        self.response.cookies[name] = value
282        self.response.cookies[name]['path'] = path
283        self.response.cookies[name]['max-age'] = 3600 * 24 * age # 90 days
284        tstamp = time.localtime ( time.time() + 3600 * 24 * age )
285        self.response.cookies[name]['expires'] = time.strftime( '%a, %d-%b-%Y %H:%M:%S GMT', tstamp )
286        self.response.cookies[name]['version'] = version
287    def _ensure_valid_session( self, session_cookie ):
288        """
289        Ensure that a valid Galaxy session exists and is available as
290        trans.session (part of initialization)
291       
292        Support for universe_session and universe_user cookies has been
293        removed as of 31 Oct 2008.
294        """
295        sa_session = self.sa_session
296        # Try to load an existing session
297        secure_id = self.get_cookie( name=session_cookie )
298        galaxy_session = None
299        prev_galaxy_session = None
300        user_for_new_session = None
301        invalidate_existing_session = False
302        # Track whether the session has changed so we can avoid calling flush
303        # in the most common case (session exists and is valid).
304        galaxy_session_requires_flush = False
305        if secure_id:
306            # Decode the cookie value to get the session_key
307            session_key = self.security.decode_guid( secure_id )
308            try:
309                # Make sure we have a valid UTF-8 string
310                session_key = session_key.encode( 'utf8' )
311            except UnicodeDecodeError:
312                # We'll end up creating a new galaxy_session
313                session_key = None
314            if session_key:
315                # Retrieve the galaxy_session id via the unique session_key
316                galaxy_session = self.sa_session.query( self.app.model.GalaxySession ) \
317                                                .filter( and_( self.app.model.GalaxySession.table.c.session_key==session_key,
318                                                               self.app.model.GalaxySession.table.c.is_valid==True ) ) \
319                                                .first()
320        # If remote user is in use it can invalidate the session, so we need to to check some things now.
321        if self.app.config.use_remote_user:
322            assert "HTTP_REMOTE_USER" in self.environ, \
323                "use_remote_user is set but no HTTP_REMOTE_USER variable"
324            remote_user_email = self.environ[ 'HTTP_REMOTE_USER' ]   
325            if galaxy_session:
326                # An existing session, make sure correct association exists
327                if galaxy_session.user is None:
328                    # No user, associate
329                    galaxy_session.user = self.__get_or_create_remote_user( remote_user_email )
330                    galaxy_session_requires_flush = True
331                elif galaxy_session.user.email != remote_user_email:
332                    # Session exists but is not associated with the correct remote user
333                    invalidate_existing_session = True
334                    user_for_new_session = self.__get_or_create_remote_user( remote_user_email )
335                    log.warning( "User logged in as '%s' externally, but has a cookie as '%s' invalidating session",
336                                 remote_user_email, galaxy_session.user.email )
337            else:
338                # No session exists, get/create user for new session
339                user_for_new_session = self.__get_or_create_remote_user( remote_user_email )
340        else:
341            if galaxy_session is not None and galaxy_session.user and galaxy_session.user.external:
342                # Remote user support is not enabled, but there is an existing
343                # session with an external user, invalidate
344                invalidate_existing_session = True
345                log.warning( "User '%s' is an external user with an existing session, invalidating session since external auth is disabled",
346                             galaxy_session.user.email )
347            elif galaxy_session is not None and galaxy_session.user is not None and galaxy_session.user.deleted:
348                invalidate_existing_session = True
349                log.warning( "User '%s' is marked deleted, invalidating session" % galaxy_session.user.email )
350        # Do we need to invalidate the session for some reason?
351        if invalidate_existing_session:
352            prev_galaxy_session = galaxy_session
353            prev_galaxy_session.is_valid = False
354            galaxy_session = None
355        # No relevant cookies, or couldn't find, or invalid, so create a new session
356        if galaxy_session is None:
357            galaxy_session = self.__create_new_session( prev_galaxy_session, user_for_new_session )
358            galaxy_session_requires_flush = True
359            self.galaxy_session = galaxy_session
360            self.__update_session_cookie( name=session_cookie )
361        else:
362            self.galaxy_session = galaxy_session
363        # Do we need to flush the session?
364        if galaxy_session_requires_flush:
365            self.sa_session.add( galaxy_session )
366            # FIXME: If prev_session is a proper relation this would not
367            #        be needed.
368            if prev_galaxy_session:
369                self.sa_session.add( prev_galaxy_session )           
370            self.sa_session.flush()
371        # If the old session was invalid, get a new history with our new session
372        if invalidate_existing_session:
373            self.new_history()
374    def _ensure_logged_in_user( self, environ ):
375        allowed_paths = (
376            url_for( controller='root', action='index' ),
377            url_for( controller='root', action='tool_menu' ),
378            url_for( controller='root', action='masthead' ),
379            url_for( controller='root', action='history' ),
380            url_for( controller='user', action='login' ),
381            url_for( controller='user', action='create' ),
382            url_for( controller='user', action='reset_password' ),
383            url_for( controller='library', action='browse' )
384        )
385        display_as = url_for( controller='root', action='display_as' )
386        if self.galaxy_session.user is None:
387            if self.app.config.ucsc_display_sites and self.request.path == display_as:
388                try:
389                    host = socket.gethostbyaddr( self.environ[ 'REMOTE_ADDR' ] )[0]
390                except( socket.error, socket.herror, socket.gaierror, socket.timeout ):
391                    host = None
392                if host in UCSC_SERVERS:
393                    return
394            if self.request.path not in allowed_paths:
395                self.response.send_redirect( url_for( controller='root', action='index' ) )
396    def __create_new_session( self, prev_galaxy_session=None, user_for_new_session=None ):
397        """
398        Create a new GalaxySession for this request, possibly with a connection
399        to a previous session (in `prev_galaxy_session`) and an existing user
400        (in `user_for_new_session`).
401       
402        Caller is responsible for flushing the returned session.
403        """
404        session_key = self.security.get_new_guid()
405        galaxy_session = self.app.model.GalaxySession(
406            session_key=session_key,
407            is_valid=True,
408            remote_host = self.request.remote_host,
409            remote_addr = self.request.remote_addr,
410            referer = self.request.headers.get( 'Referer', None ) )
411        if prev_galaxy_session:
412            # Invalidated an existing session for some reason, keep track
413            galaxy_session.prev_session_id = prev_galaxy_session.id
414        if user_for_new_session:
415            # The new session should be associated with the user
416            galaxy_session.user = user_for_new_session
417        return galaxy_session
418    def __get_or_create_remote_user( self, remote_user_email ):
419        """
420        Return the user in $HTTP_REMOTE_USER and create if necessary
421        """
422        # remote_user middleware ensures HTTP_REMOTE_USER exists
423        user = self.sa_session.query( self.app.model.User ) \
424                              .filter( self.app.model.User.table.c.email==remote_user_email ) \
425                              .first()
426        if user:
427            # GVK: June 29, 2009 - This is to correct the behavior of a previous bug where a private
428            # role and default user / history permissions were not set for remote users.  When a
429            # remote user authenticates, we'll look for this information, and if missing, create it.
430            if not self.app.security_agent.get_private_user_role( user ):
431                self.app.security_agent.create_private_user_role( user )
432            if not user.default_permissions:
433                self.app.security_agent.user_set_default_permissions( user, history=True, dataset=True )
434        elif user is None:
435            random.seed()
436            user = self.app.model.User( email=remote_user_email )
437            user.set_password_cleartext( ''.join( random.sample( string.letters + string.digits, 12 ) ) )
438            user.external = True
439            self.sa_session.add( user )
440            self.sa_session.flush()
441            self.app.security_agent.create_private_user_role( user )
442            # We set default user permissions, before we log in and set the default history permissions
443            self.app.security_agent.user_set_default_permissions( user )
444            #self.log_event( "Automatically created account '%s'", user.email )
445        return user
446    def __update_session_cookie( self, name='galaxysession' ):
447        """
448        Update the session cookie to match the current session.
449        """
450        self.set_cookie( self.security.encode_guid( self.galaxy_session.session_key ), name=name, path=self.app.config.cookie_path )
451    def handle_user_login( self, user, webapp ):
452        """
453        Login a new user (possibly newly created)
454           - create a new session
455           - associate new session with user
456           - if old session had a history and it was not associated with a user, associate it with the new session,
457             otherwise associate the current session's history with the user
458        """
459        # Set the previous session
460        prev_galaxy_session = self.galaxy_session
461        prev_galaxy_session.is_valid = False
462        # Define a new current_session
463        self.galaxy_session = self.__create_new_session( prev_galaxy_session, user )
464        if webapp == 'galaxy':
465            cookie_name = 'galaxysession'
466            # Associated the current user's last accessed history (if exists) with their new session
467            history = None
468            try:
469                users_last_session = user.galaxy_sessions[0]
470                last_accessed = True
471            except:
472                users_last_session = None
473                last_accessed = False
474            if prev_galaxy_session.current_history and \
475                not prev_galaxy_session.current_history.deleted and \
476                prev_galaxy_session.current_history.datasets:
477                if prev_galaxy_session.current_history.user is None or prev_galaxy_session.current_history.user == user:
478                    # If the previous galaxy session had a history, associate it with the new
479                    # session, but only if it didn't belong to a different user.
480                    history = prev_galaxy_session.current_history
481            elif self.galaxy_session.current_history:
482                history = self.galaxy_session.current_history
483            if not history and \
484                users_last_session and \
485                users_last_session.current_history and \
486                not users_last_session.current_history.deleted:
487                history = users_last_session.current_history
488            elif not history:
489                history = self.get_history( create=True )
490            if history not in self.galaxy_session.histories:
491                self.galaxy_session.add_history( history )
492            if history.user is None:
493                history.user = user
494            self.galaxy_session.current_history = history
495            if not last_accessed:
496                # Only set default history permissions if current history is not from a previous session
497                self.app.security_agent.history_set_default_permissions( history, dataset=True, bypass_manage_permission=True )
498            self.sa_session.add_all( ( prev_galaxy_session, self.galaxy_session, history ) )
499        else:
500            cookie_name = 'galaxycommunitysession'
501            self.sa_session.add_all( ( prev_galaxy_session, self.galaxy_session ) )
502        self.sa_session.flush()
503        # This method is not called from the Galaxy reports, so the cookie will always be galaxysession
504        self.__update_session_cookie( name=cookie_name )
505    def handle_user_logout( self ):
506        """
507        Logout the current user:
508           - invalidate the current session
509           - create a new session with no user associated
510        """
511        prev_galaxy_session = self.galaxy_session
512        prev_galaxy_session.is_valid = False
513        self.galaxy_session = self.__create_new_session( prev_galaxy_session )
514        self.sa_session.add_all( ( prev_galaxy_session, self.galaxy_session ) )
515        self.sa_session.flush()
516        # This method is not called from the Galaxy reports, so the cookie will always be galaxysession
517        self.__update_session_cookie( name='galaxysession' )
518    def get_galaxy_session( self ):
519        """
520        Return the current galaxy session
521        """
522        return self.galaxy_session
523    def get_history( self, create=False ):
524        """
525        Load the current history, creating a new one only if there is not
526        current history and we're told to create"
527        """
528        history = self.galaxy_session.current_history
529        if not history:
530            if util.string_as_bool( create ):
531                history = self.new_history()
532            else:
533                # Perhaps a bot is running a tool without having logged in to get a history
534                log.debug( "Error: this request returned None from get_history(): %s" % self.request.browser_url )
535                return None
536        return history
537    def set_history( self, history ):
538        if history and not history.deleted:
539            self.galaxy_session.current_history = history
540        self.sa_session.add( self.galaxy_session )
541        self.sa_session.flush()
542    history = property( get_history, set_history )
543    def new_history( self, name=None ):
544        """
545        Create a new history and associate it with the current session and
546        its associated user (if set).
547        """
548        # Create new history
549        history = self.app.model.History()
550        if name:
551            history.name = name
552        # Associate with session
553        history.add_galaxy_session( self.galaxy_session )
554        # Make it the session's current history
555        self.galaxy_session.current_history = history
556        # Associate with user
557        if self.galaxy_session.user:
558            history.user = self.galaxy_session.user
559        # Track genome_build with history
560        history.genome_build = util.dbnames.default_value
561        # Set the user's default history permissions
562        self.app.security_agent.history_set_default_permissions( history )
563        # Save
564        self.sa_session.add_all( ( self.galaxy_session, history ) )
565        self.sa_session.flush()
566        return history
567    def get_current_user_roles( self ):
568        user = self.get_user()
569        if user:
570            roles = user.all_roles()
571        else:
572            roles = []
573        return roles
574    def user_is_admin( self ):
575        admin_users = self.app.config.get( "admin_users", "" ).split( "," )
576        return self.user and admin_users and self.user.email in admin_users
577    def get_toolbox(self):
578        """Returns the application toolbox"""
579        return self.app.toolbox
580    @base.lazy_property
581    def template_context( self ):
582        return dict()
583    @property
584    def model( self ):
585        return self.app.model
586    def make_form_data( self, name, **kwargs ):
587        rval = self.template_context[name] = FormData()
588        rval.values.update( kwargs )
589        return rval
590    def set_message( self, message, type=None ):
591        """
592        Convenience method for setting the 'message' and 'message_type'
593        element of the template context.
594        """
595        self.template_context['message'] = message
596        if type:
597            self.template_context['status'] = type
598    def get_message( self ):
599        """
600        Convenience method for getting the 'message' element of the template
601        context.
602        """
603        return self.template_context['message']
604    def show_message( self, message, type='info', refresh_frames=[], cont=None, use_panels=False, active_view="" ):
605        """
606        Convenience method for displaying a simple page with a single message.
607       
608        `type`: one of "error", "warning", "info", or "done"; determines the
609                type of dialog box and icon displayed with the message
610               
611        `refresh_frames`: names of frames in the interface that should be
612                          refreshed when the message is displayed
613        """
614        return self.fill_template( "message.mako", status=type, message=message, refresh_frames=refresh_frames, cont=cont, use_panels=use_panels, active_view=active_view )
615    def show_error_message( self, message, refresh_frames=[], use_panels=False, active_view="" ):
616        """
617        Convenience method for displaying an error message. See `show_message`.
618        """
619        return self.show_message( message, 'error', refresh_frames, use_panels=use_panels, active_view=active_view )
620    def show_ok_message( self, message, refresh_frames=[], use_panels=False, active_view="" ):
621        """
622        Convenience method for displaying an ok message. See `show_message`.
623        """
624        return self.show_message( message, 'done', refresh_frames, use_panels=use_panels, active_view=active_view )
625    def show_warn_message( self, message, refresh_frames=[], use_panels=False, active_view="" ):
626        """
627        Convenience method for displaying an warn message. See `show_message`.
628        """
629        return self.show_message( message, 'warning', refresh_frames, use_panels=use_panels, active_view=active_view )
630    def show_form( self, form, header=None, template="form.mako", use_panels=False, active_view="" ):
631        """
632        Convenience method for displaying a simple page with a single HTML
633        form.
634        """   
635        return self.fill_template( template, form=form, header=header, use_panels=use_panels, active_view=active_view )
636    def fill_template(self, filename, **kwargs):
637        """
638        Fill in a template, putting any keyword arguments on the context.
639        """
640        # call get_user so we can invalidate sessions from external users,
641        # if external auth has been disabled.
642        self.get_user()
643        if filename.endswith( ".mako" ):
644            return self.fill_template_mako( filename, **kwargs )
645        else:
646            template = Template( file=os.path.join(self.app.config.template_path, filename),
647                                 searchList=[kwargs, self.template_context, dict(caller=self, t=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app)] )
648            return str( template )
649    def fill_template_mako( self, filename, **kwargs ):
650        template = self.webapp.mako_template_lookup.get_template( filename )
651        template.output_encoding = 'utf-8'
652        data = dict( caller=self, t=self, trans=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app )
653        data.update( self.template_context )
654        data.update( kwargs )
655        return template.render( **data )
656    def stream_template_mako( self, filename, **kwargs ):
657        template = self.webapp.mako_template_lookup.get_template( filename )
658        template.output_encoding = 'utf-8'
659        data = dict( caller=self, t=self, trans=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app )
660        data.update( self.template_context )
661        data.update( kwargs )
662        ## return template.render( **data )
663        def render( environ, start_response ):
664            response_write = start_response( self.response.wsgi_status(), self.response.wsgi_headeritems() )
665            class StreamBuffer( object ):
666                def write( self, d ):
667                    response_write( d.encode( 'utf-8' ) )
668            buffer = StreamBuffer()
669            context = mako.runtime.Context( buffer, **data )
670            template.render_context( context )
671            return []
672        return render
673    def fill_template_string(self, template_string, context=None, **kwargs):
674        """
675        Fill in a template, putting any keyword arguments on the context.
676        """
677        template = Template( source=template_string,
678                             searchList=[context or kwargs, dict(caller=self)] )
679        return str(template)
680
681    @property
682    def db_builds( self ):
683        """
684        Returns the builds defined by galaxy and the builds defined by
685        the user (chromInfo in history).
686        """
687        dbnames = list()
688        datasets = self.sa_session.query( self.app.model.HistoryDatasetAssociation ) \
689                                  .filter_by( deleted=False, history_id=self.history.id, extension="len" )
690       
691        for dataset in datasets:
692            dbnames.append( (dataset.dbkey, dataset.name) )
693           
694        user = self.get_user()
695        if user and 'dbkeys' in user.preferences:
696            user_keys = from_json_string( user.preferences['dbkeys'] )
697            for key, chrom_dict in user_keys.iteritems():
698                dbnames.append((key, "%s (%s) [Custom]" % (chrom_dict['name'], key) ))
699                   
700        dbnames.extend( util.dbnames )
701        return dbnames
702       
703    def db_dataset_for( self, dbkey ):
704        """
705        Returns the db_file dataset associated/needed by `dataset`, or `None`.
706        """
707       
708        # If no history, return None.
709        if self.history is None:
710            return None
711       
712        datasets = self.sa_session.query( self.app.model.HistoryDatasetAssociation ) \
713                                  .filter_by( deleted=False, history_id=self.history.id, extension="len" )
714        for ds in datasets:
715            if dbkey == ds.dbkey:
716                return ds
717        return None
718   
719    def request_types(self):
720        if self.sa_session.query( self.app.model.RequestType ).filter_by( deleted=False ).count() > 0:
721            return True
722        return False
723       
724class FormBuilder( object ):
725    """
726    Simple class describing an HTML form
727    """
728    def __init__( self, action="", title="", name="form", submit_text="submit" ):
729        self.title = title
730        self.name = name
731        self.action = action
732        self.submit_text = submit_text
733        self.inputs = []
734    def add_input( self, type, name, label, value=None, error=None, help=None, use_label=True  ):
735        self.inputs.append( FormInput( type, label, name, value, error, help, use_label ) )
736        return self
737    def add_text( self, name, label, value=None, error=None, help=None  ):
738        return self.add_input( 'text', label, name, value, error, help )
739    def add_password( self, name, label, value=None, error=None, help=None  ):
740        return self.add_input( 'password', label, name, value, error, help )
741    def add_select( self, name, label, value=None, options=[], error=None, help=None, use_label=True ):
742        self.inputs.append( SelectInput( name, label, value=value, options=options, error=error, help=help, use_label=use_label   ) )
743        return self
744       
745class FormInput( object ):
746    """
747    Simple class describing a form input element
748    """
749    def __init__( self, type, name, label, value=None, error=None, help=None, use_label=True ):
750        self.type = type
751        self.name = name
752        self.label = label
753        self.value = value
754        self.error = error
755        self.help = help
756        self.use_label = use_label
757
758class GalaxyWebAPITransaction( GalaxyWebTransaction ):
759    def __init__( self, environ, app, webapp ):
760        GalaxyWebTransaction.__init__( self, environ, app, webapp )
761        self.__user = None
762        self._ensure_valid_session( None )
763    def _ensure_valid_session( self, session_cookie ):
764        self.galaxy_session = Bunch()
765        self.galaxy_session.history = self.galaxy_session.current_history = Bunch()
766        self.galaxy_session.history.genome_build = None
767        self.galaxy_session.is_api = True
768    def get_user( self ):
769        """Return the current user (the expose_api decorator ensures that it is set)."""
770        return self.__user
771    def set_user( self, user ):
772        """Compatibility method"""
773        self.__user = user
774    user = property( get_user, set_user )
775    @property
776    def db_builds( self ):
777        dbnames = []
778        if 'dbkeys' in self.user.preferences:
779            user_keys = from_json_string( self.user.preferences['dbkeys'] )
780            for key, chrom_dict in user_keys.iteritems():
781                dbnames.append((key, "%s (%s) [Custom]" % (chrom_dict['name'], key) ))
782        dbnames.extend( util.dbnames )
783        return dbnames
784
785class GalaxyWebUITransaction( GalaxyWebTransaction ):
786    def __init__( self, environ, app, webapp, session_cookie ):
787        GalaxyWebTransaction.__init__( self, environ, app, webapp )
788        # Always have a valid galaxy session
789        self._ensure_valid_session( session_cookie )
790        # Prevent deleted users from accessing Galaxy
791        if self.app.config.use_remote_user and self.galaxy_session.user.deleted:
792            self.response.send_redirect( url_for( '/static/user_disabled.html' ) )
793        if self.app.config.require_login:
794            self._ensure_logged_in_user( environ )
795    def get_user( self ):
796        """Return the current user if logged in or None."""
797        return self.galaxy_session.user
798    def set_user( self, user ):
799        """Set the current user."""
800        self.galaxy_session.user = user
801        self.sa_session.add( self.galaxy_session )
802        self.sa_session.flush()
803    user = property( get_user, set_user )
804
805class SelectInput( FormInput ):
806    """ A select form input. """
807    def __init__( self, name, label, value=None, options=[], error=None, help=None, use_label=True ):
808        FormInput.__init__( self, "select", name, label, value=value, error=error, help=help, use_label=use_label )
809        self.options = options
810   
811class FormData( object ):
812    """
813    Class for passing data about a form to a template, very rudimentary, could
814    be combined with the tool form handling to build something more general.
815    """
816    def __init__( self ):
817        self.values = Bunch()
818        self.errors = Bunch()
819       
820class Bunch( dict ):
821    """
822    Bunch based on a dict
823    """
824    def __getattr__( self, key ):
825        if key not in self: raise AttributeError, key
826        return self[key]
827    def __setattr__( self, key, value ):
828        self[key] = value
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。