Sample: Version Control RSS Feed
<%@ Page Language="c#" %>
<%@ OutputCache Duration="20" Location="Server" VaryByParam="state" VaryByCustom="minorversion" VaryByHeader="Accept-Language"%>
<%
// "Copyright © Microsoft Corporation. All rights reserved. These Samples are based in part on the Extensible Markup Language (XML) 1.0 (Third Edition) specification Copyright © 2004 W3C® (MIT, ERCIM, Keio. All rights reserved. https://www.w3.org/consortium/legal/2002/copyright-documents-20021231."
%>
<%
//This posting is provided "AS IS" with no warranties of any kind and confers no rights. Use of samples included in this posting is subject to the terms specified at https://www.microsoft.com/info/cpyright.htm.
%>
<%@ Import Namespace="System" %>
<%@ Import Namespace="System.Collections" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Web" %>
<%@ Import Namespace="System.Xml" %>
<%@ Import Namespace="Microsoft.TeamFoundation.Client" %>
<%@ Import Namespace="Microsoft.TeamFoundation.VersionControl.Client"%>
<%@ Import Namespace="Microsoft.TeamFoundation.VersionControl.Common" %>
<%
// Generate an RSS feed for Team Foundation Source Code Control checkins
//
// Note: Only one request per "pester interval" from each system will be honored. See
// pesterInterval below.
//
// This feed returns information about the most recent N checkins. See maxCheckinCount
// below.
//
// Invoking this page without any parameters returns information about all Team
// Foundation checkins up to the maximum count. If a filename is supplied, information
// about the most recent checkins for that file only are returned. Specify a filename
// by adding ?serverPath=<serverPath>. Note that the filename must be expressed as a server
// pathname without the leading $/.
//
// E.g., to see the activity for a file $/teamProjectA/myFile, add this to the Url
// ?serverPath=teamProjectA/myfile
//
// This page returns no items if the file does not exist.
// ***************************************************
// (Default: off) Set this to true to throttle user requests by host/username
// ***************************************************
bool throttleRequests = false;
// Ignore requests from the same machine within this interval
int pesterInterval = 30 * 1000; // expressed in milliseconds
if (throttleRequests)
{
// If the originating system has been serviced recently, drop the request
object lastRequest = Context.Cache[User.Identity.Name+Request.UserHostAddress];
if (lastRequest != null)
{
if ((int) lastRequest + pesterInterval < Environment.TickCount)
{
return;
}
}
}
// Default -- everything under $/
string serverPathname = VersionControlPath.RootFolder;
try
{
// Check whether information on a specific file was requested
string pathname = Request.Params["serverPath"];
if (pathname != null)
{
// The pathname does not contain a leading '$'
serverPathname = VersionControlPath.Combine(VersionControlPath.RootFolder, pathname);
// The pathname is valid.
}
}
catch
{
Response.StatusCode = 404;
Response.End();
}
// The maximum number of changes to return per query
int maxCheckinCount = 50;
string rssVersion = "2.0";
string rssTtl = "5";
string rssLanguage = "en-US";
string rssLink = Request.Url.ToString();
string rssTitle = "Team Foundation RSS Feed for Source Code Control Checkins";
string rssGenerator = "Sample RSS Feed Generator for Team Foundation";
string rssDescription = "<p>This feed provides information on Team Foundation Checkins. Each Team Foundation checkin is manifested as a changeset. The changesets that have been created recently are listed here.</p><p>This feed contains the most recent <i>{0}</i> checkin(s).</p>";
string rssItemOnBehalfOf = "(on behalf of {0}) ";
string rssItemTitle = "Checkin {0} [{1} items]";
string rssNoItemsAvailableDescription = "An error occurred obtaining the latest checkin information from Team Foundation. Try again later.";
string rssItemDescription = "<p><a href=\"{5}\">Changeset {0}</a> was checked in by <i>{1} {2}</i>on {3}. This checkin includes changes to {4} item(s).</p><p>You can view the details of the checkin by selecting the provided link.</p>.";
string exceptionMessage = "<p>An exception occurred while getting updated information from Team Foundation; the detailed exception message is <i>{0}</i></p>";
string itemNotFoundErrorMessage = "<p>There were no items found.</p>";
string accessDeniedErrorMessage = "<p>You do not have permission to obtain the Team Foundation checkin information.</p>";
string otherErrorMessage = "<p>An error occurred. This may be a transient error or a permanent one. Please check the event log for messages from VSTF Source Code Control</p>";
Response.ContentType = "text/xml";
XmlTextWriter xt = new XmlTextWriter(Response.OutputStream, null);
// Begin creating the XML document
xt.WriteStartElement("rss");
xt.WriteAttributeString("version", rssVersion);
xt.WriteStartElement("channel");
xt.WriteElementString("title", rssTitle);
xt.WriteElementString("ttl", rssTtl);
xt.WriteElementString("link", rssLink);
xt.WriteElementString("pubDate", DateTime.Now.ToString());
xt.WriteElementString("language", rssLanguage);
xt.WriteElementString("generator", rssGenerator);
try
{
RecursionType recursionType = RecursionType.Full;
// Obtain the list of changes from the mid-tier; the number of changes is reported in
// the channel description.
IEnumerable changesetEnum = null;
try
{
TeamFoundationServer Tfs = null;
object cacheEntry = Context.Cache["TeamFoundationServer"];
if (cacheEntry == null)
{
// Note: only works on localhost
Tfs = new TeamFoundationServer("https://localhost:8080");
Context.Cache["TeamFoundationServer"] = Tfs;
}
else
{
Tfs = (TeamFoundationServer) cacheEntry;
}
VersionControlServer Vcs = (VersionControlServer) Tfs.GetService(typeof(VersionControlServer));
// Return changes
changesetEnum = Vcs.QueryHistory(serverPathname, // on this item
VersionSpec.Latest, // item version
0, // that are not deleted
recursionType, // at or below this item
null, // user
null, // start version
null, // stop version
maxCheckinCount, // Up to this many changes
true, // include changes
false);
int changeCount = 0;
foreach (Changeset change in changesetEnum)
{
changeCount++;
}
xt.WriteElementString("description", String.Format(rssDescription, changeCount));
}
catch (Exception e)
{
if (e is ItemNotFoundException)
{
rssNoItemsAvailableDescription += itemNotFoundErrorMessage;
}
else
{
rssNoItemsAvailableDescription += otherErrorMessage;
}
rssNoItemsAvailableDescription += String.Format(exceptionMessage, e.Message);
xt.WriteElementString("description", rssNoItemsAvailableDescription);
}
xt.WriteEndElement(); // channel
// Create an item for each returned changeset.
foreach (Changeset change in changesetEnum)
{
xt.WriteStartElement("item");
string onBehalfOf = null;
// Include the committer if it differs from the changeset owner.
// This occurs when a proxy agent performs the checkin.
if (!change.Owner.Equals(change.Committer))
{
onBehalfOf = String.Format(rssItemOnBehalfOf, change.Owner);
}
xt.WriteElementString("title", String.Format(rssItemTitle,
change.ChangesetId,
change.Changes.Length));
string csLink = HttpUtility.HtmlEncode(new ChangesetUri(
String.Format("{0}://{1}:{2}",
Request.Url.Scheme,
Request.Url.Host,
Request.Url.Port),
change.ChangesetId,
UriType.Extended).ToUrl());
xt.WriteElementString("description", String.Format(rssItemDescription,
change.ChangesetId,
change.Committer,
onBehalfOf,
change.CreationDate,
change.Changes.Length,
csLink));
xt.WriteElementString("link", csLink);
xt.WriteElementString("author", onBehalfOf == null ? change.Owner : change.Committer);
xt.WriteElementString("pubDate", change.CreationDate.ToString());
xt.WriteElementString("guid", change.ChangesetId.ToString());
xt.WriteEndElement(); // item
}
}
catch (Exception e)
{
Response.StatusCode = 404;
Response.End();
}
finally
{
xt.Close();
if (throttleRequests)
{
Context.Cache[Request.UserHostAddress] = Environment.TickCount;
}
}
%>
Comments
Anonymous
July 27, 2005
I've got a little home-grown code that I hacked together, it runs every 30 minutes and generates an XML...Anonymous
August 01, 2005
Eric Jarvi - VSTS Tip: Branching Source Code
Eric discusses what to do and not do when branching source...Anonymous
August 08, 2005
Recently, I wrote about a Team Foundation RSS feed. There may be a change required if you're seeing ASP.NET...Anonymous
August 09, 2005
I didn't realize this yesterday when I blogged about RSS checkin notifications yesterday, but Jeff has...Anonymous
August 09, 2005
As we go on developing the product we frequently feel strong about doing new things around&nbsp;our product...Anonymous
August 09, 2005
As we go on developing the product we frequently feel strong about doing new things around&nbsp;our product...Anonymous
April 21, 2006
&lt;Update: After some complains that it is difficult to cut paste code out of the blog, I have zipped...Anonymous
May 04, 2006
He encontrado un ejemplo muy interesante sobre como utilizar el API de Team System para construir interesantes...Anonymous
August 09, 2007
<Update: After some complains that it is difficult to cut paste code out of the blog, I have zippedAnonymous
June 24, 2008
Любопытно, но, похоже, пока еще никто не реализовал полноценную подписку на события TFS (eventing service)