diff --git a/tailbone/static/js/tailbone.js b/tailbone/static/js/tailbone.js index 8169b7db..6cf6a536 100644 --- a/tailbone/static/js/tailbone.js +++ b/tailbone/static/js/tailbone.js @@ -69,28 +69,69 @@ function get_uuid(obj) { /* - * get_dialog(id, callback) - * - * Returns a
element suitable for use as a jQuery dialog. - * - * ``id`` is used to construct a proper ID for the element and allows the - * dialog to be resused if possible. - * - * ``callback``, if specified, should be a callback function for the dialog. - * This function will be called whenever the dialog has been closed - * "successfully" (i.e. data submitted) by the user, and should accept a single - * ``data`` object which is the JSON response returned by the server. + * reference to existing timeout warning dialog, if any */ +var session_timeout_warning = null; -function get_dialog(id, callback) { - var dialog = $('#'+id+'-dialog'); - if (! dialog.length) { - dialog = $('
'); + +/** + * Warn user of impending session timeout. + */ +function timeout_warning() { + if (! session_timeout_warning) { + session_timeout_warning = $('
' + + 'You will be logged out in ' + + 'seconds...
'); } - if (callback) { - dialog.attr('callback', callback); + session_timeout_warning.find('.seconds').text('60'); + session_timeout_warning.dialog({ + title: "Session Timeout Warning", + modal: true, + buttons: { + "Stay Logged In": function() { + session_timeout_warning.dialog('close'); + $.get(noop_url, set_timeout_warning_timer); + }, + "Logout Now": function() { + location.href = logout_url; + } + } + }); + window.setTimeout(timeout_warning_update, 1000); +} + + +/** + * Decrement the 'seconds' counter for the current timeout warning + */ +function timeout_warning_update() { + if (session_timeout_warning.is(':visible')) { + var span = session_timeout_warning.find('.seconds'); + var seconds = parseInt(span.text()) - 1; + if (seconds) { + span.text(seconds.toString()); + window.setTimeout(timeout_warning_update, 1000); + } else { + location.href = logout_url; + } } - return dialog; +} + + +/** + * Warn user of impending session timeout. + */ +function set_timeout_warning_timer() { + // TODO: are we calculating timer ok here? (effective timeout - 60 seconds) + window.setTimeout(timeout_warning, session_timeout * 1000 - 60000); +} + + +/* + * set initial timer for timeout warning, if applicable + */ +if (session_timeout) { + set_timeout_warning_timer(); } diff --git a/tailbone/subscribers.py b/tailbone/subscribers.py index fd7f0f26..e5016136 100644 --- a/tailbone/subscribers.py +++ b/tailbone/subscribers.py @@ -154,6 +154,13 @@ def context_found(event): return referrer request.get_referrer = get_referrer + def get_session_timeout(): + """ + Returns the timeout in effect for the current session + """ + return request.session.get('_timeout') + request.get_session_timeout = get_session_timeout + def includeme(config): config.add_subscriber(add_rattail_config_attribute_to_request, 'pyramid.events.NewRequest') diff --git a/tailbone/templates/base.mako b/tailbone/templates/base.mako index 7fec90b6..ba316b51 100644 --- a/tailbone/templates/base.mako +++ b/tailbone/templates/base.mako @@ -133,6 +133,11 @@ ${h.javascript_link(request.static_url('tailbone:static/js/lib/jquery.ui.menubar.js'))} ${h.javascript_link(request.static_url('tailbone:static/js/lib/jquery.loadmask.min.js'))} ${h.javascript_link(request.static_url('tailbone:static/js/lib/jquery.ui.timepicker.js'))} + ${h.javascript_link(request.static_url('tailbone:static/js/tailbone.js'))} diff --git a/tailbone/views/auth.py b/tailbone/views/auth.py index 25b0ace5..3a828acc 100644 --- a/tailbone/views/auth.py +++ b/tailbone/views/auth.py @@ -149,6 +149,12 @@ class AuthenticationView(View): def mobile_logout(self): return self.logout(mobile=True) + def noop(self): + """ + View to serve as "no-op" / ping action to reset current user's session timer + """ + return {'status': 'ok'} + def change_password(self): """ Allows a user to change his or her password. @@ -201,6 +207,10 @@ class AuthenticationView(View): config.add_route('mobile.logout', '/mobile/logout') config.add_view(cls, attr='mobile_logout', route_name='mobile.logout') + # no-op + config.add_route('noop', '/noop') + config.add_view(cls, attr='noop', route_name='noop', renderer='json') + # change password config.add_route('change_password', '/change-password') config.add_view(cls, attr='change_password', route_name='change_password', renderer='/change_password.mako')