mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-01-30 19:48:18 +08:00
Session end implementation [SCI-7027]
This commit is contained in:
parent
9478779218
commit
ac7c873385
9 changed files with 216 additions and 0 deletions
68
app/assets/javascripts/session_end.js
Normal file
68
app/assets/javascripts/session_end.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
/* globals I18n */
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var originalTitle = '';
|
||||
var expireIn;
|
||||
var expireLimit = 900; // 15min
|
||||
var timeoutID;
|
||||
var expirationUrl = $('meta[name=\'expiration-url\']').attr('content');
|
||||
|
||||
var pad = function(i) {
|
||||
var s = ('0' + Math.floor(i));
|
||||
return s.substring(s.length - 2);
|
||||
};
|
||||
|
||||
var newTimerStr = function(expirationTime) {
|
||||
var m = (expirationTime / 60) % 60;
|
||||
var s = (expirationTime % 60);
|
||||
return [m, s].map(pad).join(':');
|
||||
};
|
||||
|
||||
function getSessionEnd() {
|
||||
if (expirationUrl) {
|
||||
$.get(expirationUrl, function(data) {
|
||||
if (data <= 0) {
|
||||
$('#session-finished').modal();
|
||||
} else if (data <= expireLimit + 1) {
|
||||
expireIn = data;
|
||||
originalTitle = document.title;
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
timeoutID = setTimeout(expirationInTime, 1000);
|
||||
} else {
|
||||
timeoutID = setTimeout(getSessionEnd, (data - expireLimit) * 1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function expirationInTime() {
|
||||
if (expireIn > 0) {
|
||||
document.title = newTimerStr(expireIn) + ' ' + originalTitle;
|
||||
$('.expiring').text(I18n.t('devise.sessions.expire_modal.session_end_in.header',
|
||||
{ time: newTimerStr(expireIn) }));
|
||||
expireIn -= 1;
|
||||
if (!$('#session-expire').hasClass('in')) {
|
||||
$('#session-expire').modal().off('hide.bs.modal').on('hide.bs.modal', function() {
|
||||
if (expireIn > 0) {
|
||||
$.post($('meta[name=\'revive-url\']').attr('content'));
|
||||
document.title = originalTitle;
|
||||
clearTimeout(timeoutID);
|
||||
timeoutID = setTimeout(getSessionEnd, 1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
timeoutID = setTimeout(expirationInTime, 1000);
|
||||
} else {
|
||||
document.title = originalTitle;
|
||||
$('#session-expire').modal('hide');
|
||||
$('#session-finished').modal();
|
||||
}
|
||||
}
|
||||
timeoutID = setTimeout(getSessionEnd, 1000);
|
||||
|
||||
$(document).on('click', '.session-login', function() {
|
||||
window.location.reload();
|
||||
});
|
||||
}());
|
39
app/assets/stylesheets/session_expired.scss
Normal file
39
app/assets/stylesheets/session_expired.scss
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
// scss-lint:disable NestingDepth ImportantRule
|
||||
|
||||
.session-modal {
|
||||
.modal-body {
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding-left: 0;
|
||||
|
||||
li {
|
||||
padding: .25em 0;
|
||||
}
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.instruction-session-collapse {
|
||||
cursor: pointer;
|
||||
margin-bottom: .5em;
|
||||
margin-top: 20px;
|
||||
|
||||
.fa-angle-up {
|
||||
margin-left: .5em;
|
||||
}
|
||||
|
||||
&.collapsed {
|
||||
|
||||
.fa-angle-up {
|
||||
@include rotate(-180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -4,6 +4,7 @@ class Users::SessionsController < Devise::SessionsController
|
|||
layout :session_layout
|
||||
after_action :after_sign_in, only: %i(create authenticate_with_two_factor)
|
||||
before_action :remove_authenticate_mesasge_if_root_path, only: :new
|
||||
prepend_before_action :skip_timeout, only: :expire_in
|
||||
|
||||
rescue_from ActionController::InvalidAuthenticityToken do
|
||||
redirect_to new_user_session_path
|
||||
|
@ -34,6 +35,16 @@ class Users::SessionsController < Devise::SessionsController
|
|||
generate_templates_project
|
||||
end
|
||||
|
||||
def expire_in
|
||||
if current_user.remember_created_at.nil?
|
||||
render plain: Devise.timeout_in.to_i - (Time.now.to_i - user_session['last_request_at']).round
|
||||
else
|
||||
render plain: Devise.remember_for - (Time.now.to_i - current_user.remember_created_at.to_i).round
|
||||
end
|
||||
end
|
||||
|
||||
def revive_session; end
|
||||
|
||||
def two_factor_recovery
|
||||
unless session[:otp_user_id]
|
||||
redirect_to new_user_session_path
|
||||
|
@ -92,6 +103,10 @@ class Users::SessionsController < Devise::SessionsController
|
|||
|
||||
private
|
||||
|
||||
def skip_timeout
|
||||
request.env['devise.skip_trackable'] = true
|
||||
end
|
||||
|
||||
def remove_authenticate_mesasge_if_root_path
|
||||
if session[:user_return_to] == root_path && flash[:alert] == I18n.t('devise.failure.unauthenticated')
|
||||
flash[:alert] = nil
|
||||
|
|
|
@ -7,9 +7,14 @@
|
|||
<meta name="max-file-size" content="<%= Rails.configuration.x.file_max_size_mb %>">
|
||||
<meta name="tiny-mce-assets-url" content="<%= tiny_mce_assets_path %>">
|
||||
<meta name="highlightjs-url" content="<%= asset_path('highlightjs-github-theme.css') %>">
|
||||
<% if user_signed_in? %>
|
||||
<meta name="expiration-url" content="<%= users_expire_in_path %>">
|
||||
<meta name="revive-url" content="<%= users_revive_session_path %>">
|
||||
<% end %>
|
||||
<%= stylesheet_link_tag 'application', media: 'all' %>
|
||||
<%= javascript_include_tag 'application' %>
|
||||
<%= javascript_pack_tag 'application' %>
|
||||
<%= javascript_include_tag 'session_end' %>
|
||||
|
||||
|
||||
|
||||
|
@ -57,6 +62,8 @@
|
|||
<%= render "shared/file_edit_modal.html.erb" %>
|
||||
<%= render "shared/marvinjs_modal.html.erb" %>
|
||||
<%= render "shared/comments/comments_sidebar.html.erb" %>
|
||||
<%= render "users/sessions/session_expire_modal.html.erb" %>
|
||||
<%= render "users/sessions/session_end_modal.html.erb" %>
|
||||
<% end %>
|
||||
|
||||
<% if user_signed_in? && flash[:system_notification_modal] && current_user.show_login_system_notification? %>
|
||||
|
|
39
app/views/users/sessions/_session_end_modal.html.erb
Normal file
39
app/views/users/sessions/_session_end_modal.html.erb
Normal file
|
@ -0,0 +1,39 @@
|
|||
<div class="modal session-modal" id="session-finished" tabindex="-1" role="dialog"
|
||||
aria-labelledby="session_expire" data-role="session_expire-modal">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title"><%= t('devise.sessions.expire_modal.session_ended.header' ) %></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<ul>
|
||||
<li><%= t('devise.sessions.expire_modal.session_ended.description') %></li>
|
||||
<li>
|
||||
<a class="collapse-row">
|
||||
<div class="row-title">
|
||||
<div class="instruction-session-collapse collapsed" data-toggle="collapse" href="#InstructionsSectionFinished" aria-expanded="false">
|
||||
<%= t('devise.sessions.expire_modal.why_see_this') %>
|
||||
<i class="fas fa-angle-up"></i>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<ul class="collapse" id="InstructionsSectionFinished">
|
||||
<li><%= t('devise.sessions.expire_modal.session_end_in.paragraph1') %></li>
|
||||
<li><%= t('devise.sessions.expire_modal.session_end_in.paragraph2') %></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">
|
||||
<%= t('general.close' ) %>
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary session-login">
|
||||
<%= t('devise.sessions.new.submit' ) %>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
34
app/views/users/sessions/_session_expire_modal.html.erb
Normal file
34
app/views/users/sessions/_session_expire_modal.html.erb
Normal file
|
@ -0,0 +1,34 @@
|
|||
<div class="modal session-modal" id="session-expire" tabindex="-1" role="dialog"
|
||||
aria-labelledby="session-expire" data-role="session-expire-modal">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title expiring"></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<ul>
|
||||
<li><%= t('devise.sessions.expire_modal.session_end_in.description') %></li>
|
||||
<li>
|
||||
<a class="collapse-row">
|
||||
<div class="row-title">
|
||||
<div class="instruction-session-collapse collapsed" data-toggle="collapse" href="#InstructionsSection" aria-expanded="false">
|
||||
<%= t('devise.sessions.expire_modal.why_see_this') %>
|
||||
<i class="fas fa-angle-up"></i>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<ul class="collapse" id="InstructionsSection">
|
||||
<li><%= t('devise.sessions.expire_modal.session_end_in.paragraph1') %></li>
|
||||
<li><%= t('devise.sessions.expire_modal.session_end_in.paragraph2') %></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" data-dismiss="modal"><%= t('devise.sessions.expire_modal.session_end_in.revive_session' ) %></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -91,6 +91,7 @@ Rails.application.config.assets.precompile += %w(sidebar_toggle.js)
|
|||
Rails.application.config.assets.precompile += %w(reports/reports_datatable.js)
|
||||
Rails.application.config.assets.precompile += %w(reports/save_pdf_to_inventory.js)
|
||||
Rails.application.config.assets.precompile += %w(reports/content.js)
|
||||
Rails.application.config.assets.precompile += %w(session_end.js)
|
||||
|
||||
# Libraries needed for Handsontable formulas
|
||||
Rails.application.config.assets.precompile += %w(jquery.js)
|
||||
|
|
|
@ -49,6 +49,17 @@ en:
|
|||
not_correct_code: "Not correct recovery code"
|
||||
create:
|
||||
team_name: "%{user}'s projects"
|
||||
expire_modal:
|
||||
why_see_this: 'Why am I seeing this?'
|
||||
session_end_in:
|
||||
header: 'Your session will end in %{time}'
|
||||
description: 'To keep the data you entered into the fields without clicking the Save button, please revive your session and save them.'
|
||||
paragraph1: 'To keep your work secure, SciNote times out sessions after 3 hours of inactivity. '
|
||||
paragraph2: 'To proceed working after the session ends, you’ll have to log in again, starting a new working session.'
|
||||
revive_session: 'Revive my session'
|
||||
session_ended:
|
||||
header: 'To proceed working, please log in again'
|
||||
description: 'If you have any unsaved data, please close this message, copy your text, and paste it back after you log in again.'
|
||||
unlocks:
|
||||
new:
|
||||
head_title: "Resend unlock instructions"
|
||||
|
|
|
@ -703,6 +703,8 @@ Rails.application.routes.draw do
|
|||
get 'users/sign_up_provider' => 'users/registrations#new_with_provider'
|
||||
get 'users/two_factor_recovery' => 'users/sessions#two_factor_recovery'
|
||||
get 'users/two_factor_auth' => 'users/sessions#two_factor_auth'
|
||||
get 'users/expire_in' => 'users/sessions#expire_in'
|
||||
post 'users/revive_session' => 'users/sessions#revive_session'
|
||||
post 'users/authenticate_with_two_factor' => 'users/sessions#authenticate_with_two_factor'
|
||||
post 'users/authenticate_with_recovery_code' => 'users/sessions#authenticate_with_recovery_code'
|
||||
post 'users/complete_sign_up_provider' => 'users/registrations#create_with_provider'
|
||||
|
|
Loading…
Reference in a new issue