Tuesday, September 23, 2008

DYNAMIC IFRAME HEIGHT

Following code sample is taken from dynmaicdrive.com to get rid of IFRAME scrollbars. It will also adjust the Frame height automatically

/***********************************************
* IFrame SSI script II- © Dynamic Drive DHTML code library (http://www.dynamicdrive.com)
* Visit DynamicDrive.com for hundreds of original DHTML scripts
* This notice must stay intact for legal use
***********************************************/

//Input the IDs of the IFRAMES you wish to dynamically resize to match its content height:
//Separate each ID with a comma. Examples: ["myframe1", "myframe2"] or ["myframe"] or [] for none:
var iframeids=["IFReports"]

//Should script hide iframe from browsers that don't support this script (non IE5+/NS6+ browsers. Recommended):
var iframehide="yes"

var getFFVersion=navigator.userAgent.substring(navigator.userAgent.indexOf("Firefox")).split("/")[1]
var FFextraHeight=parseFloat(getFFVersion)>=0.1? 16 : 0 //extra height in px to add to iframe in FireFox 1.0+ browsers

function resizeCaller() {
var dyniframe=new Array()
for (i=0; i
if (document.getElementById)
resizeIframe(iframeids[i])
//reveal iframe for lower end browsers? (see var above):
if ((document.all || document.getElementById) && iframehide=="no"){
var tempobj=document.all? document.all[iframeids[i]] : document.getElementById(iframeids[i])
tempobj.style.display="block"
}
}
}

function resizeIframe(frameid){
var currentfr=document.getElementById(frameid)
if (currentfr && !window.opera){
currentfr.style.display="block"
if (currentfr.contentDocument && currentfr.contentDocument.body.offsetHeight) //ns6 syntax
currentfr.height = currentfr.contentDocument.body.offsetHeight+FFextraHeight;
else if (currentfr.Document && currentfr.Document.body.scrollHeight) //ie5+ syntax
currentfr.height = currentfr.Document.body.scrollHeight;
if (currentfr.addEventListener)
currentfr.addEventListener("load", readjustIframe, false)
else if (currentfr.attachEvent){
currentfr.detachEvent("onload", readjustIframe) // Bug fix line
currentfr.attachEvent("onload", readjustIframe)
}
}
}

function readjustIframe(loadevt) {
var crossevt=(window.event)? event : loadevt
var iframeroot=(crossevt.currentTarget)? crossevt.currentTarget : crossevt.srcElement
if (iframeroot)
resizeIframe(iframeroot.id);
}

function loadintoIframe(iframeid, url){
if (document.getElementById)
document.getElementById(iframeid).src=url
}

if (window.addEventListener)
window.addEventListener("load", resizeCaller, false)
else if (window.attachEvent)
window.attachEvent("onload", resizeCaller)
else
window.onload=resizeCaller

ReportViewer Height + Scrollbars

If the output is mushed height-wise, add this to the ReportServices.css file which can be located under ReportManager/styles:

/* Fix report IFRAME height for Firefox */
.DocMapAndReportFrame
{
min-height: 860px;

}

------------------------------------------------------------------------------------------------

Following code uses javascript to get rid of multiple scrollbars in VS2005 reportviewer control

window.onload=function RemoveScrollbars()

{

document.getElementById("<%=ReportViewer1.ClientID %>").lastChild.firstChild.style.overflow = "visible";

}

<rsweb:ReportViewer

ID="ReportViewer1"

runat="server"

ProcessingMode="Remote"

AsyncRendering="false"

SizeToReportContent="true" />




Wednesday, September 3, 2008

Journey From NDoc To SandCastle

NDoc was Source Forge tool for generating MSDN style documentation based on XML comments in your .NET 1.0 or 1.1 code. Now with the evolution of 2.0 XML Comments with "///" no revised NDoc version is available till date.

Microsoft has stepped in an provided SandCastle, which allows you to generate documentation and help files based on your .NET 2.0 code. It is still in CTP, but has the following features:

“Sandcastle produces accurate, MSDN style, comprehensive documentation by reflecting over the source assemblies and optionally integrating XML Documentation Comments. Sandcastle has the following key features:

  • Works with or without authored comments
  • Supports Generics and .NET Framework 2.0
  • Sandcastle has 2 main components (MrefBuilder and Build Assembler)
  • MrefBuilder generates reflection xml file for Build Assembler
  • Build Assembler includes syntax generation, transformation..etc
  • Sandcastle is used internally to build .Net Framework documentation“

You can download SandCastle here

Following resources can provide you additional information about Sandcastle:


Creating a Chm build using Sandcastle

Here are the steps to create a Chm build using Sandcastle. Please see the atatchment for the steps in text format.

Prerequisites:

1. .Net Framework 2.0

2. For Chm generation download HTML Help Workshop - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/htmlhelp/html/hwMicrosoftHTMLHelpDownloads.asp

How do Reference Build From /// Comments using Sandcastle V2.0:

Sandcastle can read authored triple-slash comments embedded in source files.

  1. Sandcastle by default is installed at c:\Program files\Sandcastle. Open a command prompt and type the following:

cd \Program Files\Sandcastle\Examples\Sandcastle

In this directory, you will find only a single C# file called test.cs under Examples\sandcastle directory.

  1. Begin by compiling the C# file and extracting the /// comments.

csc /t:library /doc:comments.xml test.cs

  1. This creates not only test.dll, but also comments.xml file that contains the extracted /// comments

  1. Next, run MRefBuilder:

MRefBuilder test.dll /out:reflection.org

  1. Transform the output

For building using VS2005 transforms please use the following:

XslTransform /xsl:"..\..\ProductionTransforms\ApplyVSDocModel.xsl" reflection.org /xsl:"..\..\ProductionTransforms\AddFriendlyFilenames.xsl" /out:reflection.xml

For building using prototype transforms please use the following:

XslTransform /xsl:..\..\ProductionTransforms\ApplyPrototypeDocModel.xsl reflection.org /xsl:..\..\ProductionTransforms\AddGuidFilenames.xsl /out:reflection.xml

  1. Generate a topic manifest

XslTransform /xsl:..\..\ProductionTransforms\ReflectionToManifest.xsl reflection.xml /out:manifest.xml

  1. Create an output directory structure

For building using VS2005 transforms please use the following:

call ..\..\Presentation\vs2005\copyOutput.bat

For building using prototype transforms please use the following:

call ..\..\Presentation\Prototype\copyOutput.bat

  1. Run BuildAssembler using the sandcastle component stack (Note: We are providing VS 2005 transforms under Presentation/VS2005 folder and the transforms shipped with the previous versions under Presentation/Prototype folder. For building VS2005 format please use sandcastle.config file from C:\Program Files\Sandcastle\Presentation\vs2005\Configuration folder as it uses shared content from C:\Program Files\Sandcastle\Presentation\vs2005\Content and transforms from C:\Program Files\Sandcastle\Presentation\vs2005\Transforms)

BuildAssembler /config:sandcastle.config manifest.xml

to generate topic files in HTM.

  1. Generate HTML help project

XslTransform /xsl:..\..\ProductionTransforms\ReflectionToChmProject.xsl reflection.xml /out:Output\test.hhp

  1. Generate intermediate table of contents

For building using VS2005 transforms please use the following:

XslTransform /xsl:..\..\ProductionTransforms\createvstoc.xsl reflection.xml /out:toc.xml
For building using prototype transforms please use the following:
XslTransform /xsl:..\..\ProductionTransforms\createPrototypetoc.xsl reflection.xml /out:toc.xml

  1. Generate HTML help project information

XslTransform /xsl:..\..\ProductionTransforms\TocToChmContents.xsl toc.xml /out:Output\test.hhc

XslTransform /xsl:..\..\ProductionTransforms\ReflectionToChmIndex.xsl reflection.xml /out:Output\test.hhk

  1. Run hhc (HTML Help 1.x Compiler) to generate Chm, hhc compiles the Sandcastle target files into a CHM file.

hhc output\test.hhp

Tuesday, August 12, 2008

Securing Web Services With Username and Password

keithelder's Blog.......

The article is short and doesn't give any examples of one of the methods I use a lot. I thought I would elaborate on one of the topics it touches on which is securing a web service with a Username and a Password. While there are other ways to secure web services I find this particular method works really well when dealing with internal systems that may not speak .Net. It is also simple to implement on both sides. These systems could be in Java, PHP or even 4GL. If you are a .Net developer and want to secure a web service with a username, password, or even other information, here is what you need to get going.

1. Create an object that extends SoapHeader


The first thing you need to do is create an object that extends the built-in SoapHeader object. The object can be simple or complex. Add the tokens you want to authenticate against. Here is a sample:

1 ///

2 /// Extends from SoapHeader and provides a username / password

3 /// for web methods that need authentication.

4 ///

5 public class ServiceAuthHeader : SoapHeader

6 {

7 public string Username;

8 public string Password;

9 }

2. Add a property to your service class that uses the class above


Once you've created your base soap header class, add it as a property to your service class.

// SoapHeader for authentication

public ServiceAuthHeader CustomSoapHeader;

3. Attribute the method you want to secure


Add the SoapHeader attribute to each or certain methods you wish to secure pass in the name of the property that is defined as the SoapHeader object in step 2.

1 [WebMethod]

2 [SoapHeader("CustomSoapHeader")]

3 public int AddTwoNumbers(int x, int y)

4 {

5

6 return x + y;

7 }

4. Create a method to process your SoapHeader for authentication

The last big step is to create a static method that will take in your custom soap header class and process it for validation. The first thing we want to do is make sure that some type of credentials were passed in the SoapHeader and that the properties we are looking for are not null. Finally we want to validate each property contains the information we are looking for. This could be read from the web.config file, database, or other places.

1 public class ServiceAuthHeaderValidation

2 {

3 ///

4 /// Validates the credentials of the soap header.

5 ///

6 ///

7 public static bool Validate(ServiceAuthHeader soapHeader)

8 {

9 if (soapHeader == null)

10 {

11 throw new NullReferenceException("No soap header was specified.");

12 }

13 if (soapHeader.Username == null)

14 {

15 throw new NullReferenceException("Username was not supplied for authentication in SoapHeader.");

16 }

17 if (soapHeader.Password == null)

18 {

19 throw new NullReferenceException("Password was not supplied for authentication in SoapHeader.");

20 }

21

22 if (soapHeader.Username != "myusername" || soapHeader.Password != "mypassword")

23 {

24 throw new Exception("Please pass the proper username and password for this service.");

25 }

26 return true;

27 }

28 }

5. Add validation to service method

1 [WebMethod]

2 [SoapHeader("CustomSoapHeader")]

3 public int AddTwoNumbers(int x, int y)

4 {

5 // Test to see if the proper credentials were passed in.

6 ServiceAuthHeaderValidation.Validate(CustomSoapHeader);

7

8 // If we get this far the user has been validated.

9 return x + y;

10 }

That's it. You now have all the pieces of the puzzle to process a request and validate the credentials of the calling client via a username and or password. If we launch the solution we will see that our XML for our service has been updated and now contains an XML Node called ServiceAuthHeader which contains two sub nodes: username, password.

Passing SoapHeader Credentials To Your Service

Now that we have our service secured, we need to now call the service and pass the credentials expected from a client. Based on the example above, once you add a web reference to the service and instantiate the service in code, the thing you want to look for is a new property of your service proxy called ServiceAuthHeader. This is converted into a property called ServiceAuthHeaderValue. This property needs to be an instantiation of the ServiceAuthHeader class where you set the username and password properties. Here is an example of a console application calling our service and passing the required information to authenticate.

1 using System;

2 using System.Collections.Generic;

3 using System.Text;

4

5 namespace ConsoleApplication1

6 {

7 class Program

8 {

9 static void Main(string[] args)

10 {

11 localhost.Service service = new ConsoleApplication1.localhost.Service();

12 localhost.ServiceAuthHeader header = new ConsoleApplication1.localhost.ServiceAuthHeader();

13 header.Username = "myusername";

14 header.Password = "mypassword";

15 service.ServiceAuthHeaderValue = header;

16 int x = service.AddTwoNumbers(1, 1);

17 Console.WriteLine(x);

18 Console.ReadLine();

19 }

20 }

21 }

The return result will be 2 of course and from the client side it is rather trivial to pass the credentials as you see. That's it. Happy authenticating!

[Related Link]
To take authenticating one step further with a custom SoapExtension read this follow up article.
http://keithelder.net/blog/archive/2007/01/09/Take-Securing-Web-Services-With-Username-and-Password-One-Step.aspx

Error 8152: "String or binary data would be truncated"

Following could be the possible reasons:

This error usually comes because of incorrect length of table fields.

1. check if there are triggers on the table, if so, you should check out the code of those as well.
2. Check field length of your table and any foreign key tables if exist

Sunday, August 10, 2008

Merging New Rows to an Existing DataSet

We can merge new records to an existing fillled dataset. Following is the example code:

//Existing Dataset
DataSet ds = code to fill dataset;

DataTable dt = reports.Tables[0];
foreach (DataRow dr in dt.Rows)
{
DataRow newRow = ds.Tables[0].NewRow();
newRow["name"] = dr["DisplayName"];
newRow["path"] = dr["ID"];
newRow["rolename"] = dr["Roles"];
newRow["username"] = HttpContext.Current.User.Identity.Name.ToString();


//Appending newly generated row to existing table in the dataset
ds.Tables[0].Rows.Add(newRow);
}

Thursday, August 7, 2008

C# Dynamic object instantiation and Method Calls

Following is the code to load any assembly and reading its classes to make dynamic instances out of them. You can further call the dynamic methods as well.


//Loading an assembly
Assembly objAssembly = Assembly.LoadFrom(Server.MapPath(@"bin\test.dll"));


//Getting list of all types in a the assembly
Type[] type = objAssembly.GetTypes();

// You can also define a typename if that is already known
//Type t = objAssembly.GetType("testnamespace.testclass");

//********************************************************

//Making Instance of object by passing a particular type
object newInstance = Activator.CreateInstance(type[0]);

//Calling the method of the newly created instance by using type name
string str = (string)type[0].GetMethod("testmethod").Invoke(newInstance, null);


Your test.dll will look something like this.....

namespace testnamespace
{
public class testclass
{
public static string testmethod()
{
return "hello";
}
}
}

Saturday, August 2, 2008

Using ReportViewer control with SQL Server 2005 Reporting Services Custom Security Extension

Russell's Blog

I've been playing around with using different forms of authentication / impersonation with the Report Viewer controls, and I thought I'd post the fruits of my efforts. Here we go:

Using Forms Authentication with the Winform Report Viewer control is easy -- Just pass in the creds and you're all done:

reportViewer1.ServerReport.ReportServerCredentials.SetFormsCredentials(null, "userName", "password", "domainName");
this.reportViewer1.RefreshReport();

Doing the same thing with the Webform Report Viewer control is kind of a pain in the tail. You actually have to implement an interface called IReportServerCredentials and write/borrow another subclass that handles all the cookie related stuff when dealing with Forms Auth.

I took most of the following code from the following help topic, btw:

http://msdn2.microsoft.com/en-us/library/microsoft.reporting.webforms.ireportservercredentials.aspx

Anyway, first create the code for your Forms Auth Login page:

MyReportingService svc = new MyReportingService();
svc.Url = "
http://myServer/reportserver/reportexecution2005.asmx";
try
{
svc.LogonUser("myUserName", "MyPassword", null);
Cookie myAuthCookie = svc.AuthCookie;
if (myAuthCookie == null)
{
Message.Text = "Logon failed";
}
else
{
HttpCookie cookie = new HttpCookie(myAuthCookie.Name, myAuthCookie.Value);
Response.Cookies.Add(cookie);
string returnUrl = Request.QueryString["ReturnUrl"];
if (returnUrl == null !returnUrl.StartsWith("/"))
Message.Text = "Return url is missing or invalid!";
else
Response.Redirect("
http://myServer/appFolder/default.aspx");
}
}
catch (Exception ex)
{
Message.Text = "Logon failed: " + ex.Message;
}

The code above calls LogonUser() against the SSRS web service so that we can get the Forms Auth cookie back from SSRS itself. Then, we stick the cookie into Response.Cookies, and forward the user to a page which has a ReportViewer control on it (in this case, http://myServer/appFolder/default.aspx)

OK, so now we're sitting on the page which has the Report Viewer control itself.

In Page_Load, we first see if the previous cookie exists...if it doesn't, we send you right back to the logon form:


HttpCookie cookie = Request.Cookies["sqlAuthCookie"];
if (cookie == null)
{
Response.Redirect("/appFolder/logon.aspx?ReturnUrl=" + HttpUtility.UrlEncode(Request.RawUrl));

}
else
{

ReportViewer1.ProcessingMode = ProcessingMode.Remote;
ReportViewer1.ServerReport.ReportServerUrl = new Uri("
http://myServer/reportserver");
ReportViewer1.ServerReport.ReportPath = "/Report Project2/Report1";

Cookie authCookie = new Cookie(cookie.Name, cookie.Value);
authCookie.Domain = "myServer";
ReportViewer1.ServerReport.ReportServerCredentials =
new MyReportServerCredentials(authCookie);
}

If the cookie IS there, we set a few properties on the Report Viewer control, then set the ReportServerCredentials property of the control equal to our authCookie. Here is where the implementation of IReportServerCredentials and that other "cookie-handling class" come in.

First, here's the implementation of IReportServerCredentials. It allows us to create a MyReportServerCredentials object:

class MyReportServerCredentials : IReportServerCredentials
{
private Cookie m_authCookie;

public MyReportServerCredentials(Cookie authCookie)
{
m_authCookie = authCookie;
}

public WindowsIdentity ImpersonationUser
{
get
{
return null; // Use default identity.
}
}

public ICredentials NetworkCredentials
{
get
{
return null; // Not using NetworkCredentials to authenticate.
}
}

public bool GetFormsCredentials(out Cookie authCookie,
out string user, out string password, out string authority)
{
authCookie = m_authCookie;
user = password = authority = null;
return true; // Use forms credentials to authenticate.
}
}


As I mentioned earlier, we also have to subclass the myServer.ReportExecutionService class and override a few methods in order to do the cookie-related work:

public class MyReportingService : myServer.ReportExecutionService
{
private Cookie m_authCookie;

public Cookie AuthCookie
{
get
{
return m_authCookie;
}
}

protected override WebRequest GetWebRequest(Uri uri)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
request.Credentials = base.Credentials;
request.CookieContainer = new CookieContainer();
if (m_authCookie != null)
request.CookieContainer.Add(m_authCookie);
return request;
}

protected override WebResponse GetWebResponse(WebRequest request)
{
WebResponse response = base.GetWebResponse(request);
string cookieName = response.Headers["RSAuthenticationHeader"];
if (cookieName != null)
{
HttpWebResponse webResponse = (HttpWebResponse)response;
m_authCookie = webResponse.Cookies[cookieName];
}
return response;
}
}

Parameters can also be passed to ReportViewer and we can also Show/Hide them as needed:


Cookie authCookie = new Cookie(cookie.Name, cookie.Value);
authCookie.Domain = ConfigurationManager.AppSettings["serverName"].ToString();
reportViewer1.ServerReport.ReportServerCredentials = new MyReportServerCredentials(authCookie);
//Checking available parameter list
ReportParameterInfoCollection rparams = reportViewer1.ServerReport.GetParameters();
List paramList = new List();
//Hide parameters if user is External user i.e not Internal
if (isClientID != "false")
{
foreach (ReportParameterInfo rpinfo in rparams)
{
switch (rpinfo.Name)
{
case "ClientID":
paramList.Add(new Microsoft.Reporting.WebForms.ReportParameter("ClientID", isClientID, false));
break;
case "IsExported":
paramList.Add(new Microsoft.Reporting.WebForms.ReportParameter("IsExported","true", false));
break;
case "ServerType":
paramList.Add(new Microsoft.Reporting.WebForms.ReportParameter("ServerType", "true", false));
break;
case "IsReleased":
paramList.Add(new Microsoft.Reporting.WebForms.ReportParameter("IsReleased", "true", false));
break;
}
}
this.reportViewer1.ServerReport.SetParameters(paramList);
}

...and that's it. You may actually have better luck using the URL at the top of the page for your code copying and pasting as it contains HTML you can use for you logon page, too.

My biggest problem with this whole scenario was actually *finding* the help topic I needed...It would have nice if I could have searched on "forms authentication report viewer control" and been directed to the topic in question (grump, grump).

Forms Authentication in SQL Server 2005 Reporting Services

Form authentication extension can be easily implemented. Here's how...
http://msdn.microsoft.com/en-us/library/ms160724.aspx


You can find all the extensions for RS2005 at http://msdn.microsoft.com/en-us/library/ms160911.aspx


So once you have sample projects then you need to compile it and get the required custom security dll.
Above output dll needs to be placed at two places.
1. <system-drive>:/<path to reportingservices>/reportserver/bin
2. <system-drive>:/<path to reportingservices>/reportmanager/bin
you also need to place two .aspx files for creating cutom interface in reporting services. These two files should be placed as following:
1. <system-drive>:/<path to reportingservices>/reportserver/logon.aspx
2. <system-drive>:/<path to reportingservices>/reportmanager/pages/UILogon.aspx
After placing above files at desired locations. We need to edit RS configuration files to make them start using our custom dll extension. These editings should be done at both ReportServer and ReportManager as follows:
Important : Dont forget to take backup of all config files before making changes. Otherwise in case of errors, RS will be corrupt and you need to re-install reporting services
- Report Server
1. rsreportserver.config
- your custom report server url should be given with instead of 'localhost':
http://EXTREME-MACHINE/ReportServer
- Authentication and Authorization should be modified as :
<Security>
<Extension Name="Forms" Type="ReportingServices.CustomSecurity.Authorization,ReportingServices.CustomSecurity" >
<Configuration>
<AdminConfiguration>
<UserName>testuser
</AdminConfiguration>
</Configuration>
</Extension>
</Security>


<Authentication>

<Extension Name="Forms" Type="ReportingServices.CustomSecurity.AuthenticationExtension, ReportingServices.CustomSecurity">
<Configuration>
<ConnectionString>server=localhost;user id=sa;password=123;database=BenmarkIS
</Configuration>
</Extension>
</Authentication>

2. rssrvpolicy.config
- Trust levels should be defined as highlighted:
class="FirstMatchCodeGroup"
version="1"
PermissionSetName="FullTrust"
Description="This code group grants MyComputer code Execution permission. ">
<IMembershipCondition
class="ZoneMembershipCondition"
version="1"
Zone="MyComputer" />
<CodeGroup
class="UnionCodeGroup"
version="1"
PermissionSetName="FullTrust"
Name="Microsoft_Strong_Name"
Description="This code group grants code signed with the Microsoft strong name full trust. ">
- A new Code Group should be inserted for security extenstion as given:

<CodeGroup
class="UnionCodeGroup"
version="1"
Name="SecurityExtensionCodeGroup"
Description="Code group for the sample security extension"
PermissionSetName="FullTrust">
<IMembershipCondition
class="UrlMembershipCondition"
version="1"
Url="C:\Program Files\Microsoft SQL Server\MSSQL.4\Reporting Services\ReportServer\bin\ReportingServices.CustomSecurity.dll"
/>
</CodeGroup>
<CodeGroup
class="UnionCodeGroup"
version="1"
PermissionSetName="FullTrust"
Name="SharePoint_Server_Strong_Name"
3. Web.config
- Following highlighted changes should be made:
<authentication mode="Forms">
<forms loginUrl="logon.aspx" name="sqlAuthCookie" timeout="60" slidingExpiration="true" path="/"></forms>
</authentication>
<authorization>
<deny users="?" />
</authorization>
<identity impersonate="false" />


- Report Manager
1. rsmgrpolicy.config
- Trust levels should be changed in rsmgrpolicy.config as highlighted
class="FirstMatchCodeGroup"
version="1"
PermissionSetName="FullTrust"
Description="This code group grants MyComputer code Execution permission. ">
<IMembershipCondition
class="ZoneMembershipCondition"
version="1"
Zone="MyComputer" />
<CodeGroup
class="UnionCodeGroup"
version="1"
PermissionSetName="FullTrust"
Name="Microsoft_Strong_Name"
Description="This code group grants code signed with the Microsoft strong name full trust. ">

2. RSWebApplication.config
- Basic report manager redirection to the custome made .aspx page
*Note: Use instead of 'localhost' in the ReportServerUrl
<CustomAuthenticationUI>
<loginUrl>/Pages/UILogon.aspx</loginUrl>
<UseSSL>False</UseSSL>
</CustomAuthenticationUI>
<ReportServerUrl>http://extreme-machine/ReportServer</ReportServerUrl>
</UI>

Once you are done with above changes then you need to setup IIS for anonymous access. So in the 'security' tab of 'reportserver' and 'reports' web folders, set security level as 'anonymous'.
Here you go, you are done with the settings.
:) Happy Reporting!!