diff --git a/config/disposable_email_blacklist.conf.sample b/config/disposable_email_blacklist.conf.sample
new file mode 100644
index 00000000000..44759454c9c
--- /dev/null
+++ b/config/disposable_email_blacklist.conf.sample
@@ -0,0 +1,9 @@
+If you want to disable registration for users that are using disposable email address
+rename this file to disposable_email_blacklist.conf and fill it with the disposable domains
+that you want to have blacklisted. Each on its own line without the '@' character as shown below.
+Users using emails from these domains will get an error during the registration.
+
+mailinator.com
+sogetthis.com
+spamgourmet.com
+trashmail.net
\ No newline at end of file
diff --git a/lib/galaxy/config.py b/lib/galaxy/config.py
index 9a6506be36d..6255a5708c6 100644
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -132,6 +132,14 @@ class Configuration( object ):
self.admin_users = kwargs.get( "admin_users", "" )
self.mailing_join_addr = kwargs.get('mailing_join_addr',"galaxy-announce-join@bx.psu.edu")
self.error_email_to = kwargs.get( 'error_email_to', None )
+ self.admin_email = kwargs.get( 'admin_email', None )
+ self.user_activation_on = kwargs.get( 'user_activation_on', None )
+ self.activation_grace_period = kwargs.get( 'activation_grace_period', None )
+ self.inactivity_box_visible = kwargs.get( 'inactivity_box_visible', None )
+ self.inactivity_box_content = kwargs.get( 'inactivity_box_content', None )
+ self.inactivity_box_class = kwargs.get( 'inactivity_box_class', None )
+ # Get the disposable email domains blacklist file
+ self.blacklist_file = resolve_path( kwargs.get( 'blacklist_file', None ), self.root )
self.smtp_server = kwargs.get( 'smtp_server', None )
self.smtp_username = kwargs.get( 'smtp_username', None )
self.smtp_password = kwargs.get( 'smtp_password', None )
diff --git a/lib/galaxy/jobs/handler.py b/lib/galaxy/jobs/handler.py
index e6cb96b4d33..d738c90cce9 100644
--- a/lib/galaxy/jobs/handler.py
+++ b/lib/galaxy/jobs/handler.py
@@ -84,12 +84,25 @@ class JobHandlerQueue( object ):
Checks all jobs that are in the 'new', 'queued' or 'running' state in
the database and requeues or cleans up as necessary. Only run as the
job handler starts.
+ In case the activation is enforced it will filter out the jobs of inactive users.
"""
- for job in self.sa_session.query( model.Job ).enable_eagerloads( False ) \
+ jobs_at_startup = []
+ if self.app.config.user_activation_on:
+ jobs_at_startup = self.sa_session.query( model.Job ).enable_eagerloads( False ) \
+ .outerjoin( model.User ) \
.filter( ( ( model.Job.state == model.Job.states.NEW ) \
| ( model.Job.state == model.Job.states.RUNNING ) \
| ( model.Job.state == model.Job.states.QUEUED ) ) \
- & ( model.Job.handler == self.app.config.server_name ) ):
+ & ( model.Job.handler == self.app.config.server_name ) \
+ & or_( ( model.Job.user_id == None ),( model.User.active == True ) ) ).all()
+ else:
+ jobs_at_startup = self.sa_session.query( model.Job ).enable_eagerloads( False ) \
+ .filter( ( ( model.Job.state == model.Job.states.NEW ) \
+ | ( model.Job.state == model.Job.states.RUNNING ) \
+ | ( model.Job.state == model.Job.states.QUEUED ) ) \
+ & ( model.Job.handler == self.app.config.server_name ) ).all()
+
+ for job in jobs_at_startup:
if job.tool_id not in self.app.toolbox.tools_by_id:
log.warning( "(%s) Tool '%s' removed from tool config, unable to recover job" % ( job.id, job.tool_id ) )
JobWrapper( job, self ).fail( 'This tool was disabled before the job completed. Please contact your Galaxy administrator.' )
@@ -146,8 +159,9 @@ class JobHandlerQueue( object ):
over all new and waiting jobs to check the state of the jobs each
depends on. If the job has dependencies that have not finished, it
it goes to the waiting queue. If the job has dependencies with errors,
- it is marked as having errors and removed from the queue. Otherwise,
- the job is dispatched.
+ it is marked as having errors and removed from the queue. If the job
+ belongs to an inactive user it is ignored.
+ Otherwise, the job is dispatched.
"""
# Pull all new jobs from the queue at once
jobs_to_check = []
@@ -173,7 +187,17 @@ class JobHandlerQueue( object ):
(model.LibraryDatasetDatasetAssociation.deleted == True),
(model.Dataset.state != model.Dataset.states.OK),
(model.Dataset.deleted == True)))).subquery()
- jobs_to_check = self.sa_session.query(model.Job).enable_eagerloads(False) \
+ if self.app.config.user_activation_on:
+ jobs_to_check = self.sa_session.query(model.Job).enable_eagerloads(False) \
+ .outerjoin( model.User ) \
+ .filter(and_((model.Job.state == model.Job.states.NEW),
+ or_((model.Job.user_id == None),(model.User.active == True)),
+ (model.Job.handler == self.app.config.server_name),
+ ~model.Job.table.c.id.in_(hda_not_ready),
+ ~model.Job.table.c.id.in_(ldda_not_ready))) \
+ .order_by(model.Job.id).all()
+ else:
+ jobs_to_check = self.sa_session.query(model.Job).enable_eagerloads(False) \
.filter(and_((model.Job.state == model.Job.states.NEW),
(model.Job.handler == self.app.config.server_name),
~model.Job.table.c.id.in_(hda_not_ready),
diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py
index fb513a0121d..91597889137 100644
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -78,6 +78,8 @@ class User( object, Dictifiable ):
self.external = False
self.deleted = False
self.purged = False
+ self.active = False
+ self.activation_token = None
self.username = None
# Relationships
self.histories = []
diff --git a/lib/galaxy/model/mapping.py b/lib/galaxy/model/mapping.py
index 8459f972cb5..94f6a0859f4 100644
--- a/lib/galaxy/model/mapping.py
+++ b/lib/galaxy/model/mapping.py
@@ -52,7 +52,9 @@ model.User.table = Table( "galaxy_user", metadata,
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 ),
- Column( "disk_usage", Numeric( 15, 0 ), index=True ) )
+ Column( "disk_usage", Numeric( 15, 0 ), index=True ) ,
+ Column( "active", Boolean, index=True, default=True, nullable=False ),
+ Column( "activation_token", TrimmedString( 64 ), nullable=True, index=True ) )
model.UserAddress.table = Table( "user_address", metadata,
Column( "id", Integer, primary_key=True),
diff --git a/lib/galaxy/model/migrate/versions/0117_add_user_activation.py b/lib/galaxy/model/migrate/versions/0117_add_user_activation.py
new file mode 100644
index 00000000000..6cbfc8fa250
--- /dev/null
+++ b/lib/galaxy/model/migrate/versions/0117_add_user_activation.py
@@ -0,0 +1,57 @@
+'''
+Created on Sep 10, 2013
+
+@author: marten
+
+Adds 'active' and 'activation_token' columns to the galaxy_user table.
+'''
+
+from sqlalchemy import *
+from sqlalchemy.orm import *
+from migrate import *
+from migrate.changeset import *
+from galaxy.model.custom_types import TrimmedString
+
+import logging
+log = logging.getLogger( __name__ )
+
+user_active_column = Column( "active", Boolean, default=True, nullable=True )
+user_activation_token_column = Column( "activation_token", TrimmedString( 64 ), nullable=True )
+
+
+def display_migration_details():
+ print ""
+ print "This migration script adds active and activation_token columns to the user table"
+
+def upgrade(migrate_engine):
+ print __doc__
+ metadata = MetaData()
+ metadata.bind = migrate_engine
+ metadata.reflect()
+
+ # Add the active and activation_token columns to the user table in one try because the depend on each other.
+ try:
+ user_table = Table( "galaxy_user", metadata, autoload=True )
+ user_active_column.create( table = user_table , populate_default = True)
+ user_activation_token_column.create( table = user_table )
+ assert user_active_column is user_table.c.active
+ assert user_activation_token_column is user_table.c.activation_token
+ except Exception, e:
+ print str(e)
+ log.error( "Adding columns 'active' and 'activation_token' to galaxy_user table failed: %s" % str( e ) )
+ return
+
+def downgrade(migrate_engine):
+ metadata = MetaData()
+ metadata.bind = migrate_engine
+ metadata.reflect()
+
+ # Drop the user table's active and activation_token columns in one try because the depend on each other.
+ try:
+ user_table = Table( "galaxy_user", metadata, autoload=True )
+ user_active = user_table.c.active
+ user_activation_token = user_table.c.activation_token
+ user_active.drop()
+ user_activation_token.drop()
+ except Exception, e:
+ log.debug( "Dropping 'active' and 'activation_token' columns from galaxy_user table failed: %s" % ( str( e ) ) )
\ No newline at end of file
diff --git a/lib/galaxy/security/validate_user_input.py b/lib/galaxy/security/validate_user_input.py
index e6ce72a904b..8f3e8b7a6a2 100644
--- a/lib/galaxy/security/validate_user_input.py
+++ b/lib/galaxy/security/validate_user_input.py
@@ -2,18 +2,31 @@ import re
VALID_PUBLICNAME_RE = re.compile( "^[a-z0-9\-]+$" )
VALID_PUBLICNAME_SUB = re.compile( "[^a-z0-9\-]" )
+# Basic regular expression to check email validity.
+VALID_EMAIL_RE = re.compile( "[^@]+@[^@]+\.[^@]+" )
FILL_CHAR = '-'
def validate_email( trans, email, user=None, check_dup=True ):
+ """
+ Validates the email format, also checks whether the domain is blacklisted in the disposable domains configuration.
+ """
message = ''
+ # Load the blacklist file location from the configuration file.
+ blacklist_file = trans.app.config.blacklist_file
+ if blacklist_file is not None:
+ email_blacklist = [ line.rstrip() for line in file( blacklist_file ).readlines() ]
if user and user.email == email:
return message
- if len( email ) == 0 or "@" not in email or "." not in email:
- message = "Enter a real email address"
+ if not( VALID_EMAIL_RE.match( email ) ):
+ message = "Please enter your real email address."
elif len( email ) > 255:
- message = "Email address exceeds maximum allowable length"
+ message = "Email address exceeds maximum allowable length."
elif check_dup and trans.sa_session.query( trans.app.model.User ).filter_by( email=email ).first():
- message = "User with that email already exists"
+ message = "User with that email already exists."
+ # If the blacklist is not empty filter out the disposable domains.
+ elif email_blacklist is not None:
+ if email.split('@')[1] in email_blacklist:
+ message = "Please enter your permanent email address."
return message
def validate_publicname( trans, publicname, user=None ):
diff --git a/lib/galaxy/webapps/galaxy/buildapp.py b/lib/galaxy/webapps/galaxy/buildapp.py
index 7cdab12a50d..265eecc8845 100644
--- a/lib/galaxy/webapps/galaxy/buildapp.py
+++ b/lib/galaxy/webapps/galaxy/buildapp.py
@@ -51,6 +51,8 @@ def app_factory( global_conf, **kwargs ):
webapp.add_ui_controllers( 'galaxy.webapps.galaxy.controllers', app )
# Force /history to go to /root/history -- needed since the tests assume this
webapp.add_route( '/history', controller='root', action='history' )
+ # Force /activate to go to the controller
+ webapp.add_route( '/activate', controller='user', action='activate' )
# These two routes handle our simple needs at the moment
webapp.add_route( '/async/:tool_id/:data_id/:data_secret', controller='async', action='index', tool_id=None, data_id=None, data_secret=None )
webapp.add_route( '/:controller/:action', action='index' )
diff --git a/lib/galaxy/webapps/galaxy/controllers/user.py b/lib/galaxy/webapps/galaxy/controllers/user.py
index 22e01aea2cd..3856d22db24 100644
--- a/lib/galaxy/webapps/galaxy/controllers/user.py
+++ b/lib/galaxy/webapps/galaxy/controllers/user.py
@@ -8,6 +8,7 @@ import os
import socket
import string
import random
+import urllib
from galaxy import web
from galaxy import util, model
from galaxy.model.orm import and_
@@ -17,6 +18,8 @@ from galaxy.web import url_for
from galaxy.web.base.controller import BaseUIController, UsesFormDefinitionsMixin
from galaxy.web.form_builder import CheckboxField, build_select_field
from galaxy.web.framework.helpers import time_ago, grids
+from datetime import datetime, timedelta
+from galaxy.util import hash_util
log = logging.getLogger( __name__ )
@@ -147,7 +150,14 @@ class User( BaseUIController, UsesFormDefinitionsMixin ):
if trans.user:
if user_openid.user and user_openid.user.id != trans.user.id:
message = "The OpenID %s is already associated with another Galaxy account, %s. Please disassociate it from that account before attempting to associate it with a new account." % ( display_identifier, user_openid.user.email )
- status = "error"
+ if not trans.user.active and trans.app.config.user_activation_on: # Account activation is ON and the user is INACTIVE.
+ if ( trans.app.config.activation_grace_period != 0 ): # grace period is ON
+ if self.is_outside_grace_period( trans, trans.user.create_time ): # User is outside the grace period. Login is disabled and he will have the activation email resent.
+ message = self.resend_verification_email( trans, trans.user.email )
+ else: # User is within the grace period, let him log in.
+ pass
+ else: # Grace period is off. Login is disabled and user will have the activation email resent.
+ message = self.resend_verification_email( trans, trans.user.email )
elif not user_openid.user or user_openid.user == trans.user:
if openid_provider_obj.id:
user_openid.provider = openid_provider_obj.id
@@ -282,7 +292,7 @@ class User( BaseUIController, UsesFormDefinitionsMixin ):
subscribe_checked = CheckboxField.is_checked( subscribe )
error = ''
if not trans.app.config.allow_user_creation and not trans.user_is_admin():
- error = 'User registration is disabled. Please contact your Galaxy administrator for an account.'
+ error = 'User registration is disabled. Please contact your local Galaxy administrator for an account.'
else:
# Check email and password validity
error = self.__validate( trans, params, email, password, confirm, username )
@@ -465,9 +475,13 @@ class User( BaseUIController, UsesFormDefinitionsMixin ):
openid_providers=trans.app.openid_providers,
form_input_auto_focus=True,
active_view="user" )
+
def __validate_login( self, trans, **kwd ):
+ """
+ Function validates numerous cases that might happen during the login time.
+ """
message = kwd.get( 'message', '' )
- status = kwd.get( 'status', 'done' )
+ status = kwd.get( 'status', 'error' )
email = kwd.get( 'email', '' )
password = kwd.get( 'password', '' )
redirect = kwd.get( 'redirect', trans.request.referer ).strip()
@@ -475,27 +489,69 @@ class User( BaseUIController, UsesFormDefinitionsMixin ):
user = trans.sa_session.query( trans.app.model.User ).filter( trans.app.model.User.table.c.email==email ).first()
if not user:
message = "No such user (please note that login is case sensitive)"
- status = 'error'
elif user.deleted:
- message = "This account has been marked deleted, contact your Galaxy administrator to restore the account."
- status = 'error'
+ message = "This account has been marked deleted, contact your local Galaxy administrator to restore the account."
+ if trans.app.config.admin_email is not None:
+ message += 'Contact: %s' % trans.app.config.admin_email
elif user.external:
message = "This account was created for use with an external authentication method, contact your local Galaxy administrator to activate it."
- status = 'error'
+ if trans.app.config.admin_email is not None:
+ message += 'Contact: %s' % trans.app.config.admin_email
elif not user.check_password( password ):
message = "Invalid password"
- status = 'error'
- else:
- trans.handle_user_login( user )
- if trans.webapp.name == 'galaxy':
- trans.log_event( "User logged in" )
- message = 'You are now logged in as %s.
You can go back to the page you were visiting or go to the home page.' % \
- ( user.email, redirect, url_for( '/' ) )
- if trans.app.config.require_login:
- message += ' Click here to continue to the home page.' % web.url_for( controller="root", action="welcome" )
- success = True
+ elif trans.app.config.user_activation_on and not user.active: # activation is ON and the user is INACTIVE
+ if ( trans.app.config.activation_grace_period != 0 ): # grace period is ON
+ if self.is_outside_grace_period( trans, user.create_time ): # User is outside the grace period. Login is disabled and he will have the activation email resent.
+ message = self.resend_verification_email( trans, email )
+ else: # User is within the grace period, let him log in.
+ message, success, status = self.proceed_login( trans, user, redirect )
+ else: # Grace period is off. Login is disabled and user will have the activation email resent.
+ message = self.resend_verification_email( trans, email )
+ else: # activation is OFF
+ message, success, status = self.proceed_login( trans, user, redirect )
return ( message, status, user, success )
+ def proceed_login ( self, trans, user, redirect ):
+ """
+ Function processes user login. It is called in case all the login requirements are valid.
+ """
+ trans.handle_user_login( user )
+ if trans.webapp.name == 'galaxy':
+ trans.log_event( "User logged in" )
+ message = 'You are now logged in as %s.
You can go back to the page you were visiting or go to the home page.' % \
+ ( user.email, redirect, url_for( '/' ) )
+ if trans.app.config.require_login:
+ message += ' Click here to continue to the home page.' % web.url_for( controller="root", action="welcome" )
+ success = True
+ status = 'done'
+ return message, success, status
+
+ def resend_verification_email( self, trans, email ):
+ """
+ Function resends the verification email in case user wants to log in with an inactive account.
+ """
+ is_activation_sent = self.send_verification_email( trans, email )
+ if is_activation_sent:
+ message = 'This account has not been activated yet. The activation link has been sent again. Please check your email address %s.
' % email
+ else:
+ message = 'This account has not been activated yet but we are unable to send the activation link. Please contact your local Galaxy administrator.'
+ if trans.app.config.admin_email is not None:
+ message += 'Contact: %s' % trans.app.config.admin_email
+ return message
+
+ def is_outside_grace_period ( self, trans, create_time ):
+ """
+ Function checks whether the user is outside the config-defined grace period for inactive accounts.
+ """
+ # Activation is forced and the user is not active yet. Check the grace period.
+ activation_grace_period = trans.app.config.activation_grace_period
+ # Default value is 3 hours.
+ if activation_grace_period == None:
+ activation_grace_period = 3
+ delta = timedelta( hours = int( activation_grace_period ) )
+ time_difference = datetime.utcnow() - create_time
+ return ( time_difference > delta or activation_grace_period == 0 )
+
@web.expose
def logout( self, trans, logout_all=False ):
if trans.webapp.name == 'galaxy':
@@ -533,7 +589,9 @@ class User( BaseUIController, UsesFormDefinitionsMixin ):
redirect = kwd.get( 'redirect', trans.request.referer ).strip()
is_admin = cntrller == 'admin' and trans.user_is_admin
if not trans.app.config.allow_user_creation and not trans.user_is_admin():
- message = 'User registration is disabled. Please contact your Galaxy administrator for an account.'
+ message = 'User registration is disabled. Please contact your local Galaxy administrator for an account.'
+ if trans.app.config.admin_email is not None:
+ message += 'Contact: %s' % trans.app.config.admin_email
status = 'error'
else:
if not refresh_frames:
@@ -606,6 +664,8 @@ class User( BaseUIController, UsesFormDefinitionsMixin ):
user = trans.app.model.User( email=email )
user.set_password_cleartext( password )
user.username = username
+ if trans.app.config.user_activation_on: # Do not set the active flag in case activation is OFF.
+ user.active = False
trans.sa_session.add( user )
trans.sa_session.flush()
trans.app.security_agent.create_private_user_role( user )
@@ -633,7 +693,7 @@ class User( BaseUIController, UsesFormDefinitionsMixin ):
if subscribe_checked:
# subscribe user to email list
if trans.app.config.smtp_server is None:
- error = "Now logged in as " + user.email + ". However, subscribing to the mailing list has failed because mail is not configured for this Galaxy instance."
+ error = "Now logged in as " + user.email + ". However, subscribing to the mailing list has failed because mail is not configured for this Galaxy instance.
Please contact your local Galaxy administrator."
else:
body = 'Join Mailing list.\n'
to = trans.app.config.mailing_join_addr
@@ -659,9 +719,86 @@ class User( BaseUIController, UsesFormDefinitionsMixin ):
status = 'error'
success = False
else:
- message = 'Now logged in as %s.
Return to the home page.' % ( user.email, url_for( '/' ) )
- success = True
+ is_activation_sent = self.send_verification_email( trans, email )
+ if is_activation_sent:
+ message = 'Now logged in as %s.
Verification email has been sent to your email address. Please verify it by clicking the activation link in the email.
Return to the home page.' % ( user.email, url_for( '/' ) )
+ success = True
+ else:
+ message = 'Unable to send activation email, please contact your local Galaxy administrator.'
+ if trans.app.config.admin_email is not None:
+ message += 'Contact: %s' % trans.app.config.admin_email
+ success = False
return ( message, status, user, success )
+
+ def send_verification_email( self, trans, email ):
+ """
+ Send the verification email containing the activation link to the user's email.
+ """
+ activation_link = self.prepare_activation_link( trans, email )
+
+ body = ("Hi %s,\n\n"
+ "Please click the activation link below in order to activate your account.\n\n"
+ "Activation link: %s \n\n"
+ "Your Galaxy Team" % ( email, activation_link ))
+ to = email
+ frm = trans.app.config.admin_email
+ subject = 'How to activate your Galaxy account'
+ try:
+ util.send_mail( frm, to, subject, body, trans.app.config )
+ return True
+ except:
+ return False
+
+ def prepare_activation_link( self, trans, email ):
+ """
+ Prepares the account activation link for the user.
+ """
+ activation_token = self.get_activation_token( trans, email )
+ host = trans.request.host.split( ':' )[ 0 ]
+ if host == 'localhost':
+ host = socket.getfqdn()
+ activation_link = str( trans.request.host ) + url_for( controller='user', action='activate' ) + "?activation_token=" + str( activation_token ) + "&email=" + urllib.quote( email )
+ return activation_link
+
+ def get_activation_token ( self, trans, email ):
+ """
+ Checks for the activation token. Creates new activation token and stores it in the database if none found.
+ """
+ user = trans.sa_session.query( trans.app.model.User ).filter( trans.app.model.User.table.c.email == email ).first()
+ activation_token = user.activation_token
+ if activation_token == None:
+ activation_token = hash_util.new_secure_hash( str( random.getrandbits( 256 ) ) )
+ user.activation_token = activation_token
+ trans.sa_session.add( user )
+ trans.sa_session.flush()
+ return activation_token
+
+ @web.expose
+ def activate( self, trans, **kwd ):
+ """
+ Function checks whether token fits the user and then activates the user's account.
+ """
+ params = util.Params( kwd, sanitize=False )
+ email = urllib.unquote( params.get( 'email', None ) )
+ activation_token = params.get( 'activation_token', None )
+
+ if email == None or activation_token == None:
+ # We don't have the email or activation_token, show error.
+ return trans.show_error_message( "You are using wrong activation link. Try to log-in and we will send you a new activation email.
Go to login page." ) % web.url_for( controller="root", action="index" )
+ else:
+ # Find the user
+ user = trans.sa_session.query( trans.app.model.User ).filter( trans.app.model.User.table.c.email==email ).first()
+ if user.activation_token == activation_token:
+ user.activation_token = None
+ user.active = True
+ trans.sa_session.add(user)
+ trans.sa_session.flush()
+ return trans.show_ok_message( "Your account has been successfully activated!
Go to login page." ) % web.url_for( controller='root', action='index' )
+ else:
+ # Tokens don't match. Activation is denied.
+ return trans.show_error_message( "You are using wrong activation link. Try to log in and we will send you a new activation email.
Go to login page." ) % web.url_for( controller='root', action='index' )
+ return
+
def __get_user_type_form_definition( self, trans, user=None, **kwd ):
params = util.Params( kwd )
if user and user.values:
@@ -885,7 +1022,7 @@ class User( BaseUIController, UsesFormDefinitionsMixin ):
@web.expose
def reset_password( self, trans, email=None, **kwd ):
if trans.app.config.smtp_server is None:
- return trans.show_error_message( "Mail is not configured for this Galaxy instance. Please contact an administrator." )
+ return trans.show_error_message( "Mail is not configured for this Galaxy instance. Please contact your local Galaxy administrator." )
message = util.restore_text( kwd.get( 'message', '' ) )
status = 'done'
if kwd.get( 'reset_password_button', False ):
@@ -1042,7 +1179,7 @@ class User( BaseUIController, UsesFormDefinitionsMixin ):
phone = util.restore_text( params.get( 'phone', '' ) )
ok = True
if not trans.app.config.allow_user_creation and not is_admin:
- return trans.show_error_message( 'User registration is disabled. Please contact your Galaxy administrator for an account.' )
+ return trans.show_error_message( 'User registration is disabled. Please contact your local Galaxy administrator for an account.' )
if params.get( 'new_address_button', False ):
if not short_desc:
ok = False
diff --git a/static/style/blue/base.css b/static/style/blue/base.css
index 518608b656d..9cf7407bdfd 100644
--- a/static/style/blue/base.css
+++ b/static/style/blue/base.css
@@ -1140,6 +1140,7 @@ ul.icons{list-style-type:none;text-indent:-0.75em}ul.icons li [class^="fa-icon-"
body{background:#fff;color:#000;margin:10px}body.full-content{overflow:hidden;margin:0;padding:0;width:100%;height:100%}
#background{position:absolute;background:#fff;z-index:-1;top:0;left:0;margin:0;padding:0;width:100%;height:100%}
#messagebox{position:absolute;top:34px;left:0;width:100%;height:30px !important;overflow:hidden;border-bottom:solid #999 1px;font-size:90%}
+#inactivebox{position:absolute;top:34px;left:0;width:100%;height:30px !important;overflow:hidden;border-bottom:solid #999 1px;font-size:90%}
#left,#left-border,#center,#right-border,#right{position:absolute;top:34px;bottom:0px;overflow:hidden;background:#fff}
#left{left:0px;width:250px;z-index:200;border-right:solid #999 1px}
#left-border{left:250px}
diff --git a/templates/base/base_panels.mako b/templates/base/base_panels.mako
index 179ead168bf..e28dc80afd8 100644
--- a/templates/base/base_panels.mako
+++ b/templates/base/base_panels.mako
@@ -4,6 +4,9 @@
self.has_left_panel = hasattr( self, 'left_panel' )
self.has_right_panel = hasattr( self, 'right_panel' )
self.message_box_visible = app.config.message_box_visible
+ self.show_inactivity_warning = False
+ if trans.user:
+ self.show_inactivity_warning = ( ( trans.user.active is False ) and ( app.config.user_activation_on is True) and ( app.config.inactivity_box_content is not None ) )
self.overlay_visible=False
self.active_view=None
self.body_class=""
@@ -26,12 +29,20 @@
right: 0 !important;
%endif
}
- %if self.message_box_visible:
+## This is some dirty hack happening
+ %if self.message_box_visible or self.show_inactivity_warning:
#left, #left-border, #center, #right-border, #right
{
top: 64px;
}
%endif
+ %if self.message_box_visible and self.show_inactivity_warning:
+ #left, #left-border, #center, #right-border, #right
+ {
+ top: 94px;
+ }
+ #inactivebox{top:64px;}
+ %endif
%def>
@@ -300,11 +311,16 @@