details: http://www.bx.psu.edu/hg/galaxy/rev/cd0596754ff1 changeset: 2940:cd0596754ff1 user: rc date: Mon Nov 02 13:30:17 2009 -0500 description: User information forms first pass. - added a foreign key in the galaxy_user table to the form_values table - separate user info page for viewing/editing user info - user addresses added to user info page diffstat: lib/galaxy/model/__init__.py | 18 +- lib/galaxy/model/mapping.py | 3 + lib/galaxy/model/migrate/versions/0025_user_info.py | 62 ++ lib/galaxy/web/controllers/admin.py | 42 +- lib/galaxy/web/controllers/library_common.py | 3 +- lib/galaxy/web/controllers/user.py | 604 ++++++++++++++++++++----- lib/galaxy/web/form_builder.py | 19 + lib/galaxy/web/framework/helpers/grids.py | 3 +- templates/admin/user/grid.mako | 2 +- templates/user/edit_address.mako | 33 + templates/user/index.mako | 5 +- templates/user/info.mako | 161 ++++++ templates/user/permissions.mako | 8 +- templates/user/register.mako | 87 +++ test/base/twilltestcase.py | 105 +++- test/functional/test_forms_and_requests.py | 39 +- test/functional/test_user_info.py | 149 ++++++ 17 files changed, 1134 insertions(+), 209 deletions(-) diffs (1702 lines): diff -r 6e8263ea83fa -r cd0596754ff1 lib/galaxy/model/__init__.py --- a/lib/galaxy/model/__init__.py Mon Nov 02 09:54:18 2009 -0500 +++ b/lib/galaxy/model/__init__.py Mon Nov 02 13:30:17 2009 -0500 @@ -1098,7 +1098,8 @@ class FormDefinition( object ): types = Bunch( REQUEST = 'Sequencing Request Form', SAMPLE = 'Sequencing Sample Form', - LIBRARY_INFO_TEMPLATE = 'Library information template' ) + LIBRARY_INFO_TEMPLATE = 'Library information template', + USER_INFO = 'User Information' ) def __init__(self, name=None, desc=None, fields=[], form_definition_current=None, form_type=None, layout=None): self.name = name @@ -1203,21 +1204,6 @@ self.folder = folder self.state = state self.samples_list = [] - def add_sample(self, sample_name=None, sample_desc=None, sample_values=None): - # create a form_values row - values = trans.app.model.FormValues(self.type.sample_form, sample_values) - values.flush() - sample = Sample(sample_name, sample_desc, self, values) - sample.flush() - # set the initial state - state = self.type.states[0] - event = SampleEvent(sample, state) - event.flush() - # add this sample to the member array - self.samples_list.append(sample) - return sample - def delete_sample(self, sample_name): - pass def has_sample(self, sample_name): for s in self.samples: if s.name == sample_name: diff -r 6e8263ea83fa -r cd0596754ff1 lib/galaxy/model/mapping.py --- a/lib/galaxy/model/mapping.py Mon Nov 02 09:54:18 2009 -0500 +++ b/lib/galaxy/model/mapping.py Mon Nov 02 13:30:17 2009 -0500 @@ -47,6 +47,7 @@ Column( "username", TrimmedString( 255 ), index=True, unique=True ), Column( "password", TrimmedString( 40 ), nullable=False ), Column( "external", Boolean, default=False ), + Column( "form_values_id", Integer, ForeignKey( "form_values.id" ), index=True ), Column( "deleted", Boolean, index=True, default=False ), Column( "purged", Boolean, index=True, default=False ) ) @@ -781,6 +782,8 @@ _preferences=relation( UserPreference, backref="user", collection_class=attribute_mapped_collection('name')), # addresses=relation( UserAddress, # primaryjoin=( User.table.c.id == UserAddress.table.c.user_id ) ) + values=relation( FormValues, + primaryjoin=( User.table.c.form_values_id == FormValues.table.c.id ) ), ) ) # Set up proxy so that this syntax is possible: diff -r 6e8263ea83fa -r cd0596754ff1 lib/galaxy/model/migrate/versions/0025_user_info.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/galaxy/model/migrate/versions/0025_user_info.py Mon Nov 02 13:30:17 2009 -0500 @@ -0,0 +1,62 @@ +""" +This script adds a foreign key to the form_values table in the galaxy_user table +""" +from sqlalchemy import * +from sqlalchemy.orm import * +from sqlalchemy.exceptions import * +from migrate import * +from migrate.changeset import * +import datetime +now = datetime.datetime.utcnow +import sys, logging +# Need our custom types, but don't import anything else from model +from galaxy.model.custom_types import * + +log = logging.getLogger( __name__ ) +log.setLevel(logging.DEBUG) +handler = logging.StreamHandler( sys.stdout ) +format = "%(name)s %(levelname)s %(asctime)s %(message)s" +formatter = logging.Formatter( format ) +handler.setFormatter( formatter ) +log.addHandler( handler ) + +metadata = MetaData( migrate_engine ) +db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, transactional=False ) ) + +def display_migration_details(): + print "========================================" + print "This script adds a foreign key to the form_values table in the galaxy_user table" + print "========================================" +def upgrade(): + display_migration_details() + # Load existing tables + metadata.reflect() + try: + User_table = Table( "galaxy_user", metadata, autoload=True ) + except NoSuchTableError: + User_table = None + log.debug( "Failed loading table galaxy_user" ) + if User_table: + try: + col = Column( "form_values_id", Integer, index=True ) + col.create( User_table ) + assert col is User_table.c.form_values_id + except Exception, e: + log.debug( "Adding column 'form_values_id' to galaxy_user table failed: %s" % ( str( e ) ) ) + try: + FormValues_table = Table( "form_values", metadata, autoload=True ) + except NoSuchTableError: + FormValues_table = None + log.debug( "Failed loading table form_values" ) + # Add 1 foreign key constraint to the form_values table + if User_table and FormValues_table: + try: + cons = ForeignKeyConstraint( [User_table.c.form_values_id], + [FormValues_table.c.id], + name='user_form_values_id_fk' ) + # Create the constraint + cons.create() + except Exception, e: + log.debug( "Adding foreign key constraint 'user_form_values_id_fk' to table 'galaxy_user' failed: %s" % ( str( e ) ) ) +def downgrade(): + pass \ No newline at end of file diff -r 6e8263ea83fa -r cd0596754ff1 lib/galaxy/web/controllers/admin.py --- a/lib/galaxy/web/controllers/admin.py Mon Nov 02 09:54:18 2009 -0500 +++ b/lib/galaxy/web/controllers/admin.py Mon Nov 02 13:30:17 2009 -0500 @@ -41,7 +41,7 @@ model_class = model.User template='/admin/user/grid.mako' columns = [ - EmailColumn( "Email", link=( lambda item: dict( operation="user", id=item.id ) ), attach_popup=True ), + EmailColumn( "Email", link=( lambda item: dict( operation="information", id=item.id ) ), attach_popup=True ), UserNameColumn( "User Name", attach_popup=False ), GroupsColumn( "Groups", attach_popup=False ), RolesColumn( "Roles", attach_popup=False ), @@ -51,13 +51,15 @@ grids.GridColumn( "Deleted", key="deleted", visible=False ) ] operations = [ - grids.GridOperation( "reset_password", condition=( lambda item: not item.deleted ), allow_multiple=True ) + grids.GridOperation( "Manage Roles & Groups", condition=( lambda item: not item.deleted ), allow_multiple=False ) + ] #TODO: enhance to account for trans.app.config.allow_user_deletion here so that we can eliminate these operations if # the setting is False - operations.append( grids.GridOperation( "delete", condition=( lambda item: not item.deleted ), allow_multiple=True ) ) - operations.append( grids.GridOperation( "undelete", condition=( lambda item: item.deleted ), allow_multiple=True ) ) - operations.append( grids.GridOperation( "purge", condition=( lambda item: item.deleted ), allow_multiple=True ) ) + operations.append( grids.GridOperation( "Reset Password", condition=( lambda item: not item.deleted ), allow_multiple=True, allow_popup=False ) ) + operations.append( grids.GridOperation( "Delete", condition=( lambda item: not item.deleted ), allow_multiple=True ) ) + operations.append( grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ), allow_multiple=True ) ) + operations.append( grids.GridOperation( "Purge", condition=( lambda item: item.deleted ), allow_multiple=True ) ) standard_filters = [ grids.GridColumnFilter( "Active", args=dict( deleted=False ) ), grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ), @@ -530,6 +532,9 @@ @web.expose @web.require_admin def create_new_user( self, trans, **kwargs ): + return trans.response.send_redirect( web.url_for( controller='user', + action='create', + admin_view='True' ) ) email = '' password = '' confirm = '' @@ -731,9 +736,9 @@ def users( self, trans, **kwargs ): if 'operation' in kwargs: operation = kwargs['operation'].lower() - if operation == "user": + if operation == "roles": return self.user( trans, **kwargs ) - if operation == "reset_password": + if operation == "reset password": return self.reset_user_password( trans, **kwargs ) if operation == "delete": return self.mark_user_deleted( trans, **kwargs ) @@ -743,10 +748,33 @@ return self.purge_user( trans, **kwargs ) if operation == "create": return self.create_new_user( trans, **kwargs ) + if operation == "information": + return self.user_info( trans, **kwargs ) # Render the list view return self.user_list_grid( trans, **kwargs ) @web.expose @web.require_admin + def user_info( self, trans, **kwd ): + ''' + This method displays the user information page which consists of login + information, public username, reset password & other user information + obtained during registration + ''' + print 'KWD', kwd + user_id = kwd.get( 'id', None ) + if not user_id: + message += "Invalid user id (%s) received" % str( user_id ) + trans.response.send_redirect( web.url_for( controller='admin', + action='users', + message=util.sanitize_text( message ), + status='error' ) ) + user = get_user( trans, user_id ) + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + user_id=user.id, + admin_view=True ) ) + @web.expose + @web.require_admin def user( self, trans, **kwd ): user_id = kwd.get( 'id', None ) message = '' diff -r 6e8263ea83fa -r cd0596754ff1 lib/galaxy/web/controllers/library_common.py --- a/lib/galaxy/web/controllers/library_common.py Mon Nov 02 09:54:18 2009 -0500 +++ b/lib/galaxy/web/controllers/library_common.py Mon Nov 02 13:30:17 2009 -0500 @@ -242,7 +242,8 @@ trans.response.send_redirect( web.url_for( controller='forms', action='new', msg=msg, - messagetype='done' ) ) + messagetype='done', + form_type=trans.app.model.FormDefinition.types.LIBRARY_INFO_TEMPLATE ) ) if params.get( 'add_info_template_button', False ): form = trans.sa_session.query( trans.app.model.FormDefinition ).get( int( kwd[ 'form_id' ] ) ) #fields = list( copy.deepcopy( form.fields ) ) diff -r 6e8263ea83fa -r cd0596754ff1 lib/galaxy/web/controllers/user.py --- a/lib/galaxy/web/controllers/user.py Mon Nov 02 09:54:18 2009 -0500 +++ b/lib/galaxy/web/controllers/user.py Mon Nov 02 13:30:17 2009 -0500 @@ -6,6 +6,9 @@ from galaxy import util import logging, os, string, re from random import choice +from galaxy.web.controllers.forms import get_all_forms +from galaxy.web.form_builder import * +from galaxy.web.controllers import admin log = logging.getLogger( __name__ ) @@ -23,10 +26,11 @@ VALID_USERNAME_RE = re.compile( "^[a-z0-9\-]+$" ) class User( BaseController ): - edit_address_id = None + @web.expose def index( self, trans, **kwd ): return trans.fill_template( '/user/index.mako', user=trans.get_user() ) + @web.expose def change_password(self, trans, old_pass='', new_pass='', conf_pass='', **kwd): old_pass_err = new_pass_err = conf_pass_err = '' @@ -158,50 +162,392 @@ 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 ): + def create( self, trans, **kwd ): + params = util.Params( kwd ) + email = util.restore_text( params.get('email', '') ) + username = util.restore_text( params.get('username', '') ) + password = util.restore_text( params.get('password', '') ) + confirm = util.restore_text( params.get('confirm', '') ) + subscribe = params.get('subscribe', False) + admin_view = params.get('admin_view', 'False') + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) 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: - email_error = "Email address exceeds maximum allowable length" - elif trans.sa_session.query( trans.app.model.User ).filter_by( email=email ).all(): - email_error = "User with that email already exists" - elif len( password ) < 6: - password_error = "Please use a password of at least 6 characters" - elif password != confirm: - confirm_error = "Passwords do not match" - else: - 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, default_access_private = trans.app.config.new_user_dataset_access_role_default_private ) + # + # Create the user, save all the user info and login to Galaxy + # + if params.get('create_user_button', None) == "Submit": + # check email and password validity + error = self.__validate(trans, params, email, password, confirm) + if error: + kwd[ 'msg' ] = error + kwd[ 'messagetype' ] = 'error' + kwd[ 'create_user_button' ] = None + return trans.response.send_redirect( web.url_for( controller='user', + action='create', + **kwd ) ) + # all the values are valid + user = trans.app.model.User( email=email ) + user.set_password_cleartext( password ) + user.username = username + 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, default_access_private = trans.app.config.new_user_dataset_access_role_default_private ) + # save user info + self.__save_user_info(trans, user, action='create', new_user=True, **kwd) + if subscribe: + mail = os.popen("%s -t" % trans.app.config.sendmail_path, 'w') + 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=refresh_frames ) + if admin_view == 'False': # 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" ) - #subscribe user to email list - if subscribe: - mail = os.popen("%s -t" % trans.app.config.sendmail_path, 'w') - 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=refresh_frames ) + # subscribe user to email list return trans.show_ok_message( "Now logged in as " + user.email, refresh_frames=refresh_frames ) - return trans.show_form( - web.FormBuilder( web.url_for(), "Create account", submit_text="Create" ) - .add_text( "email", "Email address", value=email, error=email_error ) - .add_password( "password", "Password", value='', error=password_error ) - .add_password( "confirm", "Confirm password", value='', error=confirm_error ) - .add_input( "checkbox","Subscribe To Mailing List","subscribe", value='subscribe' ) ) + else: + trans.response.send_redirect( web.url_for( controller='admin', + action='users', + message='Created new user account (%s)' % user.email, + status='done' ) ) + else: + # + # Show the user registration form + # + user_info_select, user_info_form, login_info, widgets = self.__user_info_ui(trans, **kwd) + return trans.fill_template( '/user/register.mako', + user_info_select=user_info_select, + user_info_form=user_info_form, widgets=widgets, + login_info=login_info, admin_view=admin_view, + msg=msg, messagetype=messagetype) + + def __save_user_info(self, trans, user, action, new_user=True, **kwd): + ''' + This method saves the user information for new users as well as editing user + info for existing users. For new users, the user info form is retrieved from + the one that user has selected. And for existing users, the user info form is + retrieved from the db. + ''' + params = util.Params( kwd ) + # get all the user information forms + user_info_forms = get_all_forms( trans, filter=dict(deleted=False), + form_type=trans.app.model.FormDefinition.types.USER_INFO ) + # if there are no user forms available then there is nothing to save + if not len( user_info_forms ): + return + if new_user: + user_info_type = params.get( 'user_info_select', 'none' ) + try: + user_info_form = trans.sa_session.query( trans.app.model.FormDefinition ).get(int(user_info_type)) + except: + return trans.response.send_redirect( web.url_for( controller='user', + action=action, + msg='Invalid user information form id', + messagetype='error') ) + else: + user_info_form = user.values.form_definition + values = [] + for index, field in enumerate(user_info_form.fields): + if field['type'] == 'AddressField': + value = util.restore_text(params.get('field_%i' % index, '')) + if value == 'new': + # save this new address in the list of this user's addresses + user_address = trans.app.model.UserAddress( user=user ) + user_address.desc = util.restore_text(params.get('field_%i_short_desc' % index, '')) + user_address.name = util.restore_text(params.get('field_%i_name' % index, '')) + user_address.institution = util.restore_text(params.get('field_%i_institution' % index, '')) + user_address.address = util.restore_text(params.get('field_%i_address1' % index, ''))+' '+util.restore_text(params.get('field_%i_address2' % index, '')) + user_address.city = util.restore_text(params.get('field_%i_city' % index, '')) + user_address.state = util.restore_text(params.get('field_%i_state' % index, '')) + user_address.postal_code = util.restore_text(params.get('field_%i_postal_code' % index, '')) + user_address.country = util.restore_text(params.get('field_%i_country' % index, '')) + user_address.phone = util.restore_text(params.get('field_%i_phone' % index, '')) + user_address.flush() + trans.user.refresh() + values.append(int(user_address.id)) + elif value == unicode('none'): + values.append('') + else: + values.append(int(value)) + else: + values.append(util.restore_text(params.get('field_%i' % index, ''))) + if new_user: + form_values = trans.app.model.FormValues(user_info_form, values) + form_values.flush() + user.values = form_values + else: # editing the user info of an existing user + user.values.content = values + user.values.flush() + user.flush() + def __validate_email(self, trans, params, email, user=None): + error = None + if user: + if user.email == email: + return None + if len(email) == 0 or "@" not in email or "." not in email: + error = "Please enter a real email address" + elif len(email) > 255: + error = "Email address exceeds maximum allowable length" + elif trans.sa_session.query( trans.app.model.User ).filter_by(email=email).all(): + error = "User with that email already exists" + return error + def __validate_password(self, trans, params, password, confirm): + error = None + if len(password) < 6: + error = "Please use a password of at least 6 characters" + elif password != confirm: + error = "Passwords do not match" + return error + + def __validate(self, trans, params, email, password, confirm): + error = self.__validate_email(trans, params, email) + if error: + return error + error = self.__validate_password(trans, params, password, confirm) + if error: + return error + if len(get_all_forms( trans, + filter=dict(deleted=False), + form_type=trans.app.model.FormDefinition.types.USER_INFO )): + if params.get('user_info_select', 'none') == 'none': + return 'Select the user type and the user information' + return None + + def __user_info_ui(self, trans, user=None, **kwd): + ''' + This method creates the user type select box & user information form widgets + and is called during user registration and editing user information. + If there exists only one user information form then show it after main + login info. However, if there are more than one user info forms then + show a selectbox containing all the forms, then the user can select + the one that fits the user's description the most + ''' + params = util.Params( kwd ) + # get all the user information forms + user_info_forms = get_all_forms( trans, filter=dict(deleted=False), + form_type=trans.app.model.FormDefinition.types.USER_INFO ) + user_info_select = None + if user: + if user.values: + selected_user_form_id = user.values.form_definition.id + else: + selected_user_form_id = 'none' + else: + selected_user_form_id = params.get( 'user_info_select', 'none' ) + # when there are more than one user information forms then show a select box + # list all these forms + if len(user_info_forms) > 1: + # create the select box + user_info_select = SelectField('user_info_select', refresh_on_change=True, + refresh_on_change_values=[str(u.id) for u in user_info_forms]) + if selected_user_form_id == 'none': + user_info_select.add_option('Select one', 'none', selected=True) + else: + user_info_select.add_option('Select one', 'none') + for u in user_info_forms: + if selected_user_form_id == str(u.id): + user_info_select.add_option(u.name, u.id, selected=True) + else: + user_info_select.add_option(u.name, u.id) + # when there is just one user information form the just render that form + elif len(user_info_forms) == 1: + selected_user_form_id = user_info_forms[0].id + # now, create the selected user form widgets starting with the basic + # login information + if user: + login_info = { 'Email': TextField( 'email', 40, user.email ), + 'Public Username': TextField( 'username', 40, user.username ), + 'Current Password': PasswordField( 'current', 40, '' ), + 'New Password': PasswordField( 'password', 40, '' ), + 'Confirm': PasswordField( 'confirm', 40, '' ) } + else: + login_info = { 'Email': TextField( 'email', 40, + util.restore_text( params.get('email', '') ) ), + 'Public Username': TextField( 'username', 40, + util.restore_text( params.get('username', '') ) ), + 'Password': PasswordField( 'password', 40, + util.restore_text( params.get('password', '') ) ), + 'Confirm': PasswordField( 'confirm', 40, + util.restore_text( params.get('confirm', '') ) ), + 'Subscribe To Mailing List': CheckboxField( 'subscribe', + util.restore_text( params.get('subscribe', '') ) ) } + # user information + try: + user_info_form = trans.sa_session.query( trans.app.model.FormDefinition ).get(int(selected_user_form_id)) + except: + return user_info_select, None, login_info, None + if user: + if user.values: + widgets = user_info_form.get_widgets(user=user, + contents=user.values.content, + **kwd) + else: + widgets = user_info_form.get_widgets(None, contents=[], **kwd) + else: + widgets = user_info_form.get_widgets(None, contents=[], **kwd) + return user_info_select, user_info_form, login_info, widgets + + @web.expose + def show_info( self, trans, **kwd ): + ''' + This method displays the user information page which consists of login + information, public username, reset password & other user information + obtained during registration + ''' + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + # check if this method is called from the admin perspective, + if params.get('admin_view', 'False') == 'True': + try: + user = trans.sa_session.query( trans.app.model.User ).get( int( params.get( 'user_id', None ) ) ) + except: + return trans.response.send_redirect( web.url_for( controller='admin', + action='users', + message='Invalid user', + status='error' ) ) + admin_view = True + else: + user = trans.user + admin_view = False + user_info_select, user_info_form, login_info, widgets = self.__user_info_ui(trans, user, **kwd) + # user's addresses + show_filter = util.restore_text( params.get( 'show_filter', 'Active' ) ) + if show_filter == 'All': + addresses = [address for address in user.addresses] + elif show_filter == 'Deleted': + addresses = [address for address in user.addresses if address.deleted] + else: + addresses = [address for address in user.addresses if not address.deleted] + return trans.fill_template( '/user/info.mako', user=user, admin_view=admin_view, + user_info_select=user_info_select, + user_info_form=user_info_form, widgets=widgets, + login_info=login_info, + addresses=addresses, show_filter=show_filter, + msg=msg, messagetype=messagetype) + @web.expose + def edit_info( self, trans, **kwd ): + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + if params.get('admin_view', 'False') == 'True': + try: + user = trans.sa_session.query( trans.app.model.User ).get( int( params.get( 'user_id', None ) ) ) + except: + return trans.response.send_redirect( web.url_for( controller='admin', + action='users', + message='Invalid user', + status='error' ) ) + else: + user = trans.user + # + # Editing login info (email & username) + # + if params.get('login_info_button', None) == 'Save': + email = util.restore_text( params.get('email', '') ) + username = util.restore_text( params.get('username', '') ) + # validate the new values + error = self.__validate_email(trans, params, email, user) + if error: + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + msg=error, + messagetype='error') ) + # the new email & username + user.email = email + user.username = username + user.flush() + msg = 'The login information has been updated with the changes' + if params.get('admin_view', 'False') == 'True': + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + user_id=user.id, + admin_view=True, + msg=msg, + messagetype='done' ) ) + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + msg=msg, + messagetype='done') ) + # + # Change password + # + elif params.get('change_password_button', None) == 'Save': + password = util.restore_text( params.get('password', '') ) + confirm = util.restore_text( params.get('confirm', '') ) + # when from the user perspective, validate the current password + if params.get('admin_view', 'False') == 'False': + current = util.restore_text( params.get('current', '') ) + if not trans.user.check_password( current ): + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + msg='Invalid current password', + messagetype='error') ) + # validate the new values + error = self.__validate_password(trans, params, password, confirm) + if error: + if params.get('admin_view', 'False') == 'True': + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + user_id=user.id, + admin_view=True, + msg=error, + messagetype='error' ) ) + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + msg=error, + messagetype='error') ) + # save new password + user.set_password_cleartext( password ) + user.flush() + trans.log_event( "User change password" ) + msg = 'The password has been changed.' + if params.get('admin_view', 'False') == 'True': + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + user_id=user.id, + admin_view=True, + msg=msg, + messagetype='done' ) ) + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + msg=msg, + messagetype='done') ) + # + # Edit user information + # + elif params.get('edit_user_info_button', None) == 'Save': + self.__save_user_info(trans, user, "show_info", new_user=False, **kwd) + msg = "The user information has been updated with the changes." + if params.get('admin_view', 'False') == 'True': + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + user_id=user.id, + admin_view=True, + msg=msg, + messagetype='done' ) ) + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + msg=msg, + messagetype='done') ) + else: + if params.get('admin_view', 'False') == 'True': + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + user_id=user.id, + admin_view=True ) ) + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info' ) ) + @web.expose def reset_password( self, trans, email=None, **kwd ): error = '' @@ -305,7 +651,7 @@ country=country, phone=phone) user_address.flush() return trans.response.send_redirect( web.url_for( controller='user', - action='manage_addresses', + action='show_info', msg='Address <b>%s</b> has been added' % user_address.desc, messagetype='done') ) @@ -322,109 +668,123 @@ .add_text( "country", "Country", value=country, error=country_error ) .add_text( "phone", "Phone", value=phone, error=phone_error ) ) @web.expose - def edit_address( self, trans, address_id=None, short_desc='', name='', institution='', address1='', - address2='', city='', state='', postal_code='', country='', phone='' ): - import sys - - if trans.app.config.require_login: - refresh_frames = [ 'masthead', 'history', 'tools' ] + def edit_address( self, trans, **kwd ): + params = util.Params( kwd ) + msg = util.restore_text( params.get( 'msg', '' ) ) + messagetype = params.get( 'messagetype', 'done' ) + admin_view = params.get( 'admin_view', 'False' ) + error = '' + user = trans.sa_session.query( trans.app.model.User ).get( int( params.get( 'user_id', None ) ) ) + try: + user_address = trans.sa_session.query( trans.app.model.UserAddress ).get(int(params.get( 'address_id', None ))) + except: + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + user_id=user.id, + admin_view=admin_view, + msg='Invalid address ID', + messagetype='error' ) ) + if params.get( 'edit_address_button', None ) == 'Save changes': + if not len( util.restore_text( params.get( 'short_desc', '' ) ) ): + error = 'Enter a short description for this address' + elif not len( util.restore_text( params.get( 'name', '' ) ) ): + error = 'Enter the full name' + elif not len( util.restore_text( params.get( 'institution', '' ) ) ): + error = 'Enter the institution associated with the user' + elif not len ( util.restore_text( params.get( 'address1', '' ) ) ): + error = 'Enter the address' + elif not len( util.restore_text( params.get( 'city', '' ) ) ): + error = 'Enter the city' + elif not len( util.restore_text( params.get( 'state', '' ) ) ): + error = 'Enter the state/province/region' + elif not len( util.restore_text( params.get( 'postal_code', '' ) ) ): + error = 'Enter the postal code' + elif not len( util.restore_text( params.get( 'country', '' ) ) ): + error = 'Enter the country' + else: + user_address.desc = util.restore_text( params.get( 'short_desc', '' ) ) + user_address.name = util.restore_text( params.get( 'name', '' ) ) + user_address.institution = util.restore_text( params.get( 'institution', '' ) ) + user_address.address = util.restore_text( params.get( 'address1', '' ) )+' '+util.restore_text( params.get( 'address2', '' ) ) + user_address.city = util.restore_text( params.get( 'city', '' ) ) + user_address.state = util.restore_text( params.get( 'state', '' ) ) + user_address.postal_code = util.restore_text( params.get( 'postal_code', '' ) ) + user_address.country = util.restore_text( params.get( 'country', '' ) ) + user_address.phone = util.restore_text( params.get( 'phone', '' ) ) + user_address.flush() + msg = 'Changes made to address <b>%s</b> are saved.' % user_address.desc + if admin_view == 'True': + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + user_id=user.id, + admin_view=True, + msg=msg, + messagetype='done' ) ) + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + msg=msg, + messagetype='done') ) 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.' ) - short_desc_error = name_error = institution_error = address1_error = city_error = None - address2_error = state_error = postal_code_error = country_error = phone_error = None - if short_desc: - if not len( short_desc ): - short_desc_error = 'Enter a short description for this address' - elif not len( name ): - name_error = 'Enter the full name' - elif not len( institution ): - institution_error = 'Enter the institution associated with the user' - elif not len ( address1 ): - address1_error = 'Enter the address' - elif not len( city ): - city_error = 'Enter the city' - elif not len( state ): - state_error = 'Enter the state/province/region' - elif not len( postal_code ): - postal_code_error = 'Enter the postal code' - elif not len( country ): - country_error = 'Enter the country' - else: - if self.edit_address_id: - try: - user_address = trans.sa_session.query( trans.app.model.UserAddress ).get( int( self.edit_address_id ) ) - except: - return trans.response.send_redirect( web.url_for( controller='user', - action='manage_addresses', - msg='Invalid address ID', - messagetype='error') ) - user_address.desc = short_desc - user_address.name = name - user_address.institution = institution - user_address.address = address1+' '+address2 - user_address.city = city - user_address.state = state - user_address.postal_code = postal_code - user_address.country = country - user_address.phone = phone - user_address.flush() - self.edit_address_id = None - return trans.response.send_redirect( web.url_for( controller='user', - action='manage_addresses', - msg='Changes made to address <b>%s</b> are saved.' % user_address.desc, - messagetype='done') ) - self.edit_address_id = address_id - return trans.show_form( - web.FormBuilder( web.url_for(), "Edit address", submit_text="Save changes" ) - .add_text( "short_desc", "Short address description", value=short_desc, error=short_desc_error ) - .add_text( "name", "Name", value=name, error=name_error ) - .add_text( "institution", "Institution", value=institution, error=institution_error ) - .add_text( "address1", "Address Line 1", value=address1, error=address1_error ) - .add_text( "address2", "Address Line 2", value=address2, error=address2_error ) - .add_text( "city", "City", value=city, error=city_error ) - .add_text( "state", "State/Province/Region", value=state, error=state_error ) - .add_text( "postal_code", "Postal Code", value=postal_code, error=postal_code_error ) - .add_text( "country", "Country", value=country, error=country_error ) - .add_text( "phone", "Phone", value=phone, error=phone_error ) ) + # show the address form with the current values filled in + # create the widgets for each address field + widgets = [] + widgets.append(dict(label='Short description', + widget=TextField( 'short_desc', 40, user_address.desc ) ) ) + widgets.append(dict(label='Name', + widget=TextField( 'name', 40, user_address.name ) ) ) + widgets.append(dict(label='Institution', + widget=TextField( 'institution', 40, user_address.institution ) ) ) + widgets.append(dict(label='Address Line 1', + widget=TextField( 'address1', 40, user_address.address ) ) ) + widgets.append(dict(label='City', + widget=TextField( 'city', 40, user_address.city ) ) ) + widgets.append(dict(label='State', + widget=TextField( 'state', 40, user_address.state ) ) ) + widgets.append(dict(label='Postal Code', + widget=TextField( 'postal_code', 40, user_address.postal_code ) ) ) + widgets.append(dict(label='Country', + widget=TextField( 'country', 40, user_address.country ) ) ) + widgets.append(dict(label='Phone', + widget=TextField( 'phone', 40, user_address.phone ) ) ) + return trans.fill_template( 'user/edit_address.mako', user=user, + address=user_address, admin_view=admin_view, + widgets=widgets, msg=msg, messagetype=messagetype) @web.expose - def delete_address( self, trans, address_id=None): - 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.' ) + def delete_address( self, trans, address_id=None, user_id=None, admin_view='False'): try: user_address = trans.sa_session.query( trans.app.model.UserAddress ).get( int( address_id ) ) except: - return trans.fill_template( 'user/address.mako', - msg='Invalid address ID', - messagetype='error' ) + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + user_id=user_id, + admin_view=admin_view, + msg='Invalid address ID', + messagetype='error' ) ) user_address.deleted = True user_address.flush() return trans.response.send_redirect( web.url_for( controller='user', - action='manage_addresses', + action='show_info', + admin_view=admin_view, + user_id=user_id, msg='Address <b>%s</b> deleted' % user_address.desc, messagetype='done') ) @web.expose - def undelete_address( self, trans, address_id=None): - 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.' ) + def undelete_address( self, trans, address_id=None, user_id=None, admin_view='False'): try: user_address = trans.sa_session.query( trans.app.model.UserAddress ).get( int( address_id ) ) except: - return trans.fill_template( 'user/address.mako', - msg='Invalid address ID', - messagetype='error' ) + return trans.response.send_redirect( web.url_for( controller='user', + action='show_info', + user_id=user_id, + admin_view=admin_view, + msg='Invalid address ID', + messagetype='error' ) ) user_address.deleted = False user_address.flush() return trans.response.send_redirect( web.url_for( controller='user', - action='manage_addresses', - msg='Address <b>%s</b> is restored' % user_address.desc, + action='show_info', + admin_view=admin_view, + user_id=user_id, + msg='Address <b>%s</b> undeleted' % user_address.desc, messagetype='done') ) + diff -r 6e8263ea83fa -r cd0596754ff1 lib/galaxy/web/form_builder.py --- a/lib/galaxy/web/form_builder.py Mon Nov 02 09:54:18 2009 -0500 +++ b/lib/galaxy/web/form_builder.py Mon Nov 02 13:30:17 2009 -0500 @@ -33,6 +33,25 @@ def set_size(self, size): self.size = int( size ) +class PasswordField(BaseField): + """ + A password input box. text appears as "******" + + >>> print PasswordField( "foo" ).get_html() + <input type="password" name="foo" size="10" value=""> + >>> print PasswordField( "bins", size=4, value="default" ).get_html() + <input type="password" name="bins" size="4" value="default"> + """ + def __init__( self, name, size=None, value=None ): + self.name = name + self.size = int( size or 10 ) + self.value = value or "" + def get_html( self, prefix="" ): + return '<input type="password" name="%s%s" size="%d" value="%s">' \ + % ( prefix, self.name, self.size, escape(str(self.value), quote=True) ) + def set_size(self, size): + self.size = int( size ) + class NumberField(BaseField): """ A number input box. diff -r 6e8263ea83fa -r cd0596754ff1 lib/galaxy/web/framework/helpers/grids.py --- a/lib/galaxy/web/framework/helpers/grids.py Mon Nov 02 09:54:18 2009 -0500 +++ b/lib/galaxy/web/framework/helpers/grids.py Mon Nov 02 13:30:17 2009 -0500 @@ -266,10 +266,11 @@ return accepted_filters class GridOperation( object ): - def __init__( self, label, key=None, condition=None, allow_multiple=True, target=None, url_args=None ): + def __init__( self, label, key=None, condition=None, allow_multiple=True, allow_popup=True, target=None, url_args=None ): self.label = label self.key = key self.allow_multiple = allow_multiple + self.allow_popup = allow_popup self.condition = condition self.target = target self.url_args = url_args diff -r 6e8263ea83fa -r cd0596754ff1 templates/admin/user/grid.mako --- a/templates/admin/user/grid.mako Mon Nov 02 09:54:18 2009 -0500 +++ b/templates/admin/user/grid.mako Mon Nov 02 13:30:17 2009 -0500 @@ -195,7 +195,7 @@ <td> <div popupmenu="grid-${i}-popup"> %for operation in grid.operations: - %if operation.allowed( item ): + %if operation.allowed( item ) and operation.allow_popup: <a class="action-button" href="${url( operation=operation.label, id=item.id )}">${operation.label}</a> %endif %endfor diff -r 6e8263ea83fa -r cd0596754ff1 templates/user/edit_address.mako --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/user/edit_address.mako Mon Nov 02 13:30:17 2009 -0500 @@ -0,0 +1,33 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + + +%if msg: + ${render_msg( msg, messagetype )} +%endif +</br> +</br> +<h3>Edit address</h3> + +<ul class="manage-table-actions"> + <li> + <a class="action-button" href="${h.url_for( controller='user', action='show_info')}"> + <span>Manage User Information</span></a> + </li> +</ul> +<div class="toolForm"> +<form name="login_info" id="login_info" action="${h.url_for( controller='user', action='edit_address', admin_view=admin_view, address_id=address.id, user_id=user.id )}" method="post" > + <div class="toolFormTitle">Edit address</div> + <div class="toolFormBody"> + %for field in widgets: + <div class="form-row"> + <label>${field[ 'label' ]}</label> + ${field[ 'widget' ].get_html()} + </div> + %endfor + <div class="form-row"> + <input type="submit" name="edit_address_button" value="Save changes"> + </div> + </div> +</form> +</div> \ No newline at end of file diff -r 6e8263ea83fa -r cd0596754ff1 templates/user/index.mako --- a/templates/user/index.mako Mon Nov 02 09:54:18 2009 -0500 +++ b/templates/user/index.mako Mon Nov 02 13:30:17 2009 -0500 @@ -7,11 +7,8 @@ %if user: <p>You are currently logged in as ${user.email}.</p> <ul> - <li><a href="${h.url_for( action='change_password' )}">${_('Change your password')}</a></li> - <li><a href="${h.url_for( action='change_email' )}">${_('Update your email address')}</a></li> - <li><a href="${h.url_for( action='change_username' )}">${_('Change your public username')}</a></li> + <li><a href="${h.url_for( action='show_info' )}">${_('Manage your information')}</a></li> <li><a href="${h.url_for( action='set_default_permissions' )}">${_('Change default permissions')}</a> for new histories</li> - <li><a href="${h.url_for( action='manage_addresses' )}">${_('Manage your addresses')}</a></li> <li><a href="${h.url_for( action='logout' )}">${_('Logout')}</a></li> </ul> %else: diff -r 6e8263ea83fa -r cd0596754ff1 templates/user/info.mako --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/user/info.mako Mon Nov 02 13:30:17 2009 -0500 @@ -0,0 +1,161 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + + +%if msg: + ${render_msg( msg, messagetype )} +%endif + +<h2>Manage User Information</h2> +%if not admin_view: + <ul class="manage-table-actions"> + <li> + <a class="action-button" href="${h.url_for( controller='user', action='index')}"> + <span>User preferences</span></a> + </li> + </ul> +%endif + +<script type="text/javascript"> +$( function() { + $( "select[refresh_on_change='true']").change( function() { + var refresh = false; + var refresh_on_change_values = $( this )[0].attributes.getNamedItem( 'refresh_on_change_values' ) + if ( refresh_on_change_values ) { + refresh_on_change_values = refresh_on_change_values.value.split( ',' ); + var last_selected_value = $( this )[0].attributes.getNamedItem( 'last_selected_value' ); + for( i= 0; i < refresh_on_change_values.length; i++ ) { + if ( $( this )[0].value == refresh_on_change_values[i] || ( last_selected_value && last_selected_value.value == refresh_on_change_values[i] ) ){ + refresh = true; + break; + } + } + } + else { + refresh = true; + } + if ( refresh ){ + $( "#user_info" ).submit(); + } + }); +}); +</script> + +<div class="toolForm"> + <form name="login_info" id="login_info" action="${h.url_for( controller='user', action='edit_info', user_id=user.id, admin_view=admin_view )}" method="post" > + <div class="toolFormTitle">Login Information</div> + <div class="form-row"> + <label>Email</label> + ${login_info[ 'Email' ].get_html()} + </div> + <div class="form-row"> + <label>Public Username</label> + ${login_info[ 'Public Username' ].get_html()} + </div> + <div class="form-row"> + <input type="submit" name="login_info_button" value="Save"> + </div> + </form> + <form name="change_password" id="change_password" action="${h.url_for( controller='user', action='edit_info', user_id=user.id, admin_view=admin_view )}" method="post" > + <div class="toolFormTitle">Change Password</div> + %if not admin_view: + <div class="form-row"> + <label>Current Password</label> + ${login_info[ 'Current Password' ].get_html()} + </div> + %endif + <div class="form-row"> + <label>New Password</label> + ${login_info[ 'New Password' ].get_html()} + </div> + <div class="form-row"> + <label>Confirm</label> + ${login_info[ 'Confirm' ].get_html()} + </div> + <div class="form-row"> + <input type="submit" name="change_password_button" value="Save"> + </div> + </form> + %if user.values: + <form name="user_info" id="user_info" action="${h.url_for( controller='user', action='edit_info', user_id=user.id, admin_view=admin_view )}" method="post" > + <div class="toolFormTitle">User information</div> + %if user_info_form: + %for field in widgets: + <div class="form-row"> + <label>${field['label']}</label> + ${field['widget'].get_html()} + <div class="toolParamHelp" style="clear: both;"> + ${field['helptext']} + </div> + <div style="clear: both"></div> + </div> + %endfor + %if not user_info_select: + <input type="hidden" name="user_info_select" value="${user_info_form.id}"/> + %endif + %endif + <div class="form-row"> + <input type="submit" name="edit_user_info_button" value="Save"> + </div> + </form> + %endif + <form name="user_info" id="user_info" action="${h.url_for( controller='user', action='new_address' )}" method="post" > + <div class="toolFormTitle">User Addresses</div> + <div class="toolFormBody"> + %if user.addresses: + <div class="form-row"> + <div class="grid-header"> + ##<span class="title">Filter:</span> + %for i, filter in enumerate( ['Active', 'Deleted', 'All'] ): + %if i > 0: + <span>|</span> + %endif + %if show_filter == filter: + <span class="filter"><a href="${h.url_for( controller='user', action='show_info', show_filter=filter, user_id=user.id, admin_view=admin_view )}"><b>${filter}</b></a></span> + %else: + <span class="filter"><a href="${h.url_for( controller='user', action='show_info', show_filter=filter, user_id=user.id, admin_view=admin_view )}">${filter}</a></span> + %endif + %endfor + </div> + </div> + <table class="grid"> + <tbody> + %for index, address in enumerate(addresses): + <tr class="libraryRow libraryOrFolderRow" id="libraryRow"> + <td> + <div class="form-row"> + <label>${address.desc}</label> + ${address.get_html()} + </div> + <div class="form-row"> + <ul class="manage-table-actions"> + <li> + %if not address.deleted: + <a class="action-button" href="${h.url_for( controller='user', action='edit_address', admin_view=admin_view, address_id=address.id, user_id=user.id )}"> + <span>Edit</span></a> + <a class="action-button" href="${h.url_for( controller='user', action='delete_address', admin_view=admin_view, address_id=address.id, user_id=user.id)}"> + <span>Delete</span></a> + %else: + <a class="action-button" href="${h.url_for( controller='user', action='undelete_address', admin_view=admin_view, address_id=address.id, user_id=user.id)}"> + <span>Undelete</span></a> + %endif + + </li> + </ul> + </div> + </td> + </tr> + %endfor + </tbody> + </table> + %endif + <div class="form-row"> + <input type="submit" value="Add a new address"> + </div> + </div> + </form> + + + + +</div> \ No newline at end of file diff -r 6e8263ea83fa -r cd0596754ff1 templates/user/permissions.mako --- a/templates/user/permissions.mako Mon Nov 02 09:54:18 2009 -0500 +++ b/templates/user/permissions.mako Mon Nov 02 13:30:17 2009 -0500 @@ -1,7 +1,13 @@ <%inherit file="/base.mako"/> <%def name="title()">Change Default Permissions on New Histories</%def> <%namespace file="/dataset/security_common.mako" import="render_permission_form" /> - +<br/><br/> +<ul class="manage-table-actions"> + <li> + <a class="action-button" href="${h.url_for( controller='user', action='index')}"> + <span>User preferences</span></a> + </li> +</ul> %if trans.user: ${render_permission_form( trans.user, trans.user.email, h.url_for(), trans.user.all_roles() )} %endif diff -r 6e8263ea83fa -r cd0596754ff1 templates/user/register.mako --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/user/register.mako Mon Nov 02 13:30:17 2009 -0500 @@ -0,0 +1,87 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + + +%if msg: + ${render_msg( msg, messagetype )} +%endif + + + +<script type="text/javascript"> +$( function() { + $( "select[refresh_on_change='true']").change( function() { + var refresh = false; + var refresh_on_change_values = $( this )[0].attributes.getNamedItem( 'refresh_on_change_values' ) + if ( refresh_on_change_values ) { + refresh_on_change_values = refresh_on_change_values.value.split( ',' ); + var last_selected_value = $( this )[0].attributes.getNamedItem( 'last_selected_value' ); + for( i= 0; i < refresh_on_change_values.length; i++ ) { + if ( $( this )[0].value == refresh_on_change_values[i] || ( last_selected_value && last_selected_value.value == refresh_on_change_values[i] ) ){ + refresh = true; + break; + } + } + } + else { + refresh = true; + } + if ( refresh ){ + $( "#registration" ).submit(); + } + }); +}); +</script> + +<div class="toolForm"> + <form name="registration" id="registration" action="${h.url_for( controller='user', action='create', admin_view=admin_view )}" method="post" > + <div class="toolFormTitle">Create account</div> + <div class="form-row"> + <label>Email</label> + ${login_info[ 'Email' ].get_html()} + </div> + <div class="form-row"> + <label>Password</label> + ${login_info[ 'Password' ].get_html()} + </div> + <div class="form-row"> + <label>Confirm</label> + ${login_info[ 'Confirm' ].get_html()} + </div> + <div class="form-row"> + <label>Public Username</label> + ${login_info[ 'Public Username' ].get_html()} + <div class="toolParamHelp" style="clear: both;"> + Optional + </div> + </div> + <div class="form-row"> + <label>Subscribe To Mailing List</label> + ${login_info[ 'Subscribe To Mailing List' ].get_html()} + </div> + %if user_info_select: + <div class="form-row"> + <label>User type</label> + ${user_info_select.get_html()} + </div> + %endif + %if user_info_form: + %for field in widgets: + <div class="form-row"> + <label>${field['label']}</label> + ${field['widget'].get_html()} + <div class="toolParamHelp" style="clear: both;"> + ${field['helptext']} + </div> + <div style="clear: both"></div> + </div> + %endfor + %if not user_info_select: + <input type="hidden" name="user_info_select" value="${user_info_form.id}"/> + %endif + %endif + <div class="form-row"> + <input type="submit" name="create_user_button" value="Submit"> + </div> + </form> +</div> \ No newline at end of file diff -r 6e8263ea83fa -r cd0596754ff1 test/base/twilltestcase.py --- a/test/base/twilltestcase.py Mon Nov 02 09:54:18 2009 -0500 +++ b/test/base/twilltestcase.py Mon Nov 02 13:30:17 2009 -0500 @@ -596,13 +596,82 @@ # Functions associated with user accounts def create( self, email='test@bx.psu.edu', password='testuser' ): self.home() - self.visit_page( "user/create?email=%s&password=%s&confirm=%s" % ( email, password, password ) ) + self.visit_page( "user/create?email=%s&password=%s&confirm=%s&create_user_button=Submit" % ( email, password, password ) ) self.check_page_for_string( "Now logged in as %s" %email ) self.home() # Make sure a new private role was created for the user self.visit_page( "user/set_default_permissions" ) self.check_page_for_string( email ) self.home() + def create_user_with_info( self, email, password, username, user_info_forms, user_info_form_id, user_info_values ): + ''' + This method registers a new user and also provides use info + ''' + self.home() + if user_info_forms == 'multiple': + self.visit_page( "user/create?user_info_select=%i&admin_view=False" % user_info_form_id ) + else: + self.visit_page( "user/create?admin_view=False" ) + self.check_page_for_string( "Create account" ) + tc.fv( "1", "email", email ) + tc.fv( "1", "password", password ) + tc.fv( "1", "confirm", password ) + tc.fv( "1", "username", username ) + if user_info_forms == 'multiple': + self.check_page_for_string( "User type" ) + for index, info_value in enumerate(user_info_values): + tc.fv( "1", "field_%i" % index, info_value ) + tc.submit( "create_user_button" ) + self.check_page_for_string( "Now logged in as %s" % email ) + def create_user_with_info_as_admin( self, email, password, username, user_info_forms, user_info_form_id, user_info_values ): + ''' + This method registers a new user and also provides use info as an admin + ''' + self.home() + if user_info_forms == 'multiple': + self.visit_page( "admin/users?operation=create?user_info_select=%i&admin_view=False" % user_info_form_id ) + else: + self.visit_page( "admin/users?operation=create" ) + self.check_page_for_string( "Create account" ) + tc.fv( "1", "email", email ) + tc.fv( "1", "password", password ) + tc.fv( "1", "confirm", password ) + tc.fv( "1", "username", username ) + if user_info_forms == 'multiple': + self.check_page_for_string( "User type" ) + for index, info_value in enumerate(user_info_values): + tc.fv( "1", "field_%i" % index, info_value ) + tc.submit( "create_user_button" ) + self.check_page_for_string( "Created new user account (%s)" % email ) + def edit_login_info( self, new_email, new_username ): + self.home() + self.visit_page( "user/show_info" ) + self.check_page_for_string( "Manage User Information" ) + tc.fv( "1", "email", new_email ) + tc.fv( "1", "username", new_username ) + tc.submit( "login_info_button" ) + self.check_page_for_string( 'The login information has been updated with the changes' ) + self.check_page_for_string( new_email ) + self.check_page_for_string( new_username ) + def change_password( self, password, new_password ): + self.home() + self.visit_page( "user/show_info" ) + self.check_page_for_string( "Manage User Information" ) + tc.fv( "2", "current", password ) + tc.fv( "2", "password", new_password ) + tc.fv( "2", "confirm", new_password ) + tc.submit( "change_password_button" ) + self.check_page_for_string( 'The password has been changed.' ) + def edit_user_info( self, info_values ): + self.home() + self.visit_page( "user/show_info" ) + self.check_page_for_string( "Manage User Information" ) + for index, info_value in enumerate(info_values): + tc.fv( "3", "field_%i" % index, info_value ) + tc.submit( "edit_user_info_button" ) + self.check_page_for_string( "The user information has been updated with the changes." ) + for value in info_values: + self.check_page_for_string( value ) def user_set_default_permissions( self, permissions_out=[], permissions_in=[], role_id=2 ): # role.id = 2 is Private Role for test2@bx.psu.edu # NOTE: Twill has a bug that requires the ~/user/permissions page to contain at least 1 option value # in each select list or twill throws an exception, which is: ParseError: OPTION outside of SELECT @@ -1161,19 +1230,8 @@ for index, field_value in enumerate(fields): tc.fv( "1", "field_%i" % index, field_value ) tc.submit( "create_request_button" ) - def create_request_admin( self, request_type_id, user_id, name, desc, library_id, fields ): - self.home() - self.visit_url( "%s/requests_admin/new?create=True&select_request_type=%i&library_id=%i" % ( self.url, - request_type_id, - library_id ) ) - self.check_page_for_string( 'Add a new request' ) - tc.fv( "1", "select_user", str(user_id) ) - tc.fv( "1", "name", name ) - tc.fv( "1", "desc", desc ) - tc.fv( "1", "library_id", str(library_id) ) - for index, field_value in enumerate(fields): - tc.fv( "1", "field_%i" % index, field_value ) - tc.submit( "create_request_button" ) + self.check_page_for_string( name ) + self.check_page_for_string( desc ) def edit_request( self, request_id, name, new_name, new_desc, new_library_id, new_folder_id, new_fields): self.home() self.visit_url( "%s/requests/edit?request_id=%i&show=True" % (self.url, request_id) ) @@ -1185,6 +1243,8 @@ for index, field_value in enumerate(new_fields): tc.fv( "1", "field_%i" % index, field_value ) tc.submit( "save_changes_request_button" ) + self.check_page_for_string( new_name ) + self.check_page_for_string( new_desc ) def add_samples( self, request_id, request_name, samples ): self.home() self.visit_url( "%s/requests/list?sort=-create_time&operation=show_request&id=%s" % ( self.url, self.security.encode_id( request_id ) )) @@ -1196,6 +1256,11 @@ for field_index, field_value in enumerate(fields): tc.fv( "1", "sample_%i_field_%i" % ( sample_index, field_index ), field_value ) tc.submit( "save_samples_button" ) + for sample_name, fields in samples: + self.check_page_for_string( sample_name ) + self.check_page_for_string( 'Unsubmitted' ) + for field_value in fields: + self.check_page_for_string( field_value ) def submit_request( self, request_id, request_name ): self.home() self.visit_url( "%s/requests/submit_request?id=%i" % ( self.url, request_id )) @@ -1207,21 +1272,15 @@ for index, bar_code in enumerate(bar_codes): tc.fv( "1", "sample_%i_bar_code" % index, bar_code ) tc.submit( "save_bar_codes" ) - def change_sample_state( self, sample_name, sample_id, new_state_id, comment='' ): + self.check_page_for_string( 'Bar codes has been saved for this request' ) + def change_sample_state( self, sample_name, sample_id, new_state_id, new_state_name, comment='' ): self.home() self.visit_url( "%s/requests_admin/show_events?sample_id=%i" % (self.url, sample_id) ) self.check_page_for_string( 'Events for Sample "%s"' % sample_name ) tc.fv( "1", "select_state", str(new_state_id) ) tc.fv( "1", "comment", comment ) tc.submit( "add_event_button" ) - # Address stuff - def create_address( self, address ): - self.home() - self.visit_url( "%s/user/new_address" % self.url ) - self.check_page_for_string( 'New address' ) - for name, value in address.iteritems(): - tc.fv( "1", name, value ) - tc.submit( "Save_button" ) + self.check_page_for_string( new_state_name ) # Library stuff def create_library( self, name='Library One', description='This is Library One' ): """Create a new library""" diff -r 6e8263ea83fa -r cd0596754ff1 test/functional/test_forms_and_requests.py --- a/test/functional/test_forms_and_requests.py Mon Nov 02 09:54:18 2009 -0500 +++ b/test/functional/test_forms_and_requests.py Mon Nov 02 13:30:17 2009 -0500 @@ -41,16 +41,11 @@ self.login( email='test@bx.psu.edu' ) # create a form global form_one_name - name = form_one_name desc = "This is Form One's description" formtype = galaxy.model.FormDefinition.types.REQUEST - self.create_form( name=name, desc=desc, formtype=formtype, num_fields=0 ) + self.create_form( name=form_one_name, desc=desc, formtype=formtype, num_fields=0 ) # Get the form_definition object for later tests - form_one = sa_session.query( galaxy.model.FormDefinition ) \ - .filter( and_( galaxy.model.FormDefinition.table.c.name==name, - galaxy.model.FormDefinition.table.c.desc==desc, - galaxy.model.FormDefinition.table.c.type==formtype ) ) \ - .all()[-1] + form_one = get_latest_form(form_one_name) assert form_one is not None, 'Problem retrieving form named "%s" from the database' % name # edit form & add few more fields new_name = "Request Form (Renamed)" @@ -83,20 +78,18 @@ def test_015_create_sample_form( self ): """Testing creating another form (for samples)""" global form_two_name - name = form_two_name desc = "This is Form One's description" formtype = 'Sequencing Sample Form' - self.create_form( name=name, desc=desc, formtype=formtype ) + self.create_form( name=form_two_name, desc=desc, formtype=formtype ) self.home() self.visit_page( 'forms/manage' ) - self.check_page_for_string( name ) + self.check_page_for_string( form_two_name ) self.check_page_for_string( desc ) self.check_page_for_string( formtype ) def test_020_create_request_type( self ): """Testing creating a new requestype""" request_form = get_latest_form(form_one_name) sample_form = get_latest_form(form_two_name) - print request_form.id, sample_form.id self.create_request_type(request_type_name, "test request type", str(request_form.id), str(sample_form.id), sample_states ) global request_type @@ -179,7 +172,6 @@ % ( self.url, address1[ 'short_desc' ], address1[ 'name' ], address1[ 'institution' ], address1[ 'address1' ], address1[ 'address2' ], address1[ 'city' ], address1[ 'state' ], address1[ 'postal_code' ], address1[ 'country' ], address1[ 'phone' ] ) - print url_str self.visit_url( url_str ) self.check_page_for_string( 'Address <b>%s</b> has been added' % address1[ 'short_desc' ] ) global regular_user @@ -201,8 +193,6 @@ # create the request request_name, request_desc = 'Request One', 'Request One Description' self.create_request(request_type.id, request_name, request_desc, library_one.id, 'none', fields) - self.check_page_for_string( request_name ) - self.check_page_for_string( request_desc ) global request_one request_one = sa_session.query( galaxy.model.Request ) \ .filter( and_( galaxy.model.Request.table.c.name==request_name, @@ -216,18 +206,11 @@ ( 'Sample Two', [ 'S2 Field 0 Value' ] ) ] # add samples to this request self.add_samples( request_one.id, request_one.name, samples ) - for sample_name, fields in samples: - self.check_page_for_string( sample_name ) - self.check_page_for_string( 'Unsubmitted' ) - for field_value in fields: - self.check_page_for_string( field_value ) # edit this request fields = ['option2', str(user_address.id), 'field three value (edited)'] self.edit_request(request_one.id, request_one.name, request_one.name+' (Renamed)', request_one.desc+' (Re-described)', library_one.id, folder_one.id, fields) sa_session.refresh( request_one ) - self.check_page_for_string( request_name+' (Renamed)' ) - self.check_page_for_string( request_desc+' (Re-described)' ) # check if the request is showing in the 'unsubmitted' filter self.home() self.visit_url( '%s/requests/list?show_filter=Unsubmitted' % self.url ) @@ -256,15 +239,10 @@ # set bar codes for the samples bar_codes = [ '1234567890', '0987654321' ] self.add_bar_codes( request_one.id, request_one.name, bar_codes ) - self.check_page_for_string( 'Bar codes has been saved for this request' ) # change the states of all the samples of this request for sample in request_one.samples: - self.change_sample_state( sample.name, sample.id, request_type.states[1].id ) - self.check_page_for_string( request_type.states[1].name ) - self.check_page_for_string( request_type.states[1].desc ) - self.change_sample_state( sample.name, sample.id, request_type.states[2].id ) - self.check_page_for_string( request_type.states[2].name ) - self.check_page_for_string( request_type.states[2].desc ) + self.change_sample_state( sample.name, sample.id, request_type.states[1].id, request_type.states[1].name ) + self.change_sample_state( sample.name, sample.id, request_type.states[2].id, request_type.states[2].name ) self.home() sa_session.refresh( request_one ) # check if the request's state is now set to 'complete' @@ -300,11 +278,6 @@ ( 'Sample Two', [ 'S2 Field 0 Value' ] ) ] # add samples to this request self.add_samples( request_two.id, request_two.name, samples ) - for sample_name, fields in samples: - self.check_page_for_string( sample_name ) - self.check_page_for_string( 'Unsubmitted' ) - for field_value in fields: - self.check_page_for_string( field_value ) # submit the request self.submit_request( request_two.id, request_two.name ) sa_session.refresh( request_two ) diff -r 6e8263ea83fa -r cd0596754ff1 test/functional/test_user_info.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/functional/test_user_info.py Mon Nov 02 13:30:17 2009 -0500 @@ -0,0 +1,149 @@ +import galaxy.model +from galaxy.model.orm import * +from galaxy.model.mapping import context as sa_session +from base.twilltestcase import * + +not_logged_in_as_admin_security_msg = 'You must be logged in as an administrator to access this feature.' +logged_in_as_admin_security_msg = 'You must be an administrator to access this feature.' +not_logged_in_security_msg = 'You must be logged in to create/submit sequencing requests' +form_one_name = "Student" +form_two_name = "Researcher" + +def get_latest_form(form_name): + fdc_list = sa_session.query( galaxy.model.FormDefinitionCurrent ) \ + .filter( galaxy.model.FormDefinitionCurrent.table.c.deleted==False ) \ + .order_by( galaxy.model.FormDefinitionCurrent.table.c.create_time.desc() ) + for fdc in fdc_list: + if form_name == fdc.latest_form.name: + return fdc.latest_form + return None + + +class TestUserInfo( TwillTestCase ): + def test_000_create_user_info_forms( self ): + """Testing creating a new user info form and editing it""" + self.logout() + self.login( email='test@bx.psu.edu' ) + # create a the first form + global form_one_name + name = form_one_name + desc = "This is Student user info form's description" + formtype = galaxy.model.FormDefinition.types.USER_INFO + self.create_form( name=name, desc=desc, formtype=formtype, num_fields=0 ) + # Get the form_definition object for later tests + form_one = get_latest_form(form_one_name) + assert form_one is not None, 'Problem retrieving form named "%s" from the database' % name + # edit form & add few more fields + fields = [dict(name='Affiliation', + desc='The type of organization you are affiliated with', + type='SelectField', + required='optional', + selectlist=['Educational', 'Research', 'Commercial']), + dict(name='Name of Organization', + desc='', + type='TextField', + required='optional')] + form_one = get_latest_form(form_one_name) + self.form_add_field(form_one.id, form_one.name, form_one.desc, form_one.type, field_index=len(form_one.fields), fields=fields) + form_one_latest = get_latest_form(form_one_name) + assert len(form_one_latest.fields) == len(form_one.fields)+len(fields) + # create the second form + global form_two_name + name = form_two_name + desc = "This is Researcher user info form's description" + self.create_form( name=name, desc=desc, formtype=formtype, num_fields=0 ) + # Get the form_definition object for later tests + form_two = get_latest_form(form_two_name) + assert form_two is not None, 'Problem retrieving form named "%s" from the database' % name + # edit form & add few more fields + fields = [dict(name='Affiliation', + desc='The type of organization you are affiliated with', + type='SelectField', + required='optional', + selectlist=['Educational', 'Research', 'Commercial']), + dict(name='Name of Organization', + desc='', + type='TextField', + required='optional')] + form_two = get_latest_form(form_two_name) + self.form_add_field(form_two.id, form_two.name, form_two.desc, form_two.type, field_index=len(form_one.fields), fields=fields) + form_two_latest = get_latest_form(form_two_name) + assert len(form_two_latest.fields) == len(form_two.fields)+len(fields) + def test_005_user_reqistration_multiple_user_info_forms( self ): + ''' Testing user registration with multiple user info forms ''' + self.logout() + # user a new user with 'Student' user info form + form_one = get_latest_form(form_one_name) + user_info_values=['Educational', 'Penn State'] + self.create_user_with_info( 'test1@bx.psu.edu', 'testuser', 'test1', + user_info_forms='multiple', + user_info_form_id=form_one.id, + user_info_values=user_info_values ) + self.home() + self.visit_page( "user/show_info" ) + self.check_page_for_string( "Manage User Information" ) + for value in user_info_values: + self.check_page_for_string( value ) + def test_010_user_reqistration_single_user_info_forms( self ): + ''' Testing user registration with a single user info form ''' + # lets delete the 'Researcher' user info form + self.login( 'test@bx.psu.edu' ) + form_two_latest = get_latest_form(form_two_name) + form_two_latest.current.deleted = True + form_two_latest.current.flush() + self.home() + self.visit_page('forms/manage?show_filter=Deleted') + self.check_page_for_string(form_two_latest.name) + self.logout() + # user a new user with 'Student' user info form + form_one = get_latest_form(form_one_name) + user_info_values=['Educational', 'Penn State'] + self.create_user_with_info( 'test2@bx.psu.edu', 'testuser', 'test2', + user_info_forms='single', + user_info_form_id=form_one.id, + user_info_values=user_info_values ) + self.home() + self.visit_page( "user/show_info" ) + self.check_page_for_string( "Manage User Information" ) + for value in user_info_values: + self.check_page_for_string( value ) + def test_015_edit_user_info( self ): + """Testing editing user info as a regular user""" + self.logout() + self.login( 'test1@bx.psu.edu' ) + user = sa_session.query( galaxy.model.User ) \ + .filter( and_( galaxy.model.User.table.c.email=='test1@bx.psu.edu' ) ).first() + self.edit_login_info( new_email='test1_new@bx.psu.edu', new_username='test1_new' ) + self.change_password('testuser', 'new_testuser') + self.edit_user_info( ['Research', 'PSU'] ) + def test_020_create_user_as_admin( self ): + ''' Testing creating users as an admin ''' + self.logout() + self.login( 'test2@bx.psu.edu' ) + form_one = get_latest_form(form_one_name) + user_info_values=['Educational', 'Penn State'] + self.create_user_with_info( 'test3@bx.psu.edu', 'testuser', 'test3', + user_info_forms='single', + user_info_form_id=form_one.id, + user_info_values=user_info_values ) + self.logout() + self.login( 'test@bx.psu.edu' ) + user = sa_session.query( galaxy.model.User ) \ + .filter( and_( galaxy.model.User.table.c.email=='test3@bx.psu.edu' ) ).first() + self.home() + page = "admin/users?id=%s&operation=information&f-deleted=False" % self.security.encode_id( user.id ) + self.visit_page( page ) + self.check_page_for_string( 'Manage User Information' ) + self.check_page_for_string( 'test3@bx.psu.edu' ) + for value in user_info_values: + self.check_page_for_string( value ) + # lets delete the 'Student' user info form + self.login( 'test@bx.psu.edu' ) + form_one_latest = get_latest_form(form_one_name) + form_one_latest.current.deleted = True + form_one_latest.current.flush() + self.home() + self.visit_page('forms/manage?show_filter=Deleted') + self.check_page_for_string(form_one_latest.name) + self.logout() + \ No newline at end of file