1 new commit in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/80291f7ad457/ Changeset: 80291f7ad457 User: dannon Date: 2013-04-03 16:13:37 Summary: Merge GalaxyUI/API transactions -- now just GalaxyWebTransaction since we allow session-based access to the API. Merge UI/API mappers -- url_for invocations will now respond with the correct path regardless of controller origin. **IMPORTANT**: mapper.explicit is no longer set to false -- this means fully specifying the components of the url to be generated as there won't be any 'magic' guessing anymore. Affected #: 7 files diff -r ca242dca4762b4c299f1bffae13c59e2ffd1e03c -r 80291f7ad4576c95f70eaed2f36cc5a95cf303fb lib/galaxy/web/framework/__init__.py --- a/lib/galaxy/web/framework/__init__.py +++ b/lib/galaxy/web/framework/__init__.py @@ -2,11 +2,15 @@ Galaxy web application framework """ +import inspect +import os import pkg_resources +import random +import socket +import string +import time +from Cookie import CookieError -import os, time, socket, random, string -import inspect -from Cookie import CookieError pkg_resources.require( "Cheetah" ) from Cheetah.Template import Template @@ -119,15 +123,8 @@ start_response( error_status, [('Content-type', 'text/plain')] ) return error_message error_status = '403 Forbidden' - ## If there is a user, we've authenticated a session. - if key_required and not trans.user and isinstance( trans.galaxy_session, Bunch ): - # If trans.user is already set, don't check for a key. - # This happens when we're authenticating using session instead of an API key. - # The Bunch clause is used to prevent the case where there's no user, but there is a real session. - # DBTODO: This needs to be fixed when merging transaction types. - if 'key' not in kwargs: - error_message = 'No API key provided with request, please consult the API documentation.' - return error + #If no key supplied, we use the existing session which may be an anonymous user. + if key_required and not trans.user: try: provided_key = trans.sa_session.query( trans.app.model.APIKeys ).filter( trans.app.model.APIKeys.table.c.key == kwargs['key'] ).one() except NoResultFound: @@ -275,10 +272,7 @@ return base.WebApplication.make_body_iterable( self, trans, body ) def transaction_chooser( self, environ, galaxy_app, session_cookie ): - if 'is_api_request' in environ: - return GalaxyWebAPITransaction( environ, galaxy_app, self, session_cookie ) - else: - return GalaxyWebUITransaction( environ, galaxy_app, self, session_cookie ) + return GalaxyWebTransaction( environ, galaxy_app, self, session_cookie ) def add_ui_controllers( self, package_name, app ): """ @@ -335,7 +329,7 @@ Encapsulates web transaction specific state for the Galaxy application (specifically the user's "cookie" session and history) """ - def __init__( self, environ, app, webapp ): + def __init__( self, environ, app, webapp, session_cookie = None): self.app = app self.webapp = webapp self.security = webapp.security @@ -349,6 +343,15 @@ self.workflow_building_mode = False # Flag indicating whether this is an API call and the API key user is an administrator self.api_inherit_admin = False + self.__user = None + # Always have a valid galaxy session + self._ensure_valid_session( session_cookie ) + # Prevent deleted users from accessing Galaxy + if self.app.config.use_remote_user and self.galaxy_session.user.deleted: + self.response.send_redirect( url_for( '/static/user_disabled.html' ) ) + if self.app.config.require_login: + self._ensure_logged_in_user( environ, session_cookie ) + def setup_i18n( self ): locales = [] if 'HTTP_ACCEPT_LANGUAGE' in self.environ: @@ -364,6 +367,7 @@ locales = 'en' t = Translations.load( dirname='locale', locales=locales, domain='ginga' ) self.template_context.update ( dict( _=t.ugettext, n_=t.ugettext, N_=t.ungettext ) ) + @property def sa_session( self ): """ @@ -372,6 +376,24 @@ to allow migration toward a more SQLAlchemy 0.4 style of use. """ return self.app.model.context.current + + + def get_user( self ): + """Return the current user if logged in or None.""" + if self.__user: + return self.__user + else: + return self.galaxy_session.user + + def set_user( self, user ): + """Set the current user.""" + self.galaxy_session.user = user + self.sa_session.add( self.galaxy_session ) + self.sa_session.flush() + self.__user = user + + user = property( get_user, set_user ) + def log_action( self, user=None, action=None, context=None, params=None): """ Application-level logging of user actions. @@ -391,6 +413,7 @@ action.session_id = None self.sa_session.add( action ) self.sa_session.flush() + def log_event( self, message, tool_id=None, **kwargs ): """ Application level logging. Still needs fleshing out (log levels and such) @@ -421,6 +444,7 @@ event.session_id = None self.sa_session.add( event ) self.sa_session.flush() + def get_cookie( self, name='galaxysession' ): """Convenience method for getting a session cookie""" try: @@ -431,6 +455,7 @@ return self.request.cookies[name].value except: return None + def set_cookie( self, value, name='galaxysession', path='/', age=90, version='1' ): """Convenience method for setting a session cookie""" # The galaxysession cookie value must be a high entropy 128 bit random number encrypted @@ -445,6 +470,7 @@ self.response.cookies[name]['httponly'] = True except CookieError, e: log.warning( "Error setting httponly attribute in cookie '%s': %s" % ( name, e ) ) + def _ensure_valid_session( self, session_cookie, create=True): """ Ensure that a valid Galaxy session exists and is available as @@ -531,6 +557,7 @@ # If the old session was invalid, get a new history with our new session if invalidate_existing_session: self.new_history() + def _ensure_logged_in_user( self, environ, session_cookie ): # The value of session_cookie can be one of # 'galaxysession' or 'galaxycommunitysession' @@ -909,16 +936,13 @@ dbnames = list() datasets = self.sa_session.query( self.app.model.HistoryDatasetAssociation ) \ .filter_by( deleted=False, history_id=self.history.id, extension="len" ) - for dataset in datasets: dbnames.append( (dataset.dbkey, dataset.name) ) - user = self.get_user() if user and 'dbkeys' in user.preferences: user_keys = from_json_string( user.preferences['dbkeys'] ) for key, chrom_dict in user_keys.iteritems(): dbnames.append((key, "%s (%s) [Custom]" % (chrom_dict['name'], key) )) - dbnames.extend( util.dbnames ) return dbnames @@ -992,139 +1016,6 @@ self.help = help self.use_label = use_label -class GalaxyWebAPITransaction( GalaxyWebTransaction ): - """ - TODO: Unify this with WebUITransaction, since we allow session auth now. - Enable functionality of 'create' parameter in parent _ensure_valid_session - """ - def __init__( self, environ, app, webapp, session_cookie): - GalaxyWebTransaction.__init__( self, environ, app, webapp ) - self.__user = None - self._ensure_valid_session( session_cookie ) - def _ensure_valid_session( self, session_cookie ): - #Check to see if there is an existing session. Never create a new one. - # Try to load an existing session - secure_id = self.get_cookie( name=session_cookie ) - galaxy_session = None - prev_galaxy_session = None - user_for_new_session = None - invalidate_existing_session = False - # Track whether the session has changed so we can avoid calling flush - # in the most common case (session exists and is valid). - galaxy_session_requires_flush = False - if secure_id: - # Decode the cookie value to get the session_key - session_key = self.security.decode_guid( secure_id ) - try: - # Make sure we have a valid UTF-8 string - session_key = session_key.encode( 'utf8' ) - except UnicodeDecodeError: - # We'll end up creating a new galaxy_session - session_key = None - if session_key: - # Retrieve the galaxy_session id via the unique session_key - galaxy_session = self.sa_session.query( self.app.model.GalaxySession ) \ - .filter( and_( self.app.model.GalaxySession.table.c.session_key==session_key, - self.app.model.GalaxySession.table.c.is_valid==True ) ) \ - .first() - if galaxy_session: - # If remote user is in use it can invalidate the session, so we need to to check some things now. - if self.app.config.use_remote_user: - assert "HTTP_REMOTE_USER" in self.environ, \ - "use_remote_user is set but no HTTP_REMOTE_USER variable" - remote_user_email = self.environ[ 'HTTP_REMOTE_USER' ] - # An existing session, make sure correct association exists - if galaxy_session.user is None: - # No user, associate - 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 - log.warning( "User logged in as '%s' externally, but has a cookie as '%s' invalidating session", - remote_user_email, galaxy_session.user.email ) - galaxy_session = None - else: - if galaxy_session.user and galaxy_session.user.external: - # Remote user support is not enabled, but there is an existing - # session with an external user, invalidate - invalidate_existing_session = True - log.warning( "User '%s' is an external user with an existing session, invalidating session since external auth is disabled", - galaxy_session.user.email ) - galaxy_session = None - elif galaxy_session.user is not None and galaxy_session.user.deleted: - invalidate_existing_session = True - log.warning( "User '%s' is marked deleted, invalidating session" % galaxy_session.user.email ) - galaxy_session = None - # No relevant cookies, or couldn't find, or invalid, so create a new session - if galaxy_session: - self.galaxy_session = galaxy_session - self.user = galaxy_session.user - # Do we need to flush the session? - if galaxy_session_requires_flush: - self.sa_session.add( galaxy_session ) - # FIXME: If prev_session is a proper relation this would not - # be needed. - if prev_galaxy_session: - self.sa_session.add( prev_galaxy_session ) - self.sa_session.flush() - # If the old session was invalid, get a new history with our new session - if not galaxy_session: - #Failed to find a session. Set up fake stuff for API transaction - self.user = None - self.galaxy_session = Bunch() - self.galaxy_session.history = self.galaxy_session.current_history = Bunch() - self.galaxy_session.history.genome_build = None - self.galaxy_session.is_api = True - - def get_user( self ): - """Return the current user (the expose_api decorator ensures that it is set).""" - return self.__user - def set_user( self, user ): - """Compatibility method""" - self.__user = user - user = property( get_user, set_user ) - @property - def db_builds( self ): - dbnames = [] - if 'dbkeys' in self.user.preferences: - user_keys = from_json_string( self.user.preferences['dbkeys'] ) - for key, chrom_dict in user_keys.iteritems(): - dbnames.append((key, "%s (%s) [Custom]" % (chrom_dict['name'], key) )) - dbnames.extend( util.dbnames ) - return dbnames - - @property - def ucsc_builds( self ): - return util.dlnames['ucsc'] - - @property - def ensembl_builds( self ): - return util.dlnames['ensembl'] - - @property - def ncbi_builds( self ): - return util.dlnames['ncbi'] - -class GalaxyWebUITransaction( GalaxyWebTransaction ): - def __init__( self, environ, app, webapp, session_cookie ): - GalaxyWebTransaction.__init__( self, environ, app, webapp ) - # Always have a valid galaxy session - self._ensure_valid_session( session_cookie ) - # Prevent deleted users from accessing Galaxy - if self.app.config.use_remote_user and self.galaxy_session.user.deleted: - self.response.send_redirect( url_for( '/static/user_disabled.html' ) ) - if self.app.config.require_login: - self._ensure_logged_in_user( environ, session_cookie ) - def get_user( self ): - """Return the current user if logged in or None.""" - return self.galaxy_session.user - def set_user( self, user ): - """Set the current user.""" - self.galaxy_session.user = user - self.sa_session.add( self.galaxy_session ) - self.sa_session.flush() - user = property( get_user, set_user ) - class SelectInput( FormInput ): """ A select form input. """ def __init__( self, name, label, value=None, options=[], error=None, help=None, use_label=True ): diff -r ca242dca4762b4c299f1bffae13c59e2ffd1e03c -r 80291f7ad4576c95f70eaed2f36cc5a95cf303fb lib/galaxy/web/framework/base.py --- a/lib/galaxy/web/framework/base.py +++ b/lib/galaxy/web/framework/base.py @@ -2,34 +2,33 @@ A simple WSGI application/framework. """ -import socket -import types +import cgi # For FieldStorage import logging import os.path -import sys +import socket import tarfile -import threading +import types -from Cookie import SimpleCookie +import pkg_resources -import pkg_resources; -pkg_resources.require( "Paste" ) -pkg_resources.require( "Routes" ) -pkg_resources.require( "WebOb" ) +pkg_resources.require("Paste") +pkg_resources.require("Routes") +pkg_resources.require("WebOb") import routes import webob +from Cookie import SimpleCookie + # We will use some very basic HTTP/wsgi utilities from the paste library -from paste.request import parse_headers, get_cookies, parse_formvars +from paste.request import get_cookies from paste import httpexceptions from paste.response import HeaderDict -# For FieldStorage -import cgi log = logging.getLogger( __name__ ) + def __resource_with_deleted( self, member_name, collection_name, **kwargs ): """ Method to monkeypatch on to routes.mapper.Mapper which does the same thing @@ -62,12 +61,11 @@ """ self.controllers = dict() self.api_controllers = dict() - self.mapper = routes.Mapper() + self.mapper = routes.Mapper() # FIXME: The following two options are deprecated and should be # removed. Consult the Routes documentation. self.mapper.minimization = True - self.mapper.explicit = False - self.api_mapper = routes.Mapper() + #self.mapper.explicit = False self.transaction_factory = DefaultWebTransaction # Set if trace logging is enabled self.trace_logger = None @@ -108,7 +106,6 @@ """ # Create/compile the regular expressions for route mapping self.mapper.create_regs( self.controllers.keys() ) - self.api_mapper.create_regs( self.api_controllers.keys() ) def trace( self, **fields ): if self.trace_logger: self.trace_logger.log( "WebApplication", **fields ) @@ -137,20 +134,17 @@ # Map url using routes path_info = environ.get( 'PATH_INFO', '' ) map = self.mapper.match( path_info, environ ) - if map is None: + if path_info.startswith('/api'): environ[ 'is_api_request' ] = True - map = self.api_mapper.match( path_info, environ ) - mapper = self.api_mapper controllers = self.api_controllers else: - mapper = self.mapper controllers = self.controllers if map == None: raise httpexceptions.HTTPNotFound( "No route for " + path_info ) self.trace( path_info=path_info, map=map ) # Setup routes rc = routes.request_config() - rc.mapper = mapper + rc.mapper = self.mapper rc.mapper_dict = map rc.environ = environ # Setup the transaction @@ -164,6 +158,10 @@ raise httpexceptions.HTTPNotFound( "No controller for " + path_info ) # Resolve action method on controller action = map.pop( 'action', 'index' ) + # This is the easiest way to make the controller/action accessible for + # url_for invocations. Specifically, grids. + trans.controller = controller_name + trans.action = action method = getattr( controller, action, None ) if method is None: method = getattr( controller, 'default', None ) @@ -280,7 +278,7 @@ # tempfiles. Necessary for externalizing the upload tool. It's a little hacky # but for performance reasons it's way better to use Paste's tempfile than to # create a new one and copy. -import cgi, tempfile +import tempfile class FieldStorage( cgi.FieldStorage ): def make_file(self, binary=None): return tempfile.NamedTemporaryFile() diff -r ca242dca4762b4c299f1bffae13c59e2ffd1e03c -r 80291f7ad4576c95f70eaed2f36cc5a95cf303fb lib/galaxy/web/framework/helpers/grids.py --- a/lib/galaxy/web/framework/helpers/grids.py +++ b/lib/galaxy/web/framework/helpers/grids.py @@ -1,14 +1,15 @@ -from galaxy.model.orm import * -from galaxy.web.base.controller import * -from galaxy.web.framework.helpers import iff -from galaxy.web import url_for +import logging +import math + +from galaxy.model.item_attrs import RuntimeException, UsesAnnotations, UsesItemRatings from galaxy.util import sanitize_text from galaxy.util.json import from_json_string, to_json_string from galaxy.util.odict import odict -from galaxy.web.framework.helpers import to_unicode -from galaxy.model.item_attrs import * +from galaxy.web.framework import error, url_for +from galaxy.web.framework.helpers import iff -import sys, logging, math +from sqlalchemy.sql.expression import and_, func, or_ + log = logging.getLogger( __name__ ) @@ -193,7 +194,6 @@ page_num = int( kwargs['page'] ) else: page_num = 1 - if page_num == 0: # Show all rows in page. total_num_rows = query.count() @@ -209,11 +209,9 @@ # Defaults. page_num = 1 num_pages = 1 - # There are some places in grid templates where it's useful for a grid # to have its current filter. self.cur_filter_dict = cur_filter_dict - # Preserve grid state: save current filter and sort key. if self.preserve_state: pref_name = unicode( self.__class__.__name__ + self.cur_filter_pref_name ) @@ -228,6 +226,7 @@ params['sort'] = sort_key params['async'] = ( 'async' in kwargs ) trans.log_action( trans.get_user(), unicode( "grid.view" ), context, params ) + # Render grid. def url( *args, **kwargs ): # Only include sort/filter arguments if not linking to another @@ -247,7 +246,13 @@ new_kwargs[ 'id' ] = [ trans.security.encode_id( i ) for i in id ] else: new_kwargs[ 'id' ] = trans.security.encode_id( id ) - return url_for( **new_kwargs ) + #The url_for invocation *must* include a controller and action. + if 'controller' not in new_kwargs: + new_kwargs['controller'] = trans.controller + if 'action' not in new_kwargs: + new_kwargs['action'] = trans.action + return url_for( **new_kwargs) + self.use_panels = ( kwargs.get( 'use_panels', False ) in [ True, 'True', 'true' ] ) async_request = ( ( self.use_async ) and ( kwargs.get( 'async', False ) in [ True, 'True', 'true'] ) ) # Currently, filling the template returns a str object; this requires decoding the string into a diff -r ca242dca4762b4c299f1bffae13c59e2ffd1e03c -r 80291f7ad4576c95f70eaed2f36cc5a95cf303fb lib/galaxy/webapps/galaxy/buildapp.py --- a/lib/galaxy/webapps/galaxy/buildapp.py +++ b/lib/galaxy/webapps/galaxy/buildapp.py @@ -8,14 +8,12 @@ from galaxy.util import asbool -from paste.request import parse_formvars -from paste.util import import_string from paste import httpexceptions import pkg_resources log = logging.getLogger( __name__ ) -from galaxy import config, jobs, util, tools +from galaxy import util import galaxy.model import galaxy.model.mapping import galaxy.datatypes.registry @@ -36,7 +34,7 @@ from galaxy.app import UniverseApplication app = UniverseApplication( global_conf = global_conf, **kwargs ) except: - import traceback, sys + import traceback traceback.print_exc() sys.exit( 1 ) # Call app's shutdown method when the interpeter exits, this cleanly stops @@ -67,91 +65,91 @@ webapp.add_api_controllers( 'galaxy.webapps.galaxy.api', app ) # The /folders section is experimental at this point: log.debug( "app.config.api_folders: %s" % app.config.api_folders ) - webapp.api_mapper.resource( 'folder', 'folders', path_prefix='/api' ) - webapp.api_mapper.resource( 'content', 'contents', + webapp.mapper.resource( 'folder', 'folders', path_prefix='/api' ) + webapp.mapper.resource( 'content', 'contents', controller='folder_contents', name_prefix='folder_', path_prefix='/api/folders/:folder_id', parent_resources=dict( member_name='folder', collection_name='folders' ) ) - webapp.api_mapper.resource( 'content', + webapp.mapper.resource( 'content', 'contents', controller='library_contents', name_prefix='library_', path_prefix='/api/libraries/:library_id', parent_resources=dict( member_name='library', collection_name='libraries' ) ) - webapp.api_mapper.resource( 'content', + webapp.mapper.resource( 'content', 'contents', controller='history_contents', name_prefix='history_', path_prefix='/api/histories/:history_id', parent_resources=dict( member_name='history', collection_name='histories' ) ) - webapp.api_mapper.connect("history_contents_display", + webapp.mapper.connect("history_contents_display", "/api/histories/:history_id/contents/:history_content_id/display", controller="datasets", action="display", conditions=dict(method=["GET"])) - webapp.api_mapper.resource( 'permission', + webapp.mapper.resource( 'permission', 'permissions', path_prefix='/api/libraries/:library_id', parent_resources=dict( member_name='library', collection_name='libraries' ) ) - webapp.api_mapper.resource( 'user', + webapp.mapper.resource( 'user', 'users', controller='group_users', name_prefix='group_', path_prefix='/api/groups/:group_id', parent_resources=dict( member_name='group', collection_name='groups' ) ) - webapp.api_mapper.resource( 'role', + webapp.mapper.resource( 'role', 'roles', controller='group_roles', name_prefix='group_', path_prefix='/api/groups/:group_id', parent_resources=dict( member_name='group', collection_name='groups' ) ) - _add_item_tags_controller( webapp, + _add_item_tags_controller( webapp, name_prefix="history_content_", path_prefix='/api/histories/:history_id/contents/:history_content_id' ) - _add_item_tags_controller( webapp, + _add_item_tags_controller( webapp, name_prefix="history_", path_prefix='/api/histories/:history_id' ) - _add_item_tags_controller( webapp, + _add_item_tags_controller( webapp, name_prefix="workflow_", path_prefix='/api/workflows/:workflow_id' ) - _add_item_annotation_controller( webapp, + _add_item_annotation_controller( webapp, name_prefix="history_content_", path_prefix='/api/histories/:history_id/contents/:history_content_id' ) - _add_item_annotation_controller( webapp, + _add_item_annotation_controller( webapp, name_prefix="history_", path_prefix='/api/histories/:history_id' ) - _add_item_annotation_controller( webapp, + _add_item_annotation_controller( webapp, name_prefix="workflow_", path_prefix='/api/workflows/:workflow_id' ) - webapp.api_mapper.resource( 'dataset', 'datasets', path_prefix='/api' ) - webapp.api_mapper.resource_with_deleted( 'library', 'libraries', path_prefix='/api' ) - webapp.api_mapper.resource( 'sample', 'samples', path_prefix='/api' ) - webapp.api_mapper.resource( 'request', 'requests', path_prefix='/api' ) - webapp.api_mapper.resource( 'form', 'forms', path_prefix='/api' ) - webapp.api_mapper.resource( 'request_type', 'request_types', path_prefix='/api' ) - webapp.api_mapper.resource( 'role', 'roles', path_prefix='/api' ) - webapp.api_mapper.resource( 'group', 'groups', path_prefix='/api' ) - webapp.api_mapper.resource_with_deleted( 'quota', 'quotas', path_prefix='/api' ) - webapp.api_mapper.resource( 'tool', 'tools', path_prefix='/api' ) - webapp.api_mapper.resource_with_deleted( 'user', 'users', path_prefix='/api' ) - webapp.api_mapper.resource( 'genome', 'genomes', path_prefix='/api' ) - webapp.api_mapper.resource( 'visualization', 'visualizations', path_prefix='/api' ) - webapp.api_mapper.resource( 'workflow', 'workflows', path_prefix='/api' ) - webapp.api_mapper.resource_with_deleted( 'history', 'histories', path_prefix='/api' ) - webapp.api_mapper.resource( 'configuration', 'configuration', path_prefix='/api' ) - #webapp.api_mapper.connect( 'run_workflow', '/api/workflow/{workflow_id}/library/{library_id}', controller='workflows', action='run', workflow_id=None, library_id=None, conditions=dict(method=["GET"]) ) + webapp.mapper.resource( 'dataset', 'datasets', path_prefix='/api' ) + webapp.mapper.resource_with_deleted( 'library', 'libraries', path_prefix='/api' ) + webapp.mapper.resource( 'sample', 'samples', path_prefix='/api' ) + webapp.mapper.resource( 'request', 'requests', path_prefix='/api' ) + webapp.mapper.resource( 'form', 'forms', path_prefix='/api' ) + webapp.mapper.resource( 'request_type', 'request_types', path_prefix='/api' ) + webapp.mapper.resource( 'role', 'roles', path_prefix='/api' ) + webapp.mapper.resource( 'group', 'groups', path_prefix='/api' ) + webapp.mapper.resource_with_deleted( 'quota', 'quotas', path_prefix='/api' ) + webapp.mapper.resource( 'tool', 'tools', path_prefix='/api' ) + webapp.mapper.resource_with_deleted( 'user', 'users', path_prefix='/api' ) + webapp.mapper.resource( 'genome', 'genomes', path_prefix='/api' ) + webapp.mapper.resource( 'visualization', 'visualizations', path_prefix='/api' ) + webapp.mapper.resource( 'workflow', 'workflows', path_prefix='/api' ) + webapp.mapper.resource_with_deleted( 'history', 'histories', path_prefix='/api' ) + webapp.mapper.resource( 'configuration', 'configuration', path_prefix='/api' ) + #webapp.mapper.connect( 'run_workflow', '/api/workflow/{workflow_id}/library/{library_id}', controller='workflows', action='run', workflow_id=None, library_id=None, conditions=dict(method=["GET"]) ) # "POST /api/workflows/import" => ``workflows.import_workflow()``. # Defines a named route "import_workflow". - webapp.api_mapper.connect("import_workflow", "/api/workflows/upload", controller="workflows", action="import_new_workflow", conditions=dict(method=["POST"])) - webapp.api_mapper.connect("workflow_dict", '/api/workflows/{workflow_id}/download', controller='workflows', action='workflow_dict', conditions=dict(method=['GET'])) + webapp.mapper.connect("import_workflow", "/api/workflows/upload", controller="workflows", action="import_new_workflow", conditions=dict(method=["POST"])) + webapp.mapper.connect("workflow_dict", '/api/workflows/{workflow_id}/download', controller='workflows', action='workflow_dict', conditions=dict(method=['GET'])) # Preserve the following download route for now for dependent applications -- deprecate at some point - webapp.api_mapper.connect("workflow_dict", '/api/workflows/download/{workflow_id}', controller='workflows', action='workflow_dict', conditions=dict(method=['GET'])) + webapp.mapper.connect("workflow_dict", '/api/workflows/download/{workflow_id}', controller='workflows', action='workflow_dict', conditions=dict(method=['GET'])) # Galaxy API for tool shed features. - webapp.api_mapper.resource( 'tool_shed_repository', + webapp.mapper.resource( 'tool_shed_repository', 'tool_shed_repositories', controller='tool_shed_repositories', name_prefix='tool_shed_repository_', @@ -198,7 +196,7 @@ controller = "%stags" % name_prefix name = "%stag" % name_prefix path = "%s/tags" % path_prefix - map = webapp.api_mapper + map = webapp.mapper # Allow view items' tags. map.connect(name, path, controller=controller, action="index", @@ -225,7 +223,7 @@ def _add_item_annotation_controller( webapp, name_prefix, path_prefix, **kwd ): controller = "%sannotations" % name_prefix name = "%sannotation" % name_prefix - webapp.api_mapper.resource(name, "annotation", path_prefix=path_prefix, controller=controller) + webapp.mapper.resource(name, "annotation", path_prefix=path_prefix, controller=controller) def wrap_in_middleware( app, global_conf, **local_conf ): """ diff -r ca242dca4762b4c299f1bffae13c59e2ffd1e03c -r 80291f7ad4576c95f70eaed2f36cc5a95cf303fb lib/galaxy/webapps/tool_shed/buildapp.py --- a/lib/galaxy/webapps/tool_shed/buildapp.py +++ b/lib/galaxy/webapps/tool_shed/buildapp.py @@ -76,14 +76,14 @@ webapp.add_route( '/repos/*path_info', controller='hg', action='handle_request', path_info='/' ) # Add the web API. # A good resource for RESTful services - http://routes.readthedocs.org/en/latest/restful.html webapp.add_api_controllers( 'galaxy.webapps.tool_shed.api', app ) - webapp.api_mapper.resource( 'repository', + webapp.mapper.resource( 'repository', 'repositories', controller='repositories', collection={ 'get_repository_revision_install_info' : 'GET' }, name_prefix='repository_', path_prefix='/api', parent_resources=dict( member_name='repository', collection_name='repositories' ) ) - webapp.api_mapper.resource( 'repository_revision', + webapp.mapper.resource( 'repository_revision', 'repository_revisions', controller='repository_revisions', name_prefix='repository_revision_', diff -r ca242dca4762b4c299f1bffae13c59e2ffd1e03c -r 80291f7ad4576c95f70eaed2f36cc5a95cf303fb templates/grid_base.mako --- a/templates/grid_base.mako +++ b/templates/grid_base.mako @@ -56,7 +56,7 @@ <script type="text/javascript"> ## TODO: Can this be moved into base.mako? Also, this is history-specific grid code. %if refresh_frames: - %if 'masthead' in refresh_frames: + %if 'masthead' in refresh_frames: ## Refresh masthead == user changes (backward compatibility) if ( parent.user_changed ) { %if trans.user: @@ -68,7 +68,7 @@ %endif %if 'history' in refresh_frames: if ( parent.frames && parent.frames.galaxy_history ) { - parent.frames.galaxy_history.location.href="${h.url_for( controller='root', action='history')}"; + parent.frames.galaxy_history.location.href="${url( controller='root', action='history')}"; if ( parent.force_right_panel ) { parent.force_right_panel( 'show' ); } @@ -76,14 +76,13 @@ else { // TODO: redirecting to root should be done on the server side so that page // does not have to load. - // No history frame, so refresh to root to see history. - window.top.location.href = "${h.url_for( controller='root' )}"; + window.top.location.href = "${url( controller='root' )}"; } %endif %if 'tools' in refresh_frames: if ( parent.frames && parent.frames.galaxy_tools ) { - parent.frames.galaxy_tools.location.href="${h.url_for( controller='root', action='tool_menu')}"; + parent.frames.galaxy_tools.location.href="${url( controller='root', action='tool_menu')}"; if ( parent.force_left_panel ) { parent.force_left_panel( 'show' ); } @@ -92,8 +91,8 @@ %endif // Needed URLs for grid history searching. - var history_tag_autocomplete_url = "${h.url_for( controller='tag', action='tag_autocomplete_data', item_class='History' )}", - history_name_autocomplete_url = "${h.url_for( controller='history', action='name_autocomplete_data' )}"; + var history_tag_autocomplete_url = "${url( controller='tag', action='tag_autocomplete_data', item_class='History' )}", + history_name_autocomplete_url = "${url( controller='history', action='name_autocomplete_data' )}"; // // Create grid object. @@ -117,9 +116,10 @@ /** Returns true if string denotes true. */ var is_true = function(s) { return _.indexOf(['True', 'true', 't'], s) !== -1; }; + // Create grid. var grid = new Grid({ - url_base: '${h.url_for()}', + url_base: '${trans.request.path_url}', async: is_true('${grid.use_async}'), async_ops: async_ops, categorical_filters: categorical_filters, @@ -135,7 +135,6 @@ $(document).ready(function() { init_grid_elements(); init_grid_controls(); - // Initialize text filters to select text on click and use normal font when user is typing. $('input[type=text]').each(function() { $(this).click(function() { $(this).select(); } ) @@ -197,13 +196,13 @@ <ul class="manage-table-actions"> %if len( grid.global_actions ) < 3: %for action in grid.global_actions: - <li><a class="action-button" href="${h.url_for( **action.url_args )}">${action.label}</a></li> + <li><a class="action-button" href="${url( **action.url_args )}">${action.label}</a></li> %endfor %else: <li><a class="action-button" id="action-8675309-popup" class="menubutton">Actions</a></li><div popupmenu="action-8675309-popup"> %for action in grid.global_actions: - <a class="action-button" href="${h.url_for( **action.url_args )}">${action.label}</a> + <a class="action-button" href="${url( **action.url_args )}">${action.label}</a> %endfor </div> %endif diff -r ca242dca4762b4c299f1bffae13c59e2ffd1e03c -r 80291f7ad4576c95f70eaed2f36cc5a95cf303fb templates/grid_common.mako --- a/templates/grid_common.mako +++ b/templates/grid_common.mako @@ -1,5 +1,5 @@ -<%! - from galaxy.web.framework.helpers.grids import TextColumn, StateColumn, GridColumnFilter +<%! + from galaxy.web.framework.helpers.grids import TextColumn, StateColumn, GridColumnFilter from galaxy.web.framework.helpers import iff %> @@ -16,7 +16,7 @@ %endif <td style="padding: 0;"> %if isinstance(column, TextColumn): - <form class="text-filter-form" column_key="${column.key}" action="${url( dict() )}" method="get" > + <form class="text-filter-form" column_key="${column.key}" action="${url(dict())}" method="get" > ## Carry forward filtering criteria with hidden inputs. %for temp_column in grid.columns: %if temp_column.key in cur_filter_dict: @@ -30,7 +30,6 @@ %endif %endif %endfor - ## Print current filtering criteria and links to delete. <span id="${column.key}-filtering-criteria"> %if column.key in cur_filter_dict: @@ -40,7 +39,7 @@ <span class='text-filter-val'> ${cur_filter_dict[column.key]} <% filter_all = GridColumnFilter( "", { column.key : "All" } ) %> - <a href="${url( filter_all.get_url_args() )}"><span class="delete-search-icon" /></a> + <a href="${url(filter_all.get_url_args())}"><span class="delete-search-icon" /></a></span> %endif %elif isinstance( column_filter, list ): @@ -54,7 +53,7 @@ del new_filter[ i ] new_column_filter = GridColumnFilter( "", { column.key : h.to_json_string( new_filter ) } ) %> - <a href="${url( new_column_filter.get_url_args() )}"><span class="delete-search-icon" /></a> + <a href="${url(new_column_filter.get_url_args())}"><span class="delete-search-icon" /></a></span> %endfor %endif @@ -91,7 +90,7 @@ <span class="categorical-filter ${column.key}-filter current-filter">${filter.label}</span> %else: <span class="categorical-filter ${column.key}-filter"> - <a href="${url( filter.get_url_args() )}" filter_key="${filter_key}" filter_val="${filter_arg}">${filter.label}</a> + <a href="${url(filter.get_url_args())}" filter_key="${filter_key}" filter_val="${filter_arg}">${filter.label}</a></span> %endif %endfor @@ -119,7 +118,7 @@ ## Clear the standard search. ##| ##<% filter_all = GridColumnFilter( "", { column.key : "All" } ) %> - ##<a href="${url( filter_all.get_url_args() )}">Clear All</a> + ##<a href="${url(filter_all.get_url_args())}">Clear All</a> ## Only show advanced search if there are filterable columns. <% @@ -133,7 +132,7 @@ %> %if show_advanced_search_link: <% args = { "advanced-search" : True } %> - <a href="${url( args )}" class="advanced-search-toggle">Advanced Search</a> + <a href="${url(args)}" class="advanced-search-toggle">Advanced Search</a> %endif </td></tr></table> @@ -157,13 +156,13 @@ <table><tr><td style="text-align: left" colspan="100"><% args = { "advanced-search" : False } %> - <a href="${url( args )}" class="advanced-search-toggle">Close Advanced Search</a> + <a href="${url(args)}" class="advanced-search-toggle">Close Advanced Search</a> ## Link to clear all filters. ##| ##<% ## no_filter = GridColumnFilter("Clear All", default_filter_dict) ##%> - ##<a href="${url( no_filter.get_url_args() )}">${no_filter.label}</a> + ##<a href="${url(no_filter.get_url_args())}">${no_filter.label}</a></td></tr> %for column in grid.columns: %if column.filterable == "advanced": @@ -180,4 +179,4 @@ %endfor </table></div> -</%def> \ No newline at end of file +</%def> Repository URL: https://bitbucket.org/galaxy/galaxy-central/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.