Display Session timeout warning message before Session expires in ASP.NET Core - asp.net-core

I can able to set the session end the below code.
services.AddSession(options => {
options.IdleTimeout = TimeSpan.FromMinutes(2);
});
I need to extend the session after 20 minutes and if show the session time out warning message to the user and so the user can extend their time out from the application UI.

You already have session timeout code in your question. By the way, default value is 20 minutes. If you want more information, you can read more at Configuring Session.
As far as, I know ASP.NET doesn't have a build-in mechanism to display session expire notification message. So, we have to write our own.
Here is mine, and here is the usage. You are feel free to use it. Since I use Kendo UI, I use Kendo UI Window for the dialog. You could replace it with jQuery UI, if you do not want to use Kendo UI.
_SessionExpireNotification.cshtml
I keep the setting inside appsettings.json. You could hard coded them in this file.
#using Asp.Core
#using Microsoft.Extensions.Options
#using Asp.Web.Common
#inject IUserSession UserSession
#inject IOptions<AppSettings> AppSettings
#if (UserSession.IsAuthenticated)
{
#(Html.Kendo().Window()
.Name("SessionExpireNotification")
.Title("Need More Time?")
.Modal(true)
.Content(#<text>
<p>
Your session is about to expire. You will be automatically signed out in
</p>
<h2 style="margin-top: 0">
<span id="logout-counter-span">0#(AppSettings.Value.CookieAuthentication.SessionExpireNotificationMinutes):00</span>
</h2>
<p>
To continue your session, select <strong>Stay Signed In</strong>.
</p>
<p>
<button id="stay-logged-in-button" type="button" class="btn btn-primary">
Stay Signed In
</button>
<button id="signout-button" type="button" class="btn btn-default">
Sign out
</button>
</p>
</text>)
.Width(450)
.Visible(false)
.Events(ev => ev.Close("onSessionExpireNotificationClose"))
)
<script>
var notificationInterval,
logoutInterval,
logoutCounterSpan;
function startNotificationCounter() {
var counter = #AppSettings.Value.CookieAuthentication.ExpireMinutes;
notificationInterval = setInterval(function() {
counter--;
if (counter === #AppSettings.Value.CookieAuthentication.SessionExpireNotificationMinutes) {
$("#SessionExpireNotification").data("kendoWindow").center().open();
startLogoutCounter();
}
},
60000);
}
function startLogoutCounter() {
var counter = #(AppSettings.Value.CookieAuthentication.SessionExpireNotificationMinutes*60);
logoutInterval = setInterval(function() {
counter--;
if (counter < 0) {
$("#logoutForm").submit();
} else {
var m = Math.floor(counter / 60);
var s = Math.floor(counter % 60);
var mDisplay = m < 10 ? "0" + m : m;
var sDisplay = s < 10 ? "0" + s : s;
logoutCounterSpan.text(mDisplay + ":" + sDisplay);
}
},
1000);
}
function resetCounters() {
clearInterval(notificationInterval);
clearInterval(logoutInterval);
logoutCounterSpan.text("0#(AppSettings.Value.CookieAuthentication.SessionExpireNotificationMinutes):00");
startNotificationCounter();
}
function onSessionExpireNotificationClose() {
resetCounters();
}
$(function() {
logoutCounterSpan = $("#logout-counter-span");
startNotificationCounter();
$("#stay-logged-in-button").click(function() {
$.get("#Url.Action("Index", "KeepAlive", new {area = ""})",
null,
function(data) {
resetCounters();
$("#SessionExpireNotification").data("kendoWindow").center().close();
}
);
});
$("#signout-button").click(function() {
$("#logoutForm").submit();
});
});
</script>
}
Extending the session timeout is easy. You just call a dummy action method.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Asp.Web.Controllers
{
[AllowAnonymous]
public class KeepAliveController : Controller
{
//
// GET: /KeepAlive
[AllowAnonymous]
public ActionResult Index()
{
return Content("I am alive!");
}
}
}

Related

How to show page before executing OnGet?

I have a razor page that shows a 'Please wait' box and the OnGet method does some stuff that might take a few seconds and ends with a LocalRedirect.
The razor code:
#page
#inject IStringLocalizer<Startup> localizer
<div class="login-page">
<div class="login-box">
<div class="card">
<div class="card-body login-card-body">
<div class="help-block text-center">
<div class="spinner-border" role="status" />
</div>
<p class="login-box-msg">#localizer["PleaseWait"]</p>
</div>
</div>
</div>
</div>
And the code-behind:
public async Task<IActionResult> OnGet()
{
//Do some stuff that takes a few seconds...
return LocalRedirect("/Dashboard");
}
Everything is working apart from the page first being shown and then executing the code.
Is it possible to first render the page (so that the users can see that something is happening) and then execute the code in the OnGet?
Move your long async routines from OnGet to a number of named handler methods. Allow the page to render a "please wait" message and use client-side code (jQuery AJAX or plain Fetch) to call the named handlers. Keep track of when they complete and when all have completed, redirect to the other page.
Here's an example where the OnGet simply renders the page (can include a "please wait" message"), and a number of named handlers simulate routines of varying length:
public void OnGet()
{
}
public async Task OnGetTwoSeconds()
{
await Task.Delay(2000);
}
public async Task OnGetThreeSeconds()
{
await Task.Delay(3000);
}
public async Task OnGetFiveSeconds()
{
await Task.Delay(5000);
}
The following script goes in the Razor page itself. It consists of three variables for tracking task completion and a method that redirects to the home page when all three have completed. Each of the named handlers is called by the code and sets its tracking variable to true on completion as well as calling the redirect function:
#section scripts{
<script>
let twosecondsdon = false;
let threesecondsdone = false;
let fivesecondsdone = false;
function redirect(){
if (twosecondsdone && threesecondsdone && fivesecondsdone) {
location.href = '/';
}
}
fetch('?handler=TwoSeconds').then(() => {
twosecondsdone = true;
redirect();
})
fetch('?handler=ThreeSeconds').then(() => {
threesecondsdone = true;
redirect();
})
fetch('?handler=FiveSeconds').then(()=>{
fivesecondsdone = true;
redirect();
})
</script>
}
When all three complete, the redirect function does its thing.
Is it possible to first render the page (so that the users can see
that something is happening) and then execute the code in the OnGet?
Yes you can do that. Currently, I am showing you the delay counter where you can replace your page. Please follow the steps below:
HTML:
Script:
#section scripts {
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.2.1.min.js"></script>
<script>
$(document).ready(function () {
var counter = 5;
(function countDown() {
if (counter-- > 0) {
$('#timer').text("Please wait... we are redirecting you to register page..." + counter + ' s');
setTimeout(countDown, 1000);
} else {
window.location.href = "https://localhost:44361/userlog/ViewCalculateAge";// Here put your controller URL where you would like to redirect
}
})();
});
</script>
}
Output:

Prestashop: display "Choose language" inline in admin

I wanna simplify my life and display language flags inline next to input fields in admin panel.
Example:
Turn this:
into this:
I tried override
abstract class ModuleCore { public function displayFlags() }
but no effect.
Then I modify admin\themes\default\template\helpers\options\options.tpl to:
<div class="displayed_flag">
{foreach $languages as $language}
<img src="../img/l/{$language.id_lang}.jpg"
class="pointer"
alt="{$language.name}"
title="{$language.name}"
onclick="changeLanguage('{$key}', '{if isset($custom_key)}{$custom_key}{else}{$key}{/if}', {$language.id_lang}, '{$language.iso_code}');" />
{/foreach}
</div>
But still nothing.
Of course I deleted class_index.php, clear cache etc...
I am using Prestashop 1.5.5 and default theme.
You're searching for the displayFlags function inside /js/admin.js file.
Here it works on my installation with this changes:
function displayFlags(languages, defaultLanguageID, employee_cookie)
{
if ($('.translatable'))
{
$('.translatable').each(function() {
if (!$(this).find('.displayed_flag').length > 0) {
$.each(languages, function(key, language) {
if (language['id_lang'] == defaultLanguageID)
{
defaultLanguage = language;
return false;
}
});
var displayFlags = $('<div></div>')
.addClass('displayed_flag');
$.each(languages, function(key, language) {
var img = $('<img>')
.addClass('pointer')
.css('margin', '0 2px')
.attr('src', '../img/l/' + language['id_lang'] + '.jpg')
.attr('alt', language['name'])
.click(function() {
changeFormLanguage(language['id_lang'], language['iso_code'], employee_cookie);
});
displayFlags.append(img);
});
if ($(this).find('p:last-child').hasClass('clear'))
$(this).find('p:last-child').before(displayFlags);
else
$(this).append(displayFlags);
}
});
}
}

AutoComplete Textbox with database

I wanna do autocomplete when i enter a letter.
I have a database "USERS" and it has name .When i try texted for example e
it must show "edgar,edwin,emir" but ,t shows nothing.
ClientController here:
public class ClientController : Controller
{
public JsonResult AutocompleteSuggestions(string searchstring)
{
ModelContext db = new ModelContext();
var suggestions = from E in db.USERS
select E.Name;
var namelist = suggestions.Where(n => n.ToLower().Contains(searchstring.ToLower()));
return Json(namelist, JsonRequestBehavior.AllowGet);
}
}
index.cshtml here:in here there is a textbox and i send client controller autocopleteSuggeston method but it doesnt go or it doesnt work.I add jquery script file on cshtml but it still not working.
#using (Html.BeginForm())
{
<p>
Name: #Html.TextBox("SearchString")
<input type="submit" value="Search" />
</p>
}
<script type="text/javascript">
window.jQuery(function () {
window.jQuery("#SearchString").autocomplete({
source: "/Client/AutocompleteSuggestions",
minLength: 1,
select: function (event, ui) {
if (ui.item) {
window.jQuery("#SearchString").val(ui.item.value);
window.jQuery("form").submit();
}
}
});
});
</script>
i add jquery
Where is the mistake?
you need to add [HttpPost] before JsonResult method like this:
[HttpPost]
public JsonResult AutocompleteSuggestions(string searchstring)
{
ModelContext db = new ModelContext();
var suggestions = from E in db.USERS
select E.Name;
var namelist = suggestions.Where(n => n.ToLower().Contains(searchstring.ToLower()));
return Json(namelist, JsonRequestBehavior.AllowGet);
}
as the form here is submitted using window.jQuery("form").submit(),it invokes a Post Action, so you need to add [HttpPost] for capturing the form submissions or any kind of Post Action!
Change your View Code to
#using( Html.BeginForm(null, null, FormMethod.Post, new{#id ="SearchForm"} ))
{
<p>
Name: #Html.TextBox("SearchString")
<input type="submit" value="Search" />
</p>
}
$(function() {
$("#SearchString").autocomplete({
source: "/Client/AutocompleteSuggestions",
select: function(event, ui) {
$("#SearchString").val(ui.item.value);
$("#SearchForm").submit();
}
});
});

Can't get multiple viewmodels under master viewmodel work in Durandal.JS

I am working on an application which has DurandalJS 2.0.1. I have written a viewmodel, basically I want to implement a master viewmodel that has multiple viewmodels (e.g. account viewmodel has register and login sub viewmodels)
define(['knockout'],function (ko) {
var register = function(){
var self = this;
self.Welcome = "Register";
self.Username = ko.observable();
self.Password = ko.observable();
}
var login= function(){
var self = this;
self.Welcome = "Login";
self.Username = ko.observable();
self.Password = ko.observable();
}
var account = {
testVariable : "Hello Cruel World!",
register : register,
login : login
};
return account;
});
View is :
<h2 data-bind="text: testVariable"></h2>
<h2 data-bind = "text: register().Welcome"></h2>
<h2 data-bind = "text: login().Welcome"></h2>
testVariable is being displayed correctly but I can't make register().Welcome, login().Welcome or tried register.Welcome or login.Welcome working.
Any idea how can I get it working?
Here's how you can tackle such situations... #robert.westerlund is right, if that is function then you've to create new object of each function before proceeding..
define(['plugins/http', 'durandal/app', 'jquery', 'knockout'], function (http, app, $, ko) {
var loginVM = {
Welcome : "Hello Login",
Username : ko.observable(),
Password : ko.observable(),
rememberMe : ko.observable(false),
Login : function (obj, event) {
//Do the login stuff
}
};
var registerVM = {
Welcome : "Hello Register",
Username : ko.observable(),
Password : ko.observable(),
ConfirmPassword : ko.observable(),
Register : function (obj, event) {
//Do the register stuff
}
};
return {
login : loginVM,
register: registerVM
};
});
VIEW
<div data-bind="with:login">
<h2 data-bind = "text: Welcome"></h2>
</div>
<div data-bind="with:register">
<h2 data-bind = "text: Welcome"></h2>
</div>
Instead of writing functions, I wrote object literals. That worked pretty well :-)

Pass ViewModel back to controller

I have a working application integrating Bootstrap and Knockout. This app pulls data from my controller and displays this in the UI as I would expect. I can see values are updated when I click or change a value but I can't seem to see that data passed back to my controller for the purposes of saving it. All I need to know is how to fix what I have to allow me to pass the selectedRequestorName back to the controller.
Here is a sample class
public class Requestor
{
public int Id { get; set; }
public string Name { get; set; }
}
Interface
interface IRequestorRepository
{
IList<Requestor> GetAllRequestors();
}
Here is the repository with the seed data
public class RequestorRepository : IRequestorRepository
{
private List<Requestor> requestors = new List<Requestor>();
private int _nextId = 1;
public RequestorRepository()
{
Add(new Requestor{ Id = 1, Name = "Brian" });
Add(new Requestor { Id = 2, Name = "Steve" });
Add(new Requestor { Id = 3, Name = "Jake" });
}
public IList<Requestor> GetAllRequestors()
{
return requestors;
}
public Requestor Add(Requestor item)
{
if (item == null)
{
throw new ArgumentNullException("Null Requestor");
}
item.Id = _nextId++;
requestors.Add(item);
return item;
}
}
My HomeController looks like the following
public class HomeController : Controller
{
static readonly IRequestorRepository req_repository = new RequestorRepository();
// GET: /Home/
public ActionResult Index()
{
ViewBag.DateNow = DateTime.Now.ToShortDateString();
return View();
}
public JsonResult GetRequestors()
{
return Json(req_repository.GetAllRequestors(), JsonRequestBehavior.AllowGet);
}
[HttpPost]
public JsonResult SaveDetails(Requestor selectedRequestorName)
{
int id = -1;
return Json(id, "json");
}
}
In my Index.cshtml I have the following in a script tag at the top of the page
// Global variable
var viewModel = null;
$(document).ready(function () {
function ViewModel() {
//Make the self as 'this' reference
var self = this;
// Requestors
self.RequestorId = ko.observable("");
self.RequestorName = ko.observable("");
self.RequestorSourceDatabase = ko.observable("");
var RequestorNames = {
Id: self.RequestorId,
Name: self.RequestorName,
SourceDatabase: self.RequestorSourceDatabase
};
self.selectedRequestorName = ko.observable();
self.RequestorNames = ko.observableArray(); // Contains the list of RequestorNames
// Initialize the view-model for Requestors
$.ajax({
url: '#Url.Action("GetRequestors", "Home")',
cache: false,
type: 'GET',
contentType: 'application/json; charset=utf-8',
data: {},
success: function (data) {
self.RequestorNames(data);
}
});
// END Requestors
// Reset
self.reset = function () {
self.Name("");
}
// Cancel
self.cancel = function () {
self.Name(null);
}
}
viewModel = new ViewModel();
ko.applyBindings(viewModel);
});
$(function () {
$('#Save').click(function (e) {
// Check whether the form is valid. Note: Remove this check, if you are not using HTML5
if (document.forms[0].checkValidity()) {
e.preventDefault();
$.ajax({
type: "POST",
url: '#Url.Action("SaveDetails", "Home")',
data: ko.toJSON(viewModel.selectedRequestorName),
contentType: 'application/json; charset=utf-8',
async: true,
beforeSend: function () {
// Display loading image
},
success: function (result) {
if (result > 0) {
alert("This work request has been successfully saved in database. The Document ID is: " + result);
} else {
alert("The Work Request was not saved, there was an issue.");
}
},
complete: function () {
// Hide loading image.
},
error: function (jqXHR, textStatus, errorThrown) {
// Handle error.
}
});
}
else {
alert("Form is not valid");
}
});
});
And finally the control that contains the displayed data for the user to select from...
<p>Current selection is <span data-bind="text:selectedRequestorName"></span></p>
<!-- Requestors -->
<div class="input-group col-sm-8">
<input type="text" data-bind="value:selectedRequestorName" class="form-control item" placeholder="Requestor Name" name="Requestor">
<div class="input-group-btn">
<button type="button" class="btn btn-default dropdown-toggle item" data-toggle="dropdown">Select <span class="caret"></span></button>
<ul class="dropdown-menu" data-bind="foreach: RequestorNames">
<li class="dropdown">
</li>
</ul>
</div>
</div>
<div>
<button id="Save" type="submit" class="btn btn-default btn-success">Create</button>
</div>
create an action method on your controller that accepts an selectedRequestorName (string?) as argument.
create a function in your knockout viewmodel that reads the selectedRequestorName from the ko vm, JsonStringify it and pass it back to the above action method via ajax.
[HttpPost]
public JsonResult SaveDetails(String selectedRequestorName)
{
int id = -1;
return Json(id, "json");
}
change type of selectedRequestorName to String from Requestor as above that should work.
note not tested.but let me know if it helps.
Within $('#Save').click(), would you please change the line
data: ko.toJSON(viewModel.selectedRequestorName)
with
data: ko.toJSON(viewModel.selectedRequestorName())
hope, this will help.