Feed Icon  

Contact

  • Bryant Likes
  • Send mail to the author(s) E-mail
  • twitter
  • View Bryant Likes's profile on LinkedIn
  • del.icio.us
Get Microsoft Silverlight
by clicking "Install Microsoft Silverlight" you accept the
Silverlight license agreement

Hosting By

Hot Topics

Tags

Open Source Projects

Archives

Ads

Reporting Services WebParts - Part I

Posted in Reporting Services | SharePoint at Monday, 26 April 2004 04:26 Pacific Daylight Time

This article is the first in a series of articles that I hope to publish about creating some Reporting Services WebParts. These webparts will be used to display Reporting Services reports as SharePoint WebParts. The articles assume that you have a working knowledge of SharePoint, WebParts, Reporting Services, and JavaScript.

In this article on Reporting Services webparts I want to setup some of the background information that will be used in creating the different parts. One of the first challenges to using Reporting Services (RS) in a WebPart is that RS uses a web service to manipulate the reports. There are some authentication issues that you will face if you try to call these web services directly from your webpart's code. The specific issue you will hit is called the double-hop and it is caused by the way Windows Authentication works. Mainly, Windows Authentication does not actually send your credentials over the wire but instead does some challenge/response stuff to authenticate the user, all of which is way outside the scope of this article and my expertise. So the route that these articles will take will be to make the web service calls directly from the client using some client side script.

If you're not already familiar with web services, SOAP, and javascript, you may need to read up a little before this all makes sense. But even if you don't understand it you can probably still work your way through the article and get it to work.

So our first task is to figure out how to make web service calls via client side script. Fourtunately for us a really big company has already figured out how to do this. Microsoft uses client side script in a lot of their web parts in order to call the SharePoint web services. This serves as a good starting point for us and their code is pretty easy to modify to fit our needs. To take a look at their code you need to browse to the “[c:\]Program Files\Common Files\Microsoft Shared\web server extensions\60\TEMPLATE\LAYOUTS\1033“ directory on your SharePoint computer. In that directory you will need to look at two files: IE55UP.js and IE50UP.js. There are a few diferences between these and which one you use will depend on what browser your org is using. I will be using the IE55UP.js for these articles but feel free to post comments about any differences you find.

In the IE55UP.js file you will find a procedure called SPSoapRequestBuilder. This procedure is the one used to called the SharePoint (SP) web services. It is important to note the namespaces used in the soap call: http://microsoft.com/sharepoint/webpartpages. This is one of the main changes we will make in order to use this same procedure to call the RS web service. Below is the procedure that the RS webparts will be using:

function RSSoapRequestBuilder(functionName)
{
  var object = new Object();
  function AddParameter(parameterName, parameterValue)
  {
    var index = this.parameterNameList.length;
    this.parameterNameList[index] = parameterName;
    this.parameterValueList[index] = parameterValue;
  }
  function SendSOAPMessage(xmlhttp)
  {
    var funcName = this.functionName;
    var paramNames = this.parameterNameList;
    var paramValues = this.parameterValueList;
    xmlhttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    xmlhttp.setRequestHeader("SOAPAction",
      "http://schemas.microsoft.com/sqlserver/2003/12/reporting/reportingservices/" 
      + funcName);
    var soapData = '<?xml version="1.0" encoding="utf-8"?>' +
      '<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' +
      'xmlns:xsd="http://www.w3.org/2001/XMLSchema" ' +
      'xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">' +
      '<soap:Body>' +
      '<' + funcName +
      ' xmlns="http://schemas.microsoft.com/sqlserver/2003/12/reporting/reportingservices">';
    for(var i=0; i < paramNames.length; i++)
    {
      var soapParam = (typeof(paramValues[i]) == "string") ? XmlEncode(paramValues[i]) : paramValues[i];
      soapData += '<' + paramNames[i] + '>' + soapParam + '</' + paramNames[i] + '>';
    }
    soapData += '</' + funcName + '>' +
      '</soap:Body>' +
      '</soap:Envelope>'
    xmlhttp.Send(soapData);
    return xmlhttp;
  }
  object.functionName = functionName;
  object.parameterNameList =
new Array();
  object.parameterValueList =
new Array();
  object.AddParameter = AddParameter;
  object.SendSOAPMessage = SendSOAPMessage;
  return object;
}

Notice that we are using a different namespace: http://schemas.microsoft.com/sqlserver/2003/12/reporting/reportingservices/. This is the namespace for the RS web service. Using this function we can now create the javascript object that will be used to call the RS web service. You will also need the XmlEncode function which is just the String2XML function from the SP script but renamed so that we don't run into conflicts and the Xml check was added so that we could pass in complex parameter types.

function XmlEncode(Value)
{
  if (Value.substring(0,1) == '<') return Value;
  var XmlString = "";
  var re = /&/g;
  XmlString = Value.replace(re,"&amp;");
  re = /</g;
  XmlString = XmlString.replace(re,"&lt;");
  re = />/g;
  XmlString = XmlString.replace(re,"&gt;");
  re = /"/g;
  XmlString = XmlString.replace(re,"&quot;");
  re = /'/g;
  XmlString = XmlString.replace(re,"&apos;");
  return XmlString;
}

This function just encodes any characters that would cause our Xml Soap request to be invalid.

To get started on our Visual Studio project, you will need to first download and install the WebPart Project Template from the SharePoint MSDN website. Once you have that downloaded and installed you can run through the following steps:

1) Start Visual Studio and create a new WebPart Library (name it something useful). You can create either a C# or a VB project. My code will be in C#, but I can provide the VB code if you get stuck in the translation.

2) Delete the WebPart1.cs and WebPart1.dwp files from the project.

3) Create a new folder called Scripts and add a new Javascript file called rs.js. Copy/Paste the two functions (XmlEncode and RSSoapRequestBuilder) into this new file and save it.

Now that we have the script to call, we need to make it easy for our webparts to call it. So we will add a utility class called Globals (style borrowed from .Text) that will encapsulate this for us. Here is what my Globals class looks like. You can Copy/Paste this into a new class called Globals in a new folder called Utility.

using System;

namespace Bml.RsWebParts.Utility
{
  /// <summary>
  /// Utility class containing common functions
  /// </summary>
  public sealed class Globals
  {
    /// <summary>
    /// Static methods only, so use a private
    /// constructor
    /// </summary>
    private Globals()
    {}

    /// <summary>
    /// Property ResourceMap (String)
    /// </summary>
    public static string ResourceMap
    {
      get
      {
        return "/wpresources/Bml.RsWebParts/";
      }
    }
    public static string GetResourceScriptTags(string scriptFileName)
    {
      return string.Format("<script language='javascript' src='{0}{1}'></script>",
        ResourceMap,
        scriptFileName);
    }
  }
}

Now you will have to modify what the ResourceMap function returns based on two things: (1) what the name of your assembly is and (2) how you deploy your assembly. When you deploy your assembly the extra files you include with it (such as script files) will get deployed into a folder that has the same name as your assembly. If you deploy your webpart using the STSADM tool, then you can just use the “/wpresources/[Your Assembly Name]” format. If you get fancy and create an installer tool then you might need to tweak this to fit where you install your assembly to. For now just change it to “/wpresources/[Your Assembly Name]“.

The next step is to create your first webpart which is going to be a very simple example so that this article isn't too long. So in your VS project select Add New Item and then select Web Part. Name it RsReportInfo. Next delete everything inside the class body so that you have an empty class with no variables, methods, or properties.

The first thing we are going to add is three properties (two of which we will eliminate later) that we will use to setup this webpart. Below is the C# code that you need to add to your class.


#region
Properties


private string serverUrl = string.Empty;
private string reportPath = string.Empty;
private string propertyList = "Name|Description";

 


[Browsable(

 

true),
 Category("Reporting Services"),
 DefaultValue(""),
 WebPartStorage(Storage.Shared),
 FriendlyName("Server URL"),
 Description("Server url such as http://localhost/reportserver")]
public string ServerUrl
{
  get {return serverUrl;}
  set {serverUrl = value;}
}


[Browsable(

 

true),
 Category("Reporting Services"),
 DefaultValue(""),
 WebPartStorage(Storage.Shared),
 FriendlyName("Report Path"),
 Description("Report path such as /SampleReports/Product Line Sales")]
public string ReportPath
{
  get {return reportPath;}
  set {reportPath = value;}
}


[Browsable(
true),
 Category("Reporting Services"),
 DefaultValue(""),
 WebPartStorage(Storage.Shared),
 FriendlyName("Property List"),
 Description("List of properties to display such as Name|Description")]
public string PropertyList
{
  get {return propertyList;}
  set {propertyList = value;}
}


#endregion

 

These three properties will allow us to configure the webpart to display information about a particular report. The property list is a pipe separated list of properties that should be displayed. The properties that you can display for a report are found in the documentation here (if you have RS installed). Next we are going to add four overriden methods and one private method. Below are these methods along with a short description of what they do.

 

 
public override ToolPart[] GetToolParts()
{
  ToolPart[] toolparts =
new ToolPart[2];
  WebPartToolPart wptp =
new WebPartToolPart();
  CustomPropertyToolPart custom =
new CustomPropertyToolPart();
  toolparts[1] = wptp;
  toolparts[0] = custom;
  custom.Expand(0);
  return toolparts;
}

 

 

This method will put our custom properties on the top of the property list and will expand it. This section is really optional but it is worth adding since it makes configuring the webpart much easier.protected override void OnLoad(EventArgs e)
{
  Page.RegisterClientScriptBlock("rs", Globals.GetResourceScriptTags("rs.js"));
  base.OnLoad (e);
}

We register our client script block when the Load event fires. We use the name “rs“ since the rs.js file is shared by all the RS webparts. If another webpart has already loaded the rs.js script it won't be loaded again.

protected override void OnPreRender(EventArgs e)
{
  if (serverUrl.Length > 0 && reportPath.Length > 0)
  {
    Page.RegisterStartupScript("rs_info" +
base.Qualifier, GetStartupScript());
  }
  base.OnPreRender (e);
}

We register a startup script during the PreRender event. This will be important later when the ServerUrl and ReportPath properties can be set by other webparts. For now we are just creating our startup script using the properties set in the property window.

protected override void RenderWebPart(HtmlTextWriter output)
{
  if (serverUrl.Length > 0 && reportPath.Length > 0)
  {
    output.AddAttribute(HtmlTextWriterAttribute.Class, "ms-WPBody");
    output.AddAttribute(HtmlTextWriterAttribute.Id, "divInfo");
    output.AddAttribute(HtmlTextWriterAttribute.Style, "overflow:auto;");
    output.RenderBeginTag(HtmlTextWriterTag.Div);
    output.RenderEndTag();
// div
  }
}

Here is where our webpart is actually rendered. Our webpart is really just a div tag that will get populated by the client side script which we will create next.

private string GetStartupScript()
{
  StringBuilder sb =
new StringBuilder();
  string[] properties = propertyList.Split('|');
  sb.Append("<script language='javascript'>");
  sb.Append(Environment.NewLine);
  sb.AppendFormat("GetReportInfo('{0}', '{1}', '", serverUrl, reportPath);

  foreach(string prop in properties)
  {
    sb.AppendFormat("<Property><Name>{0}</Name></Property>", prop);
  }
  sb.Append("');");
// ends the GetReportInfo method call
  sb.Append("</script>");
  sb.Append(Environment.NewLine);
  return sb.ToString();
}

This client side script passes in a call to the GetReportInfo method. This method needs to be added to our rs.js file (and is shown below). The properties parameter of the GetProperties web method takes an array of Property objects. If you're wondering how I figured this out you can take a look at the RS WSDL file (if you don't know what WSDL is then you probably don't want to look at the file). In the WSDL file you can see the type of XML that needs to be passed for this parameter by looking at the schema.

Finally here is the GetReportInfo method that needs to be added to the rs.js file.

function GetReportInfo(serverUrl, reportPath, properties)
{
  var returnList = '';
  try
  {
    var xmlhttp = null;
    try
    {
      xmlhttp =
new ActiveXObject("Msxml2.XMLHTTP.4.0");
    }
    catch(e)
    {
      xmlhttp =
new ActiveXObject("Msxml2.XMLHTTP");
    }
    xmlhttp.open("POST", serverUrl + '/reportservice.asmx',
false);
    var soapBuilder = RSSoapRequestBuilder("GetProperties");
    soapBuilder.AddParameter("Item", reportPath);
    soapBuilder.AddParameter("Properties", properties);
    soapBuilder.SendSOAPMessage(xmlhttp);
    returnList = xmlhttp.responseText;
  }
  catch(e)
  {
    alert(e.description);
    return null;
  }

  try
  {
    var xmldom = new ActiveXObject("Msxml2.DOMDocument");
    xmldom.async =
false;
    xmldom.loadXML(returnList);
    //did we get a soap error?
    isSoapError = xmldom.selectSingleNode(".//faultstring");
    if(isSoapError)
    {
      alert(isSoapError.text);
      return false;
    }

    var nodeList = xmldom.selectNodes(".//Property");
    var output = '';

    for(var i = 0; i < nodeList.length; i++)
    {
       output += "<b>" +
        nodeList[i].selectSingleNode("Name").text +
        "</b>: " +
        nodeList[i].selectSingleNode("Value").text +
        "<br>";
    }

    if (output.length == 0) output = "No information available.";

    divInfo.innerHTML = output;

  }
  catch(e)
  {
    alert(e.description);
  }
  return false;
}

 

 

The only tasks that remain are to create the DWP file, modify the manifest, and to deploy the webpart. The DWP file is added by right clicking the project and selecting Add New Item. Select Web Part DWP for the item type and give it the same name as your class file. Below is the DWP for my web part (your file should look similar).

<?xml version="1.0" encoding="utf-8"?>
<WebPart xmlns="http://schemas.microsoft.com/WebPart/v2" >
  <Title>Rs Report Info</Title>
  <Description>Displays information about a report.</Description>
  <Assembly>Bml.RsWebParts</Assembly>
  <TypeName>Bml.RsWebParts.RsReportInfo</TypeName>
</WebPart>

 

The next step is to modify the manifest file to include all the information about your new webpart and its resources. You will need to add the rs.js file as a resource so that it will be deployed with the webpart. Below is my manifest file (and again your file should look similar).

<?xml version="1.0"?>
<WebPartManifest xmlns="http://schemas.microsoft.com/WebPart/v2/Manifest">
  <Assemblies>
    <Assembly FileName="Bml.RsWebParts.dll">
      <ClassResources>
        <ClassResource FileName="rs.js"/>
      </ClassResources>
      <SafeControls>
        <SafeControl
          Namespace="Bml.RsWebParts"
          TypeName="*"
        />
      </SafeControls>
    </Assembly>
  </Assemblies>
  <DwpFiles>
    <DwpFile FileName="RsReportInfo.dwp"/>
  </DwpFiles>
</
WebPartManifest>

Finally you will need to add the setup project to your existing project. Right click the solution and select add new project and choose CAB Project under Setup and Deployment Projects. Name it something useful since this will be the name you will see when using the STSADM tool. Next right click your setup project and select Add Project Outputs. Select primary output and content files (select both by holding down ctrl). Back in your webpart project right click rs.js and select properties. In the properties window change the Build Action to Content. Do the same thing for the manifest.xml and the DWP file. Build the project and as long as you don't have any build errors you're ready to deploy the project to a test server.

Copy the cab file from either the debug or release folder in the setup project's folder (using windows explorer) to your server. The next step will involve using the STSADM tool which is a very simple tool once you understand it. In order to make using this tool easier I add “c:\Program Files\Common Files\Microsoft Shared\web server extensions\60\BIN” to the path environment variable on the server (right click My Computer, select Propreties, Advanced Tab, Environment Variables, find the PATH variable and add the path above but put a semicolon between it and the last path in the variable). Once you've added that path you can just open a command window and type “stsadm -o addwppack -filename c:\path_to_your_cab_file\your_cab_file.cab” and your webpart should now be installed.

 

 

Here is a screenshot of the webpart in action. Clearly there is a lot more we could do with this, but for now this is a good start. Hopefully this has given you a good start on using RS and SP webparts together.

Article version 1.0
Updated: 4/27/2004

Tuesday, 27 April 2004 08:29:00 (Pacific Daylight Time, UTC-07:00)
Thanks for this - very interesting ... and i look forward to the next installment as this is a very useful topic you are addressing. Mark
Wednesday, 30 June 2004 01:29:00 (Pacific Daylight Time, UTC-07:00)
Hi there - got the webpart installed ok but I cant get the report path to work - I have the reports server on a seperate machine and I am getting a problem when i try to setup the server url and reports path.
<br>
<br>I have http://(servername)/reportserver/
<br>and folder path is /Teched/
<br>
<br>Can anyone give me advice if this looks wrong?
<br>
<br>Cheers
<br>Gregor
gregor suttie
Tuesday, 03 August 2004 19:31:00 (Pacific Daylight Time, UTC-07:00)
I was wondering if it's possible to connect 2 webparts which are both RS Report Viewer webparts. The idea is to click on a link in webpart1 and pass a parameter to webpart2 which uses the parameter in a query and shows the results.
<br>
<br>Thanks!
Pieter
Tuesday, 03 August 2004 19:34:00 (Pacific Daylight Time, UTC-07:00)
Oh, I forgot to say that when I put 2 RS Report View webparts on 1 page a get a javascript error saying: 'style' is null or not an object.
<br>
<br>Is this a known error or am I just making some mistake?
Pieter
Tuesday, 05 October 2004 08:56:00 (Pacific Daylight Time, UTC-07:00)

<br>
<br>
<br>Hi Bryant
<br>I have some problems with reporting service web parts
<br>If the parameters contain some special value such as #, & [] , I have received the following error Message
<br>
<br>The path of the item '/R/Product Division,P]' is not valid. The full path must be less than 260 characters long, must start with slash; other restrictions apply. Check the documentation for complete set of restrictions. (rsInvalidItemPath) Get Online
<br>
<br>
<br>
<br>Regards & Thanks
<br>Vijay Vijayaratnam
Vijay
Tuesday, 19 October 2004 08:13:00 (Pacific Daylight Time, UTC-07:00)
Thanks for all the feedback! I'm looking into every issue....
Thursday, 21 October 2004 07:06:00 (Pacific Daylight Time, UTC-07:00)
First of all thanks for this great example!
<br>My question:
<br>In order to build up the parameter list, I notice that it is done in the rs.js script file, through client side script. Is there any particular reason to do so. Can the parameter list not be populated on the server via the renderwebpart method?
<br>
<br>Thanks for your feedback
Jansoone Wilfried
Thursday, 21 October 2004 07:35:00 (Pacific Daylight Time, UTC-07:00)
Thanks, glad you like the example. There is a reason it is client side (I'd much, much prefer to do everything on the server side). The problem is authentication. Even though you're logged into SharePoint, your credentials cannot be passed to the Report Server (unless you use a solution like Single Sign-On). By doing everything on the client you pass the credentials correctly.
<br>
<br>
Monday, 29 November 2004 21:48:00 (Pacific Standard Time, UTC-08:00)
could you post me the example in vb.
<br>
<br>my email is david@elbit.co.il
david
Friday, 14 January 2005 06:16:00 (Pacific Standard Time, UTC-08:00)
I got this one successfully working. Where can I get the other ones (RS Explorer, RS Report View, RS Report Parameters and RS Report Export)?
Carlos Silva
Sunday, 16 January 2005 10:25:00 (Pacific Standard Time, UTC-08:00)
how do i download the source
Intekhab
Sunday, 16 January 2005 10:27:00 (Pacific Standard Time, UTC-08:00)
Please email me the source code
<br>at
<br>shaikh_intekhab@hotmail.com
Shaikh Intekhab
Monday, 07 February 2005 16:31:00 (Pacific Standard Time, UTC-08:00)
hi,
<br>
<br>I have a problem when my report has dependent parameters.. When I tried to change the selected value on the first parameter, the second parameter did not changed.. How can I fix this bug.. Please help.. thanks!
jane
Monday, 19 June 2006 20:13:22 (Pacific Daylight Time, UTC-07:00)
hi all.
i am facing a problem in usuing the Rs Report view web part,
the problem is whatever path i enter in the Reports Path the last report parameters appear twice:
"The path of the item 'ExamDetails?ExamDetails' is not valid. The full path must be less than 260 characters long, must start with slash; other restrictions apply. Check the documentation for complete set of restrictions. (rsInvalidItemPath)
please revert,
Thanks
Ankit
Ankit Goel
Thursday, 29 May 2008 20:33:38 (Pacific Daylight Time, UTC-07:00)
hi

i need to pass parameters to the reportviewer webpart from other webparts.

i have two webparts (1. webpart that accepts parameters in a dropdown and textbox, 2. reportviewer webpart)

first webpart has few controls like dropdowns, textboxes and a viewreport button

on click of the viewreport button in the first webpart it should pass these parameters to the 2nd webparts (ideally it would be a report viewer webpart) and to the .rdl file and show the report accordingly.

how can i achieve this.

Regards
Ram
ram.chenna@hotmail.com

Wednesday, 29 April 2009 05:47:54 (Pacific Daylight Time, UTC-07:00)
Do you have any experience with the ReportViewerWebPart that is included with the sharepoint integratd reporting services add-in for sql 2005 and sharepoint 2007? Through the UI it is very easy to add and display a reporting service report. I of course assume you know much more about it than I, and am wondering how in the world to use it programmatically? My application is to include a page that has the reportviewerwebpart in a site definition, then when the site is deployed, I want to update the ReportPath. I've tried to get at the webpart through the LimitedWebPartManager, but for that webpart, all it returns is an "errorwebpart". Any ideas?
Dan
Thursday, 27 August 2009 11:53:20 (Pacific Daylight Time, UTC-07:00)
Dan, did you ever figure this out? I am running into the same issue as well. Thanks!
Nara
Comments are closed.