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'))}
%def>
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')