Tutorial - generate RSS feed in VB.Net.
In this tutorial, we will create a feed that displays all activities created by other users on the current user’s contact. A feed displays information. Each item in the feed includes metadata about that item.
The example handles user authentication with forms authentication mode with a custom login page. Based on the user credentials provided, the link to the RSS feed is generated.
The second half explains how to generate the XML contents with the relevant activity information. How the formatting of the feed contents is handled will be addressed next, followed by an explanation of how to configure the feed in Outlook Express 2007 enabling Feed subscription through Outlook Express 2007.
The code example uses the SuperOffice.CRM.ArchiveLists.ActivityArchiveProvider to retrieve the activities registered by another user on the current user's contact.
RSS (Really Simple Syndication) is a web content syndication format. As specified in RSS 2.0 specification, "RSS is a dialect of XML. All RSS files must conform to the XML 1.0 specification, as published on the World Wide Web Consortium (W3C) website."
The format for the RSS Feed is predefined with a set of required and optional elements. The generated feed conforms to this format.
Here is an example of how the base RSS feed template appears.
<?xml-stylesheet type="text/xsl" href="rss.xsl" media="screen"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
<title>SuperOffice Feed VB</title>
<link><%= _url %></link>
<description><%= _description %></description>
<language>en-us</language>
<copyright>Copyright 2006 SuperOffice</copyright>
<pubDate><%= DateTime.Now.ToUniversalTime().ToString("R") %></pubDate>
<generator>Late Night SuperOffice Hacker</generator>
<%= _items %>
</channel>
</rss>
An XML stylesheet formats how the feed is displayed. The stylesheet is referenced on the first line in the example above and will be discussed later.
The generated feed link uses the user credentials along with the user preference for the number of items displayed in Feed contents is read and appended to the feed URL.
Then the logged-on user information is validated with the use of Authenticate method of SOSession. The user password has to be encrypted since it's sent along with the URL in plain text format. The support method Rot13 is used for this data conversion as shown in the following code segment.
''' <summary>
''' generates the link for the RSS feed
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
Protected Sub btnGenerateLink_Click(ByVal sender As Object, ByVal e As EventArgs)
Try
' read user credentials
Dim user As String = Server.UrlEncode(Me.txtUName.Text)
Dim pass As String = Server.UrlEncode(Rot13(Me.txtPwd.Text))
' read maximum number of items to be displayed for the feed contents
Dim num As String = Me.ddlMaxValue.SelectedValue
' generate the feed URL
Dim path As String = "http://" + "localhost:50804" + "" + Request.ApplicationPath
Dim url As String = String.Format("{0}/Feed.aspx?user={1}&pass={2}&size={3}", path, user, pass, num)
' authenticate the user with the credentials provided
Dim session As SoSession = SoSession.Authenticate(user, Me.txtPwd.Text)
If session Is Nothing Then
Me.lnkURL.NavigateUrl = ""
Me.lnkURL.Text = "Login failed - please try again"
Else
' set the link
Me.lnkURL.NavigateUrl = url
Me.lnkURL.Text = url
_url = url
' kill the session
session.Close()
session.Dispose()
End If
Catch exception As Exception
Me.lblMessage.Text = exception.Message
End Try
End Sub
''' <summary>
''' Modify the character code of the character
''' </summary>
''' <param name="text"></param>
''' <returns>the modified text</returns>
Public Shared Function Rot13(ByVal text As String) As String
Dim res As String = ""
Dim chars As Char() = text.ToCharArray()
For i As Integer = 0 To chars.Length - 1
Dim curChar As Integer = Microsoft.VisualBasic.AscW(chars(i))
'Modify the character code of the character, - this
'so that "a" becomes "n", "z" becomes "m", "N" becomes "Y" and so on
If curChar >= 97 AndAlso curChar <= 109 Then
curChar = curChar + 13
ElseIf curChar >= 110 AndAlso curChar <= 122 Then
curChar = curChar - 13
ElseIf curChar >= 65 AndAlso curChar <= 77 Then
curChar = curChar + 13
ElseIf curChar >= 78 AndAlso curChar <= 90 Then
curChar = curChar - 13
End If
'Add the current character to the string to be returned
res += Microsoft.VisualBasic.ChrW(curChar)
Next
Return res
End Function
Let's take a look at how the activity is generated and passed to the feed.
Imports SuperOffice
Imports SuperOffice.CRM.Services
Imports System.Collections.Generic
Imports System.Collections.Specialized
Imports SuperOffice.CRM.ArchiveLists
Imports SuperOffice.CRM.Archives
Imports SuperOffice.CRM.Globalization
' Some code left out for brevity
Public _pubDate As DateTime = DateTime.MinValue
Public _url As String = ""
Public _items As String = ""
Public _description As String = "All activities that other users have recently registered on your contacts in SuperOffice."
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
' validate the session
Dim session As SoSession = Me.validateSession()
If session IsNot Nothing Then
' set the url value for the feed
Dim path As String = "http://" + "localhost:50804" + "" + Request.ApplicationPath
_url = String.Format("{0}/default.aspx", path)
' - - - - - - - - - - - - - - - - - - - - - -
' SECTION 1 : Retrieve Activity information
' - - - - - - - - - - - - - - - - - - - - - -
' Setting the Parameters that needs to be passed to Agent method and retrieve activity information
' Parameter - providerName - The name of the archive provider to use
Dim providerName As String = ActivityArchiveProvider.ProviderName
'Parameter - columns - An array of the names of the columns wanted.
Dim columns As String() = New String() {"date", "saleId", "appointmentId", "documentId", "sale/description", "appointment/description", _
"document/description", "project/name", "contact/name", "associateId", "type", "contact/department", _
"contactId"}
'Parameter - restriction - Archive restrictions
Dim associateRestriction As New ArchiveRestrictionInfo("associateId", "associateIsNotOneOf", SoContext.CurrentPrincipal.AssociateId)
Dim registeredUserRestriction As New ArchiveRestrictionInfo("contact/associateId", "currentAssociate", SoContext.CurrentPrincipal.AssociateId)
Dim restrictions As ArchiveRestrictionInfo() = New ArchiveRestrictionInfo(1) {}
restrictions(0) = associateRestriction
restrictions(1) = registeredUserRestriction
'Parameter - sortOrder - Sort order for the archive
Dim archiveSrtOrd As ArchiveOrderByInfo() = New ArchiveOrderByInfo(0) {}
archiveSrtOrd(0) = New ArchiveOrderByInfo("date", SuperOffice.Util.OrderBySortType.DESC)
'Parameter - entities – which entities to be included
Dim entities As String() = New String() {"document", "appointment", "sale"}
'Parameter - page - Page number, page 0 is the first page
'Parameter - pageSize - Page size
Dim page As Integer = 0
Dim pageSize As Integer = 500
' Create an ArchiveAgent object
Dim newActivity As IArchiveAgent = AgentFactory.GetArchiveAgent()
' Call the get ‘GetArchiveListByColumns’ method to retrieve the specified records
Dim activitytItems As ArchiveListItem() = _
newActivity.GetArchiveListByColumns(providerName, _
columns, archiveSrtOrd, restrictions, entities, page, _
pageSize)
Private Function validateSession() As SoSession
' read the query string values
Dim user As String = Request("user")
Dim secret As String = Request("pass")
Dim maxnum As String = Request("size")
' get the secret password value secret = Rot13(secret)
' read the maximum number of records displayed at a time
Dim max As Integer = 5
If maxnum IsNot Nothing Then
Integer.TryParse(maxnum, max)
End If
' validate the session
Dim session As SoSession = Nothing
If session Is Nothing AndAlso user IsNot Nothing AndAlso secret IsNot Nothing Then
session = SoSession.Authenticate(user, secret)
End If
Return session
End Function
Section 1 shows how the parameters required by GetArchiveListByColumns
are created (the columns to be included in the selection). It also defines the search restrictions and the entities to be included in the search.
Next, an IArchiveAgent
object is retrieved using the AgentFactory
. GetArchiveListByColumns
method is then invoked to get the activity information.
' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
' SECTION 2 : Generate the feed XML with retrieved activity data
' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
' iterate over the activity collection
For Each listItem As ArchiveListItem In activitytItems
Dim lstActivityInfo As New ListDictionary()
Dim id As String = Nothing
Dim description As String = Nothing
Dim activityMainType As String = Nothing
Dim contactId As String = Nothing
Dim link As String = "http://localhost/SuperOfficeWebUpdatable/default.aspx?superoffice:contact.activityarchive."
Dim newDate As DateTime = DateTime.MinValue
' retrieve the column names and the data values for each ArchiveListItem and store in the ListDictionary
For Each column As KeyValuePair(Of String, ArchiveColumnData) In listItem.ColumnData
Dim displayValue As String = "-"
If (column.Value IsNot Nothing) Then
displayValue = column.Value.DisplayValue.ToString()
End If
Dim key As String = column.Key
If (key = "date") AndAlso (displayValue IsNot Nothing) Then
Dim tempDate As String = displayValue.Substring(3, 10)
newDate = DateTime.Parse(tempDate)
displayValue = newDate.ToUniversalTime().ToString("yyyyMMdd-T-HHmmss-Z")
End If
lstActivityInfo.Add(key, displayValue)
Next
contactId = lstActivityInfo("contactId").ToString()
' activity type : Sale
If listItem.EntityName = "sale" Then
id = lstActivityInfo("saleId").ToString()
description = lstActivityInfo("sale/description").ToString()
activityMainType = "Sale"
link += "sale?contact_id=" + Me.activityId(contactId) + "&sale_id=" + Me.activityId(id)
End If
' activity type : Appointment
If listItem.EntityName = "appointment" Then
id = lstActivityInfo("appointmentId").ToString()
description = lstActivityInfo("appointment/description").ToString()
activityMainType = "Appointment"
link += "appointment?contact_id=" + Me.activityId(contactId) + "&appointment_id=" + Me.activityId(id)
End If
' activity type : Document
If listItem.EntityName = "document" Then
id = lstActivityInfo("documentId").ToString()
description = lstActivityInfo("document/description").ToString()
activityMainType = "Document"
link += "document?contact_id=" + Me.activityId(contactId) + "&document_id=" + Me.activityId(id)
End If
' generate the <item> information of the feed XML (values for <title>,<link>,<description>,<pubDate> and <dc:creator> components are set here)
Dim item As String = ""
item += "<item>"
item += "<title>"
item += Server.mdEncode(activityMainType + ": " + lstActivityInfo("contact/name").ToString())
item += "</title>"
item += "<guid isPermaLink='false'>"
item += "guid.SuperOfficeASA.appointid." + id
item += "</guid>"
item += "<link>"
item += Server.mdEncode(link)
item += "</link>"
item += "<description>"
Dim desc As String = ""
desc += "** Summary : ** " + lstActivityInfo("associateId").ToString() + " registered an activity of type **" + lstActivityInfo("type").ToString() + "** on " + lstActivityInfo("contact/name").ToString() + ", " + lstActivityInfo("contact/department").ToString() + "<p>"
desc += "** Project :** " + lstActivityInfo("project/name").ToString() + "<br/>"
desc += "** On :** " + newDate.ToUniversalTime().ToString("R") + "<br/>"
desc += "** Activity description :** " + description + "<br/>"
desc += "</p>"
item += Server.mdEncode(desc)
item += "</description>"
item += "<pubDate>"
item += newDate.ToString("f")
item += "</pubDate>"
item += "<dc:creator>"
item += "Creator :" + lstActivityInfo("associateId").ToString()
item += "</dc:creator>"
item += "</item>"
_items += item
lstActivityInfo.Clear()
Next
session.Close()
session.Dispose()
Else
' validation fails: redirect to login page
Response.Redirect("~/Default.aspx")
End If
End Sub
''' <summary>
''' format the identity values dat has the format [I:XXX]
''' </summary>
''' <param name="originalId"></param>
''' <returns></returns>
Private Function activityId(ByVal originalId As String) As String
Dim index As Integer = originalId.IndexOf("]"c)
Dim temString As String = originalId.Remove(index)
Return temString.Substring(3).Trim()
End Function
Section 2 demonstrates how to, using the query results, generate the RSS feed. Here, we have chosen to iterates over the retrieved ArchiveListItem
collection. It extracts data values for each ArchiveListItem
and stores those in a ListDictionary
.
The ListDictionary
contains details of one activity at a time. Then the following item
sub-element values are set based on data in the ListDictionary
:
- title
- link
- description
- pubDate
- dc:creator
As shown in the feed code segment, this is the data referred to by the <%= _items %>
data-binding expression.
Internet Explorer 7+ and Firefox 2+ browsers have the integrated RSS reading capability. Previous versions of these browsers display RSS as plain XML. To address this issue, we use XSLT style sheets to control how the Feed will be rendered in a browser. The XML stylesheet will be ignored depending on whether the RSS is recognized as an XML document or as an RSS feed by a browser.
XSLT style sheet:
<div id="Content">
<h1><xsl:value-of select="rss/channel/title"/></h1>
<ol id="ItemList">
<xsl:for-each select="rss/channel/item">
<li class="ItemListItem">
<h1>
<a><xsl:attribute name="href"><xsl:value-of select="link"/></a>
</h1>
<div class="ItemListItemDetails">
Published <xsl:value-of select="pubDate"/> by <xsl:value-of select="dc:creator" />
</div>
</li>
</xsl:for-each>
</ol>
</div>
Configure the feed in Outlook
RSS feeds can be subscribed through Microsoft Office Outlook 2007. The RSS subscriptions are kept in a separate folder along with the mail folders in Outlook 2007. Once subscribed, RSS feeds can be read the same way emails are read. When you click a particular feed all the posts appear in the same format as regular email in the mail reader pane.
To subscribe to RSS in Outlook 2007:
- Go to Tools, then Account Settings.
- Click RSS Feeds tab and Click New to add a new RSS Feed.
- Enter the URL of the feed as shown below.
Now you can see a folder under RSS Feeds main folder with the name of the feed.
General settings
For the application to run, some modifications are required in the web configuration file. The following section illustrates the modifications required in the authentication section and the database section of the configuration file.
<authentication mode="Forms">
<forms name="RSSFeedFinalVB" loginUrl="~/Default.aspx" protection="All" timeout="30" path="/"></forms>
</authentication>
<sessionState mode="InProc"
stateConnectionString="tcpip=127.0.0.1:42424"
stateNetworkTimeout="10"
sqlConnectionString="data source=127.0.0.1;Integrated Security=SSPI"
cookieless="false"
timeout="40"/>
<authorization>
<!--
<deny users="?"/>
<allow users="[comma separated list of users]"
roles="[comma separated list of roles]" />
<deny users="[comma separated list of users]"
roles="[comma separated list of roles]" />
-->
</authorization>
<Database>
<add key="DatabaseMajor" value="MSSQL"/>
<add key="DatabaseMinor" value="8"/>
<add key="Server" value="ECCOLVISPHE"/>
<add key="Database" value="SuperOffice"/>
<add key="CommandTimeOut" value="300"/>
<add key="TablePrefix" value="CRM5"/>
<add key="ConnectionString" value="Server=[@Server];Database=[@Database];User ID=[@User];Password=[@Password]"/>
</Database>
The authentication mode is set to Forms, to enable custom user authentication. Here, the loginUrl
points to the application's custom login page.
Further, the Database information has to be modified.
Add references
To run the sample code, after we have updated the web.config file, we have to add the following references:
- SOCore.dll
- SODataBase.dll
- SuperOffice.Legacy.dll
- SuperOffice.Services.dll
- SuperOffice.Services.Impl.dll
Feed content shown in a browser before subscribed:
Once subscribed to the feed it becomes available in the folder specified by the user.
By clicking on the link in the feed contents, the appropriate dialog in SuperOffice CRM Web is opened: