If you are allowing TFS access over internet, reports will be one thing that you would find difficult to expose over internet as this might sit in your SQL server.
What about the web access? Where is report page on team foundation web access? There is none!
So, What you could do is, move the SSRS service to the Web Server or a new server hosted outside.
Both requires IT’s involvement and support. No one would be ready to move the DB server out or even SSRS server out of the firewall if it’s a shared server. If you have your other reports than the TFS reports hosted on this server, this might not be received well from the IT.
So to solve this, I created a new page in TFS and exposed all the reports from the TFS web page. Which just embeds the report viewer control and renders the report on the TFS webpage.
Here are list of changes you will have to do for implementing this reporting plug-in.
- Open C:\Program Files\Microsoft Team Foundation Server 2010\Application Tier\Web Access\Web\UI\Controls\PageHeader.ascx. And insert a new row
<table cellspacing="0" cellpadding="0" class="mainTab">
<tr>
<td class="<%=(Request.Url.AbsolutePath.Contains("/UI/Pages/Reports/Reports.aspx") || Request.Url.AbsolutePath.Contains("/UI/Pages/Reports/Viewer.aspx") ? "att" : "ptt")%>">
<a class="<%=(Request.Url.AbsolutePath.Contains("/UI/Pages/Reports/Reports.aspx") || Request.Url.AbsolutePath.Contains("/UI/Pages/Reports/Viewer.aspx") ? "att" : "ptt")%>"
href="/tfs/web/UI/Pages/Reports/Reports.aspx?pguid=<%=this.Locator.ProjectUri.Segments[3] %>"><span class="ptt">Reports</span></a>
</td>
<td></td>
</tr>
</table>
- Create a project visual studio, ASP.Net Web page project. Create a new page and call it as Reports.aspx. In this page we will list all the reports and give a link to access all the reports.
- Open the reports.aspx and add a data list control to it. Your mark up should look like
<asp:Content ID="c" ContentPlaceHolderID="c" runat="server">
<asp:DataList ID="ReportList" runat="server">
<itemtemplate>
<td>
<asp:HyperLink ID="reportLink" runat="server">
<%1: # DataBinder.Eval(Container.DataItem,"Name")
%>
</asp:HyperLink>
</td>
</itemtemplate>
</asp:DataList>
</asp:Content>
Change the base page to WebAccessPage, this is how you access the TFS connection details that are buried in the base page.
public partial class Reports : WebAccessPage
In the code behind, In page initialization set the current tab and subscribe to list binding event.
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
ActiveTab = "Reports";
ReportList.ItemCreated += new DataListItemEventHandler(ReportListItemCreated);
}
On Load method, connect to the reports server and fetch all the reports using reporting services. Before adding the code, add a service reference to the SSRS service by Project->Add Service Reference –> URL as http://sqlserver:5050/ReportServer/ReportService2010.asmx. I have given the name for the service reference as ReportingService2010.
protected void Page_Load(object sender, EventArgs e)
{
ReportList.DataSource = GetProjectReports(this.Connection.CollectionName + "/" + this.CurrentProject.Name); ;
ReportList.DataBind();
}
private ICollection<CatalogItem> GetProjectReports(string path)
{
WindowsImpersonationContext ctx = WindowsIdentity.Impersonate(IntPtr.Zero);
CatalogItem[] reports = null;
try
{
ReportingService2010 reportService = new ReportingService2010();
// get the URL from the config http://sqlserver:5050/ReportServer
reportService.Url = config.ReportServerURL + "//ReportService2010.asmx";
reportService.UseDefaultCredentials = true;
reports = reportService.ListChildren(path, true);
}
finally
{
ctx.Undo();
}
return reports;
}
Assumption above is, workerprocess is running under the identity which has access to reporting server. So we are impersonating to default and reverting back while connecting to SSRS.
Now we need to make all the report list as hyper links with proper navigation set, in the item created event of the list we would implement this.
void ReportListItemCreated(object sender, DataListItemEventArgs e)
{
CatalogItem currItem = (CatalogItem)e.Item.DataItem;
HyperLink link = (HyperLink)e.Item.FindControl("reportLink");
if (currItem.TypeName == "Folder")
{
link.Attributes.Add("style", " color:Gray; font-size:16px");
link.Enabled = false;
}
else if (currItem.TypeName == "Report")
{
e.Item.Enabled = true;
link.Attributes.Add("style", "font-size:13px; padding-left:30px;");
link.NavigateUrl = base.ResolveUrl("~/UI/Pages/Reports/Viewer.aspx") + "?pguid=" + this.Locator.ProjectUri.Segments[3] + "&rep=" + currItem.Path;
}
else
{
link.Visible = false;
e.Item.Visible = false;
}
link.Text = currItem.Name;
}
We don’t have the report viewer implemented yet, so lets create a new page called viewer.aspx and add report viewer control to it. Mark up would look like
<%@ Register Assembly="Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
Namespace="Microsoft.Reporting.WebForms" TagPrefix="rsweb" %>
<asp:Content ID="c" ContentPlaceHolderID="c" runat="server">
<rsweb:ReportViewer EnableViewState="true" AsyncRendering="true" ID="ReportViewer" runat="server"
Width="100%" Height="100%"
BackColor="#f8f8f8" ForeColor="#555555" InternalBorderColor="#cccccc"
BorderColor="#dddddd" BorderStyle="Solid" BorderWidth="1px"
ProcessingMode="Remote">
</rsweb:ReportViewer>
</asp:Content>
In the code behind, binding to the control is straight forward once we have the path
public partial class Viewer : WebAccessPage
{
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
ActiveTab = "Reports";
Page.EnableViewState = true;
Master.EnableViewState = true;
ReportViewer.ReportError += new Microsoft.Reporting.WebForms.ReportErrorEventHandler(ReportViewerReportError);
}
void ReportViewerReportError(object sender, ReportErrorEventArgs e)
{
if (TimeSheetHelper.IsUserAdministrator(Request.LogonUserIdentity.Name))
{
Response.Write(e.Exception.ToString());
}
else
throw new Exception("Report exception", e.Exception);
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadReports(Request.QueryString["rep"]);
}
private void LoadReports(string reportPath)
{
ReportViewer.ServerReport.ReportServerCredentials = new ReportServerCredentials(CredentialCache.DefaultCredentials);
ReportViewer.ServerReport.ReportPath = reportPath;
ReportViewer.ServerReport.ReportServerUrl = new Uri(config.ReportServerURL );
ReportViewer.ServerReport.Refresh();
}
}
No comments:
Post a Comment