×
Namespaces

Variants
Actions
(Difference between revisions)

How to implement session-time out in a WP app

From Nokia Developer Wiki
Jump to: navigation, search
chintandave_er (Talk | contribs)
m (Chintandave er - code formatting)
hamishwillee (Talk | contribs)
m (Text replace - "[[Category:Silverlight" to "[[Category:XAML")
(11 intermediate revisions by 4 users not shown)
Line 1: Line 1:
[[Category:Windows Phone]]
+
[[Category:Windows Phone]][[Category:Code Examples]][[Category:General Programming]][[Category:UI]][[Category:XAML]][[Category:Tutorial]]
{{Abstract|This wiki article shows how to implement session time out in a windows phone 7 app using Dispatch Timer }}  
+
{{Abstract|This article shows how to implement session time out in a Windows Phone 7 app using a [http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatchertimer(v=vs.95).aspx DispatcherTimer] }}  
  
 
{{ArticleMetaData <!-- v1.2 -->
 
{{ArticleMetaData <!-- v1.2 -->
|sourcecode= <!-- Link to example source code e.g. [[Media:The Code Example ZIP.zip]] -->
+
|sourcecode= [[File:Session-TimeOut App.zip]]
|installfile= <!-- Link to installation file (e.g. [[Media:The Installation File.sis]]) -->
+
 
|devices= <!-- Devices tested against - e.g. ''devices=Nokia 6131 NFC, Nokia C7-00'') -->
 
|devices= <!-- Devices tested against - e.g. ''devices=Nokia 6131 NFC, Nokia C7-00'') -->
 
|sdk= Windows Phone SDK 7.1
 
|sdk= Windows Phone SDK 7.1
|platform= Windows Phone 7.1 and later
+
|platform= Windows Phone 7.5 and later
 
|devicecompatability= <!-- Compatible devices e.g.: All* (must have internal GPS) -->
 
|devicecompatability= <!-- Compatible devices e.g.: All* (must have internal GPS) -->
|dependencies= <!-- Any other/external dependencies e.g.: Google Maps Api v1.0 -->  
+
|dependencies= <!-- Any other/external dependencies e.g.: Google Maps Api v1.0 -->
|signing=<!-- Signing requirements - empty or one of: Self-Signed, DevCert, Manufacturer -->
+
|signing= <!-- Signing requirements - empty or one of: Self-Signed, DevCert, Manufacturer -->
 
|capabilities= <!-- Capabilities required by the article/code example (e.g. Location, NetworkServices. -->
 
|capabilities= <!-- Capabilities required by the article/code example (e.g. Location, NetworkServices. -->
 
|keywords= <!-- APIs, classes and methods (e.g. QSystemScreenSaver, QList, CBase -->
 
|keywords= <!-- APIs, classes and methods (e.g. QSystemScreenSaver, QList, CBase -->
 
|language= <!-- Language category code for non-English topics - e.g. Lang-Chinese -->
 
|language= <!-- Language category code for non-English topics - e.g. Lang-Chinese -->
 
|translated-by= <!-- [[User:XXXX]] -->
 
|translated-by= <!-- [[User:XXXX]] -->
|translated-from-title= <!-- Title only -->  
+
|translated-from-title= <!-- Title only -->
 
|translated-from-id= <!-- Id of translated revision -->
 
|translated-from-id= <!-- Id of translated revision -->
|review-by=<!-- After re-review: [[User:username]] -->
+
|review-by= <!-- After re-review: [[User:username]] -->
 
|review-timestamp= <!-- After re-review: YYYYMMDD -->
 
|review-timestamp= <!-- After re-review: YYYYMMDD -->
 
|update-by= <!-- After significant update: [[User:username]]-->
 
|update-by= <!-- After significant update: [[User:username]]-->
 
|update-timestamp= <!-- After significant update: YYYYMMDD -->
 
|update-timestamp= <!-- After significant update: YYYYMMDD -->
 
|creationdate= 20120828
 
|creationdate= 20120828
|author= [[User:Vaishali Rawat]]
+
|author= [[User:Vaishali Rawat]]
 
}}
 
}}
  
== Introduction ==  
+
== Introduction ==
Several times we encounter apps where if the user does not refresh or request a page within the time-out period, the session ends. Session time-outs are treated as security "feature". In this app we will implement the session time-out feature.
+
  
== Basic Concept ==
+
Sometimes it is a useful "security feature" close a login (or other) page if the user does not enter the required details within a specified time. This code example demonstrates how to implement a session time-out.
In this app we will use three pages.
+
* login page
+
* registration page
+
* detail page
+
  
Once the app is launched a login page is displayed. User is requested to enter login credentials and incase its a new user he/she can navigate to registeration page.
+
The app has three pages: login, registration and detail. When the app is launched the login page is displayed. The user is asked to enter login credentials or can navigate to the registeration page if they are a new users. If the user does not provide their registration details within a specific time the session ends and the login page is displayed. Upon successful registration user is sent to the detail page.
'''registration page is session bound.''' If the user dosen't provide the credentials in the specific time (session time e.g 20sec) or else doesn't even tap the screen the session ends and user is navigated back to the login page.  
+
Upon successful registration user is navigated to detail page.
+
  
== Creating UI==
+
==Starting a new project==
 
+
* Create a new project in Visual Studio 2010 from '''File > New Project > Windows Phone Application - Visual C#'''. Give a desired name of the project.
==Starting a New Project==
+
* Create a new project in Visual Studio 2010 from File>>New Project>>Windows Phone Application - Visual C#. Give a desired name of the project.
+
 
[[File:NewProject WP.jpg|Creating a new Project in Windows Phone 7 using Visual C#|600px]]
 
[[File:NewProject WP.jpg|Creating a new Project in Windows Phone 7 using Visual C#|600px]]
  
 
==Designing LoginPage==
 
==Designing LoginPage==
In the '''MainPage.xaml''' file, we will drop down a few Text Block & Text Box controls. We will also use few App bar buttons. We will set the desired properties like shown below:
+
In the '''MainPage.xaml''' file, we will drag and drop a TextBlock & TextBox controls. We will also use few App bar buttons. We will set the desired properties like shown below:
 
<code xml>
 
<code xml>
 
   <Grid x:Name="LayoutRoot" Background="Transparent">
 
   <Grid x:Name="LayoutRoot" Background="Transparent">
Line 57: Line 47:
 
         </Grid.RowDefinitions>
 
         </Grid.RowDefinitions>
  
         <TextBlock   x:Name="PageTitle"      Text="login"    Grid.Row="0" FontSize="80" Margin="10,0" VerticalAlignment="Bottom"/>
+
         <TextBlock x:Name="PageTitle"      Text="login"    Grid.Row="0" FontSize="80" Margin="10,0" VerticalAlignment="Bottom"/>
         <TextBlock   x:Name="txt_username"  Text="UserName" Grid.Row="1" FontSize="40" Margin="15,0" VerticalAlignment="Bottom" FontWeight="Medium"/>
+
         <TextBlock x:Name="txt_username"  Text="UserName" Grid.Row="1" FontSize="40" Margin="15,0" VerticalAlignment="Bottom" FontWeight="Medium"/>
         <TextBox     x:Name="txtbx_username" Text=""        Grid.Row="2" TextWrapping="Wrap" Margin="10,0" />
+
         <TextBox x:Name="txtbx_username" Text=""        Grid.Row="2" TextWrapping="Wrap" Margin="10,0" />
         <TextBlock   x:Name="txt_password"  Text="Password" Grid.Row="3" FontSize="40" Margin="15,0" VerticalAlignment="Bottom" FontWeight="Medium"/>
+
         <TextBlock x:Name="txt_password"  Text="Password" Grid.Row="3" FontSize="40" Margin="15,0" VerticalAlignment="Bottom" FontWeight="Medium"/>
 
         <PasswordBox x:Name="txtbx_password" Password=""    Grid.Row="4"  Margin="10,0" />
 
         <PasswordBox x:Name="txtbx_password" Password=""    Grid.Row="4"  Margin="10,0" />
 
     </Grid>
 
     </Grid>
Line 75: Line 65:
  
 
==Designing Registeration Page==
 
==Designing Registeration Page==
In the '''Page1.xaml''' file, we will drop down a few Text Block & Text Box controls. We will also use few App bar buttons. We will set the desired properties like shown below:
+
In the '''Page1.xaml''' file, we will drag and drop a TextBlock & TextBox controls. We will also use few App bar buttons. We will set the desired properties like shown below:
 
<code xml>
 
<code xml>
 
   <Grid x:Name="LayoutRoot" Tap="LayoutRoot_Tap">
 
   <Grid x:Name="LayoutRoot" Tap="LayoutRoot_Tap">
Line 84: Line 74:
 
          
 
          
 
         <!--TitlePanel contains the name of the application and page title-->
 
         <!--TitlePanel contains the name of the application and page title-->
         <TextBlock   x:Name="PageTitle" Text="register" Grid.Row="0" FontSize="80" Margin="10,0" VerticalAlignment="Bottom"/>
+
         <TextBlock x:Name="PageTitle" Text="register" Grid.Row="0" FontSize="80" Margin="10,0" VerticalAlignment="Bottom"/>
 
    
 
    
 
         <!--ContentPanel - place additional content here-->
 
         <!--ContentPanel - place additional content here-->
Line 101: Line 91:
 
                     </Grid.RowDefinitions>
 
                     </Grid.RowDefinitions>
  
                     <TextBlock   x:Name="txt_username"  Text="UserName" Grid.Row="0" FontSize="40" Margin="15,0" VerticalAlignment="Bottom" FontWeight="Medium"/>
+
                     <TextBlock x:Name="txt_username"  Text="UserName" Grid.Row="0" FontSize="40" Margin="15,0" VerticalAlignment="Bottom" FontWeight="Medium"/>
                     <TextBox     x:Name="txtbx_username" Text=""        Grid.Row="1" TextWrapping="Wrap" Margin="10,0" />
+
                     <TextBox x:Name="txtbx_username" Text=""        Grid.Row="1" TextWrapping="Wrap" Margin="10,0" />
                     <TextBlock   x:Name="txt_password"  Text="Password" Grid.Row="2" FontSize="40" Margin="15,0" VerticalAlignment="Bottom" FontWeight="Medium"/>
+
                     <TextBlock x:Name="txt_password"  Text="Password" Grid.Row="2" FontSize="40" Margin="15,0" VerticalAlignment="Bottom" FontWeight="Medium"/>
 
                     <PasswordBox x:Name="txtbx_password" Password=""    Grid.Row="3"  Margin="10,0" />
 
                     <PasswordBox x:Name="txtbx_password" Password=""    Grid.Row="3"  Margin="10,0" />
  
                     <TextBlock   x:Name="txt_Emailid"  Text="E-mail Id" Grid.Row="4" FontSize="40" Margin="15,0" VerticalAlignment="Bottom" FontWeight="Medium"/>
+
                     <TextBlock x:Name="txt_Emailid"  Text="E-mail Id" Grid.Row="4" FontSize="40" Margin="15,0" VerticalAlignment="Bottom" FontWeight="Medium"/>
                     <TextBox     x:Name="txtbx_Emailid" Text=""          Grid.Row="5" TextWrapping="Wrap" Margin="10,0" />
+
                     <TextBox x:Name="txtbx_Emailid" Text=""          Grid.Row="5" TextWrapping="Wrap" Margin="10,0" />
                     <TextBlock   x:Name="txt_Country"  Text="Country"  Grid.Row="6" FontSize="40" Margin="15,0" VerticalAlignment="Bottom" FontWeight="Medium"/>
+
                     <TextBlock x:Name="txt_Country"  Text="Country"  Grid.Row="6" FontSize="40" Margin="15,0" VerticalAlignment="Bottom" FontWeight="Medium"/>
                     <TextBox     x:Name="txtbx_Country" Text=""          Grid.Row="7" TextWrapping="Wrap" Margin="10,0" />
+
                     <TextBox x:Name="txtbx_Country" Text=""          Grid.Row="7" TextWrapping="Wrap" Margin="10,0" />
 
                 </Grid>
 
                 </Grid>
 
             </ScrollViewer>
 
             </ScrollViewer>
Line 124: Line 114:
  
 
==Designing Details Page==
 
==Designing Details Page==
In the ''welcome.xaml''' file, we will drop down a few Text Block & image controls. We will set the desired properties like shown below:
+
In the '''welcome.xaml''' file, we will drag and drop a TextBlock & Image control. We will set the desired properties like shown below:
 
<code xml>
 
<code xml>
 
<Grid x:Name="LayoutRoot" Background="Transparent">
 
<Grid x:Name="LayoutRoot" Background="Transparent">
Line 139: Line 129:
 
         <!--ContentPanel - place additional content here-->
 
         <!--ContentPanel - place additional content here-->
 
         <Grid x:Name="ContentPanel" Grid.Row="1" Margin="10">
 
         <Grid x:Name="ContentPanel" Grid.Row="1" Margin="10">
             <Image Source="/Session-TimeOut%20App;component/Images/Penguins.jpg" Stretch="Fill" />
+
             <Image Source="/Session-TimeOut%20App;component/Images/Penguins.jpg" Stretch="Fill" />
 
         </Grid>
 
         </Grid>
 
     </Grid>
 
     </Grid>
Line 145: Line 135:
  
 
== Code Behind ==
 
== Code Behind ==
We have a session time-out class named as ''SessionOut.cs'' which keeps track of the dispatch timer.
+
 
Once the user is navigated to registration page the dispatch timer is invoked.Below mention is the code used :
+
We have a session time-out class named {{Icode|SessionOut}} which keeps track of the dispatch timer.
<code cpp>
+
 
 +
Once the user is navigated to registration page the dispatch timer is invoked, as shown below:
 +
<code csharp>
 
public void start_timer(int time)
 
public void start_timer(int time)
 
{
 
{
Line 159: Line 151:
 
}
 
}
 
</code>
 
</code>
''time parameter'' used in the start_timer() is the default session time
+
''time parameter'' used in the {{Icode|start_timer()}} is the default session time.
  
 
*Timer tick event :
 
*Timer tick event :
<code cpp>
+
<code csharp>
 
void TimerTick(object sender, EventArgs e)
 
void TimerTick(object sender, EventArgs e)
 
{
 
{
Line 173: Line 165:
 
}  
 
}  
 
</code>
 
</code>
in the above function we are decrement the ''_timeLeft''. Once the value for _timeLeft becomes zero the timer is stopped and log_off variable is set to true. Now, if the user will tap on the registration page "Session Timed-Out" is popped and he/she is navigated back to the login page.
+
In the above function we are decrement the {{Icode|_timeLeft}}. Once the value for {{Icode|_timeLeft}} becomes zero the timer is stopped and {{Icode|log_off}} variable is set to true. Now, if the user will tap on the registration page "Session Timed-Out" is popped and the user is navigated back to the login page.
Where as if the user will tap the registration page before the value of _timeLeft becomes zero , timer is reset to default session time and in case he/she will provide the credentials before the value of _timeLeft becomes zero , timer is stopped and user is navigated to details page.  
+
 
 +
Where as if the user will tap the registration page before the value of {{Icode|_timeLeft}} becomes 0, timer is reset to default session time and in case he/she will provide the credentials before the value of {{Icode|_timeLeft}} becomes 0, timer is stopped and user is navigated to details page.  
  
*Method used to re-invoke the dispatch timer
+
Below is the method used to re-invoke the dispatch timer:
<code cpp>
+
<code csharp>
 
  private void LayoutRoot_Tap(object sender, GestureEventArgs e)
 
  private void LayoutRoot_Tap(object sender, GestureEventArgs e)
 
{
 
{
             if (SessionOut.log_off == true)  // if SessionOut flag is set to true , user is navigated back to main screen
+
             if (SessionOut.log_off == true)  // if SessionOut flag is set to true, user is navigated back to main screen
 
             {
 
             {
 
                 MessageBox.Show("Session Expired");
 
                 MessageBox.Show("Session Expired");
Line 192: Line 185:
 
}
 
}
 
</code>
 
</code>
LayoutRoot_Tap is the event used to capture the tap event in registration page.
+
{{Icode|LayoutRoot_Tap}} is the event used to capture the tap event in registration page.
  
* Now build the project and run it using F5.
+
* Now build the project and run it using F5.[[Category:Windows Phone 7.5]]

Revision as of 04:28, 10 April 2013

This article shows how to implement session time out in a Windows Phone 7 app using a DispatcherTimer

WP Metro Icon UI.png
SignpostIcon XAML 40.png
WP Metro Icon WP8.png
SignpostIcon WP7 70px.png
Article Metadata
Code ExampleTested with
SDK: Windows Phone SDK 7.1
Compatibility
Platform(s): Windows Phone 7.5 and later
Windows Phone 8
Windows Phone 7.5
Article
Created: Vaishali Rawat (28 Aug 2012)
Last edited: hamishwillee (10 Apr 2013)

Contents

Introduction

Sometimes it is a useful "security feature" close a login (or other) page if the user does not enter the required details within a specified time. This code example demonstrates how to implement a session time-out.

The app has three pages: login, registration and detail. When the app is launched the login page is displayed. The user is asked to enter login credentials or can navigate to the registeration page if they are a new users. If the user does not provide their registration details within a specific time the session ends and the login page is displayed. Upon successful registration user is sent to the detail page.

Starting a new project

  • Create a new project in Visual Studio 2010 from File > New Project > Windows Phone Application - Visual C#. Give a desired name of the project.

Creating a new Project in Windows Phone 7 using Visual C#

Designing LoginPage

In the MainPage.xaml file, we will drag and drop a TextBlock & TextBox controls. We will also use few App bar buttons. We will set the desired properties like shown below:

  <Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height=".50*"/>
<RowDefinition Height=".40*"/>
<RowDefinition Height=".30*"/>
<RowDefinition Height=".20*"/>
<RowDefinition Height=".30*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
 
<TextBlock x:Name="PageTitle" Text="login" Grid.Row="0" FontSize="80" Margin="10,0" VerticalAlignment="Bottom"/>
<TextBlock x:Name="txt_username" Text="UserName" Grid.Row="1" FontSize="40" Margin="15,0" VerticalAlignment="Bottom" FontWeight="Medium"/>
<TextBox x:Name="txtbx_username" Text="" Grid.Row="2" TextWrapping="Wrap" Margin="10,0" />
<TextBlock x:Name="txt_password" Text="Password" Grid.Row="3" FontSize="40" Margin="15,0" VerticalAlignment="Bottom" FontWeight="Medium"/>
<PasswordBox x:Name="txtbx_password" Password="" Grid.Row="4" Margin="10,0" />
</Grid>
 
<!--Sample code showing usage of ApplicationBar-->
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
<shell:ApplicationBarIconButton x:Name="btn_login" Click="btn_login_Click" IconUri="/Icons/Tick.png" Text="login"/>
<shell:ApplicationBarIconButton x:Name="btn_reset" Click="btn_reset_Click" IconUri="/Icons/Reset.png" Text="reset"/>
<shell:ApplicationBarIconButton x:Name="btn_register" Click="btn_register_Click" IconUri="/Icons/Reset.png" Text="register"/>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

Designing Registeration Page

In the Page1.xaml file, we will drag and drop a TextBlock & TextBox controls. We will also use few App bar buttons. We will set the desired properties like shown below:

  <Grid x:Name="LayoutRoot" Tap="LayoutRoot_Tap">
<Grid.RowDefinitions>
<RowDefinition Height=".20*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
 
<!--TitlePanel contains the name of the application and page title-->
<TextBlock x:Name="PageTitle" Text="register" Grid.Row="0" FontSize="80" Margin="10,0" VerticalAlignment="Bottom"/>
 
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="10">
<ScrollViewer>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height=".20*"/>
<RowDefinition Height=".30*"/>
<RowDefinition Height=".20*"/>
<RowDefinition Height=".30*"/>
<RowDefinition Height=".20*"/>
<RowDefinition Height=".30*"/>
<RowDefinition Height=".20*"/>
<RowDefinition Height=".30*"/>
</Grid.RowDefinitions>
 
<TextBlock x:Name="txt_username" Text="UserName" Grid.Row="0" FontSize="40" Margin="15,0" VerticalAlignment="Bottom" FontWeight="Medium"/>
<TextBox x:Name="txtbx_username" Text="" Grid.Row="1" TextWrapping="Wrap" Margin="10,0" />
<TextBlock x:Name="txt_password" Text="Password" Grid.Row="2" FontSize="40" Margin="15,0" VerticalAlignment="Bottom" FontWeight="Medium"/>
<PasswordBox x:Name="txtbx_password" Password="" Grid.Row="3" Margin="10,0" />
 
<TextBlock x:Name="txt_Emailid" Text="E-mail Id" Grid.Row="4" FontSize="40" Margin="15,0" VerticalAlignment="Bottom" FontWeight="Medium"/>
<TextBox x:Name="txtbx_Emailid" Text="" Grid.Row="5" TextWrapping="Wrap" Margin="10,0" />
<TextBlock x:Name="txt_Country" Text="Country" Grid.Row="6" FontSize="40" Margin="15,0" VerticalAlignment="Bottom" FontWeight="Medium"/>
<TextBox x:Name="txtbx_Country" Text="" Grid.Row="7" TextWrapping="Wrap" Margin="10,0" />
</Grid>
</ScrollViewer>
</Grid>
</Grid>
 
<!--Sample code showing usage of ApplicationBar-->
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
<shell:ApplicationBarIconButton x:Name="btn_register" Click="btn_register_Click" IconUri="/Icons/Tick.png" Text="register"/>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

Designing Details Page

In the welcome.xaml file, we will drag and drop a TextBlock & Image control. We will set the desired properties like shown below:

<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
 
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="PageTitle" Text="Welcome !!" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
 
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="10">
<Image Source="/Session-TimeOut%20App;component/Images/Penguins.jpg" Stretch="Fill" />
</Grid>
</Grid>

Code Behind

We have a session time-out class named SessionOut which keeps track of the dispatch timer.

Once the user is navigated to registration page the dispatch timer is invoked, as shown below:

public void start_timer(int time)
{
if (_timer.IsEnabled == true)
{
_timer.Stop();
}
_timeLeft = time;
_timer.Tick += TimerTick; // event handler
_timer.Start();
}

time parameter used in the start_timer() is the default session time.

  • Timer tick event :
void TimerTick(object sender, EventArgs e)
{
_timeLeft--;
if (_timeLeft == 0)
{
_timer.Stop();
log_off = true;
}
}

In the above function we are decrement the _timeLeft. Once the value for _timeLeft becomes zero the timer is stopped and log_off variable is set to true. Now, if the user will tap on the registration page "Session Timed-Out" is popped and the user is navigated back to the login page.

Where as if the user will tap the registration page before the value of _timeLeft becomes 0, timer is reset to default session time and in case he/she will provide the credentials before the value of _timeLeft becomes 0, timer is stopped and user is navigated to details page.

Below is the method used to re-invoke the dispatch timer:

 private void LayoutRoot_Tap(object sender, GestureEventArgs e)
{
if (SessionOut.log_off == true) // if SessionOut flag is set to true, user is navigated back to main screen
{
MessageBox.Show("Session Expired");
SessionOut.log_off = false;
NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.Relative));
}
else
{
_SessionOut.start_timer(25); // reset the timer
}
}

LayoutRoot_Tap is the event used to capture the tap event in registration page.

  • Now build the project and run it using F5.
154 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×