The default pages of many intranets today contain corporate news and information. Most of the time, however, the news and information is either manually entered by a web designer or pulled from some type of database that might not easily support the rendering of attachments for news and general information items. Furthermore, frequently a user must e-mail the web designer to add new content to the news and information page.
Using Microsoft Exchange Server Public Folders facilitates the e-mailing of new content for posting to a web page. By using Public Folders, users can e-mail news items with attachments as well as text using any standard mail client. Because Exchange Server supports auto-expiring of items in Public Folders, users can set how long the item should remain on the news site, which saves the web designer time and effort. If the user e-mails the news item using Microsoft Outlook, the user can set the message category type and thus control how the news item will be identified, or categorized, in the Intranet News application. This solution doesn't require any user training because everyone knows how to send an e-mail message!
Using Exchange Server Public Folders also simplifies administrative tasks for the web designer. The designer can easily set up a group of moderators for the content by using the moderation features built into Exchange Server Public Folders. This allows items to be screened and approved before they are published. (See Chapter 4 for more information about moderating Public Folders.) Figure 11-32 shows the Intranet News application in Microsoft Internet Explorer 4.0. Note that this application is written specifically for the Internet Explorer 4.0 browser and the marquee feature it contains. You can, however, add code to detect the browser you use in your organization and employ the correct display mechanisms for that browser type.
Figure 11-32 The Intranet News application in Internet Explorer 4.0. The application scrolls news and information on a corporation's intranet site and pulls the information dynamically from an Exchange Server Public Folder.
Before you can install the application, you must have a Windows NT 4.0 Server and a client with certain software installed. Table 11-4 describes the installation requirements.
Table 11-4 Installation Requirements for the Intranet News Application
Required Software | Installation Notes |
---|---|
Exchange Server 5.5 SP1 with Outlook Web Access | |
IIS 3.0 or higher with Active Server Pages | IIS 4.0 is recommended. |
CDO library (cdo.dll) CDO Rendering library (cdohtml.dll) | Exchange Server 5.5 SP1 installs CDO library 1.21 and CDO Rendering library 1.21. Outlook 98 installs CDO library 1.21. |
For the client: A web browser Outlook 98 | You can run the client software on the same machine or on a separate machine. |
To set up the Intranet News application, you first need to install the application. Copy the Intranet News folder from the companion CD to your web server location where you want to run the application.
Start the IIS administration program. Create a virtual directory that points to the location where you copied the Intranet News files, and name the virtual directory exchnews. Enable Execute permissions for the virtual directory. This allows you to use the following URL to access your intranet news site: http://yourservername/exchnews.
NOTE
If you use a different virtual directory name, you will have to edit the file virtroot.inc accordingly.
Included with the Intranet News files is a file named Exchnews.pst. Make sure the Read-Only flag for this file is unchecked. Launch Outlook and, from the File menu, point to Open and then choose Personal Folders File (.pst). In the Open Personal Folders dialog box, select the Exchnews.pst file, and click OK. In the Outlook Folder List, expand the Exchange Intranet News file folder. While holding the Ctrl key, copy the Exchnews folder to All Public Folders.
NOTE
You must install the Exchnews folder to All Public Folders or the application will not work. If you cannot install the application there, you can modify the code contained in the Intranet News application so that it looks for the folder in another location, or you can retrieve the folder by using its EntryID.
Right-click on the Exchnews public folder and select Properties. Click on the Permissions tab, and give the Anonymous user Read Items permissions. If you want the folder to be moderated, click on the Administration tab and then click the Moderated Folder button. Fill out the information in the Moderated Folder dialog box. Any new items sent to the folder will first be sent to the moderators you select.
To make it easier for you and your users to e-mail information into the Exchnews public folder, you can enable displaying the folder in the Global Address List. To do this, launch the Exchange Administrator program. Expand the Folders tree and then the Public Folders tree. Select the Exchnews public folder. Choose Properties from the File menu, click on the Advanced tab, and uncheck the check box named Hide From Address Book. Click OK.
To enable the Intranet News application to anonymously access the Exchnews folder, expand the Configuration tree for your Exchange Server site while still in the Exchange Administrator program. Select the Protocols icon, and then double-click HTTP (Web) Site Settings in the right pane. Make sure that the check box named Allow Anonymous Users To Access The Anonymous Public Folders is checked. Then click on the Folder Shortcuts tab. Click New, and select the Exchnews Public Folder in the tree. Click OK twice.
You're finished. You can now add news to the Exchnews public folder. Test it from the URL http://yourservername/exchnews.
The Intranet News application uses the anonymous logon capabilities of the CDO library because the application does not require people to authenticate before being able to read the news ticker on the site. The way anonymous access works in the CDO library is that Exchange Server supports a new anonymous user permission feature, which allows you to set the permissions for all users who access the server anonymously. By using this feature, you can set whether anonymous users can create, delete, read, or modify items in folders.
Developers typically forget that when they set up the application, they need to actually publish the folder as an anonymous folder using the Exchange Administrator program. Even though you might give users anonymous access to the folder, the folder will not appear in the anonymous Public Folder hierarchy through CDO. Exchange Server has a very good reason for making you take this explicit step to publish the folder anonymously: you probably do not want anonymous users browsing through your Public Folder hierarchy. Exchange Server keeps a list of the published anonymous folders in the Exchange Server directory so that you can retrieve them by using the CDO Rendering library, as you'll see in the next section.
The Intranet News application logon is different from the previous two logon methods we've seen because it uses anonymous logons. Instead of storing the CDO session in an ASP session variable, as in the Calendar of Events application, the Intranet News application stores the CDO session in the Application object so that you can keep sessions for different users distinct. You want to keep the sessions separate because your users are operating in different security and configuration contexts.
With anonymous access, all users are treated in the same way. They have the same security context and are not identified individually by CDO. Anonymous access assumes you do not care when an anonymous user logs on or logs off, because these processes are not unique to the individual user. Therefore, to increase performance, the application stores a valid anonymous CDO session in the ASP application scope and shares this session across all the users who access the application.
One more issue to consider for anonymous users is that they cannot access folders other than Public Folders. This means that a user cannot access a mailbox when logging in using the anonymous method. Instead, the user is allowed to access only the published list of Public Folders that you set up in the Exchange Server Administrator program.
When a user logs on anonymously, you call the Logon method of the CDO Session object, which is the same method you call to authenticate a user. However, instead of passing in the server name and the mailbox as the profile information, you must pass in the Exchange Server enterprise name, the site name, and the configuration container; the server's container; and the server name. For example, if your Exchange Server organization was CompanyABC, your site was New York, and your server name in that site was Exchange1, the profile information would look like this:
/o=CompanyABC/ou=New York/cn=Configuration/cn=Servers/cn=Exchange1 |
You do not have to hard-code the Exchange Server organization and site information into your applications. Instead the CDO Rendering library allows you to query this information dynamically from the Registry or from the Exchange Server directory service in your application. This query is accomplished by using the LoadConfiguration method on the RenderingApplication object and then calling the ConfigParameter method.
The first thing you must do when logging a user on anonymously is retrieve the enterprise, site, and server name from the Microsoft Windows Registry. You cannot call the LoadConfiguration method with the Exchange Server directory as the source without first retrieving the necessary information from the Registry because CDO must know which directory server to read the requested information from. This step is accomplished in the following code:
Dim objRenderApp Set objRenderApp= Application( "RenderApplication") ' 1 means load configuration from the Registry objRenderApp.LoadConfiguration 1, _ "HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\" & _ MSExchangeWeb\Parameters" If Not ReportError( _ "RenderingApplication.LoadConfiguration from registry") Then bstrEnterprise= objRenderApp.ConfigParameter("Enterprise") bstrSite = objRenderApp.ConfigParameter("Site") bstrServer = objRenderApp.ConfigParameter("Server") End If |
Next you create the profile string, which specifies the anonymous account you the user want to log on as, and create a valid CDO object. Then, you must call the Logon method to actually create an anonymous session with the Exchange Server. The following code implements these steps:
bstrProfileInfo = "/o=" + bstrEnterprise + "/ou=" + bstrSite + _ "/cn=Configuration/cn=Servers/cn=" + bstrServer +_ vbLF + "anon" + vbLF + "anon" Err.Clear Set objAMSession1 = Server.CreateObject("MAPI.Session") If Not ReportError( "create MAPI.Session") Then Set objRenderApp= Application( "RenderApplication") Err.Clear objAMSession1.Logon "", "", False, True, 0, True, bstrProfileInfo |
Now you have an anonymous session with the Exchange Server. However, you must remember to not only store the anonymous session object so that you can share it throughout your ASP application, but also to store the security context handle for the anonymous user session so that when your ASP application ends, CDO can kill the current anonymous sessions cleanly. The following code implements storing the security context handle in an Application scope variable:
If Not ReportError( "Anonymous Logon") Then Set Application("AMAnonSession") = objAMSession1 Application("hImp") = objRenderApp.ImpID |
IIS will call the following code, taken from Global.asa, when ending the ASP application to impersonate the correct security context for the anonymous session, and then it will set the Application variable for the anonymous session to Nothing:
Sub Application_OnEnd Set objRenderApp = Application("RenderApplication") hImp = Application("hImp") If Not IsEmpty(hImp) Then objRenderApp.Impersonate(hImp) End If Set Application("AMAnonFolders")= Nothing Set Application("AMAnonSession")= Nothing Set Application("RenderApplication") = Nothing End Sub |
After logging on, your application should attempt to open the Public Folder store to make sure that the anonymous logon was successful. The easiest way to find the Public Folder store with an anonymous logon is to scroll through the Infostores collection and use the property PR_STORE_SUPPORT_MASK (&H340D0003). This property contains a bitmask of flags that describe the characteristics of an Infostore object. One of these flags, STORE_ PUBLIC_FOLDERS (&H00004000), identifies a Public Folder store. The following code shows you how to use these properties to find the Public Folder store in your anonymous logon:
For i = 1 To objStores.Count Set objStore = objStores.Item(i) ' PR_STORE_SUPPORT_MASK lMask = objStore.Fields.Item(&H340D0003) 'Err.Clear ' STORE_PUBLIC_FOLDERS If lMask And &H00004000 Then Exit For End If Next End If End If |
The next step in the application is to find the Exchnews Public Folder in the published Public Folder list, access the messages the folder contains, and render the messages to the Internet Explorer 4.0 marquee control. To access the Exchnews folder, you first need to retrieve the list of published Public Folders for anonymous users. To do this, you use the same method that we used to access the enterprise and site information, the LoadConfiguration method. This time, however, we pass as a parameter the number 2, which indicates that we want to load the information from the Exchange Server Directory rather than from the Registry. Once CDO loads the information from the directory, we can use the ConfigParameter method to retrieve specific parameters from the Exchange Server directory. One of these parameters is the list of anonymously published Public Folders. This list is returned as a string array of entryIDs for the anonymous Public Folders. The following code implements this process:
' 2 means load configuration from the DS objRenderApp.LoadConfiguration 2, "" If Not ReportError( _ "RenderingApplication.LoadConfiguration from DS") Then amFolders = objRenderApp.ConfigParameter( _ "Published Public Folders") |
We then must add two items to the list of anonymous folders: a dummy folder, which represents the root of all the public folders; and an Infostore object, which represents the Public Folder store. Even though we will not use either of these items in this application, you should do this whenever you are accessing anonymous folders using an anonymous logon. These two items allow the CDO Rendering library to correctly render the anonymous Public Folder information store. If you do not add these items, you might receive an error, or your folder hierarchy might look incorrect when rendered. The code for adding these items takes advantage of dynamic arrays in VBScript, as you can see in this snippet of code:
iFolderCount = UBound(amFolders) ReDim Preserve amFolders(iFolderCount + 2) ' To the list of folders, add two things: ' ...a name for the pseudofolder we're making up amFolders(iFolderCount + 1) = "Public Folders" ' ...and a store interface so that the renderer can get ' stuff from the folders Set objStores = objAMAnonSession.InfoStores For idx = 1 To objStores.Count Set objStore = objStores.Item(idx) ' PR_STORE_SUPPORT_MASK lMask = objStore.Fields.Item(&H340D0003) ' STORE_PUBLIC_FOLDERS If lMask And &H00004000 Then Set amFolders(iFolderCount + 2) = objStore Exit For End If Next Application( "AMAnonFolders")= amFolders End If 'LoadConfiguration |
Now that we have the correct list of items in the array for all the anonymous Public Folders, we need to find the Exchnews Public Folder. Scroll through the array of folder entryIDs, retrieve each folder, and then check the name of the folder against the literal "Exchnews". When we find the Exchnews folder, we should break out of the loop because hundreds or thousands of anonymous Public Folders could be available. As you can see in the following code, when the Exchnews folder is found, the folder is set to an object variable, its Messages collection is retrieved, and, using the Sort method on the Messages collection, the items are sorted in descending order so that the most recent messages are moved to the top of the Internet Explorer marquee control.
If CheckAMAnonSession Then Set objAMAnonSession= Application( "AMAnonSession") End If If CheckAMAnonFolders Then amFolders= Application( "AMAnonFolders") End If For iFolder = LBound( amFolders) to (UBound( amFolders) - 2) ' amFolders is an array of folder IDs. ' Get the FolderID of the exchnews public folder. Set objFolder= objAMAnonSession.GetFolder( amFolders( iFolder), _ NULL) if objFolder.Name = "Exchnews" then exchnewsid = amFolders(iFolder) exit for end if Next set objFolder = objAMAnonSession.GetFolder(exchnewsid,NULL) set objMessages = objFolder.Messages 'Sort the messages descending so that newer messages are at the top objMessages.Sort 2 |
Once we have retrieved and sorted the items in the folder, we need to put the items into the marquee control. Suppose some users want to categorize their news items so that the items can be read in context. For example, the human resources department might want to submit items to the folder that represent different human resources offerings, which can be broken down into categories such as benefits, work and life balance, and training. Human resources might also want to include a banner on the screen before these messages appear to tell users which category the news corresponds to. To implement this sorting, the application uses the Categories property on Outlook messages. A user can assign a single category to a news item, and the application will display the selected category in the marquee control. If the user does not enter a category for the item, the application automatically displays the item as a general news item.
The application automatically detects as newer items any items entered into the news system within seven days of the current date. To highlight all new items in the Public Folder, these items receive a new graphic next to their text.
The next chunk of code implements all of this. To scroll through all news items in the folder, the application uses a For...Each loop on the Messages collection for the folder. The application then checks the Categories property on the item to see whether any categories exist. (Remember that the Categories property is an array of strings, and to access an individual member, you must specify the index using the following syntax: objMessage.Categories()(Index).) The application then checks the date of the message, and if the message was received within the last seven days, the application adds the new graphic to the item. The marquee control also has hyperlinks to the messages. When the user holds the mouse pointer over a hyperlink or holds the mouse button down while the mouse pointer is over the marquee, the control will stop scrolling so that the user does not have to chase the hyperlinks and can read the text.
<TD WIDTH="20%" VALIGN="Top"> <MARQUEE DIRECTION=UP ID="Marquee" BEHAVIOR=SCROLL SCROLLAMOUNT=10 SCROLLDELAY=500 TITLE= "Hold the mouse down or over an item to stop the News Ticker." ONMOUSEDOWN="this.stop();" ONMOUSEUP="this.start();"> <% for each objMessage in objMessages %> <DIV CLASS=big> <% on error resume next strCatName = objMessage.Categories(0)(0) if strCatName = "" then strCatName = "General" end if %> <%=strCatName%> News</DIV> <hr> <a href="details.asp?id=<%=objMessage.ID%>" ONMOUSEOVER ="this.style.textDecorationUnderline=true; document.all['Marquee'].stop()" ONMOUSEOUT="this.style.textDecorationUnderline=false; document.all['Marquee'].start()"> <% if (datediff("d", objMessage.TimeReceived, Date()) <= 7) then %> <img src="newicon.gif" border=0 align="center"> <% end if %> <FONT FACE="VERDANA, ARIAL, HELVETICA" SIZE="2"> <%=objMessage.Subject%> </FONT> </a> <P> <% strCatName = "" next %> |
Every news item scrolled through the marquee has a hyperlink to the file details.asp. The details.asp file allows the user to drill into the specifics of a news item and to see any rich text, attachments, or hyperlinks that the author of a news item entered into the Outlook message sent to the Exchnews Public Folder. This information is rendered to the browser by using the CDO Rendering library. An example of a details page for an item is shown in Figure 11-33. Compare it to the same details page presented as an Outlook message, shown in Figure 11-34.
Figure 11-33 The details of the intranet news item include rich text, hyperlinks, and attachments.
Figure 11-34 The item in Figure 11-33, shown as an Outlook message. Notice how the web and Outlook versions look almost identical. This is due to the CDO Rendering library's automatic conversion of rich text to HTML.
The Events Calendar and Intranet News applications use similar code to render information to the web user, but the Intranet News application uses the CDO Rendering library in a slightly different way. In the Events Calendar application, the HTML generated by the CDO Rendering library is added to the Response object of the ASP object model. In the Intranet News application, the HTML produced is not added to the Response object but rather is placed into a string so that the application can modify the HTML before it is presented to the user. This modification replaces the generic paper-clip icon that CDO automatically renders for all attachments with the specific application icons for Microsoft Office products. You will see how this functionality is achieved a little later.
Before attempting to render the details of the news item to the browser, we first need to change the virtual root of the Rendering application. If we do not change this root, all virtual roots in the rendered hyperlinks will point to the /Exchange virtual root. Then we have to create an object renderer, because we will be rendering two specific properties on the item: the subject and the message body. The following code shows you how to accomplish these tasks:
'Change the virtual root for the rendering application Set objRenderApp = Application("RenderApplication") objRenderApp.VirtualRoot = virtroot 'Create an Object Renderer set objObjRenderer = objRenderApp.CreateRenderer(2) objObjRenderer.DataSource = objMessage |
To create the page, we have to render the rich-text message body into a string. To do this, instead of passing a Response object to the RenderProperty method, we set a string variable equal to the RenderProperty method, as shown here:
'Render the HTML into a string strHTML = objObjRenderer.RenderProperty(ActMsgPR_RTF_COMPRESSED, 0) |
Now that we have the HTML that the CDO Rendering library would normally display in the browser, we need to check to see whether the message has any attachments. If it does, then we need to scroll through the HTML and change the image source to point to the Microsoft Word, the Microsoft Excel, or the Microsoft PowerPoint icons instead of the generic paper-clip icons. The following code shows how to check for attachments in the message by using the Attachments collection and Count property:
set oAttachments = objMessage.Attachments intAttachCount = oAttachments.Count if intAttachCount > 0 then 'Need to find any Office documents by using the extensions |
If there are attachments, we need to scroll through the attachments to determine what type of document they are. This is where the code gets into manipulating strings, and the degree to which it's confusing depends on how well you know the string functions in VBScript! I built this code so that you can add your custom extension and image types to it, which enables documents to be displayed with their specific icons rather than with generic icons. If the code does not find the text for the application in the document, it leaves the generic icon. Following is the code for replacing the images in the message text of an item for Word documents. The code for PowerPoint and Excel attachments is very similar and can be found in the code on the companion CD:
'Find all the Word docs found = 1 Do while (found <> 0 or found <> Null) found = instr(found, strHTML, ".doc</A>") if found <> 0 then strIcon = "Iword.gif" revfound = instrrev(strHTML, "generic.gif", found) newstrHTML = Replace(strHTML, "generic.gif", strIcon, _ revfound,1) origstrHTML = Left(strHTML, revfound-1) strHTML = origstrHTML & newstrHTML found = found + 1 end if Loop |
This code sets a variable named found equal to 1. The variable is used as the starting point for the string and also as a Boolean for the Do…While loop, which parses the string. The Do…While loop searches through the string until no .doc extensions representing Word documents are found or until the InStr function returns a Null value, which would indicate that the source or string being searched for is Null—in other words, some weird condition has occurred in string processing. When searching through the string, the application knows that the CDO Rendering library always follows the .doc extension with an ending hyperlink tag. Adding </A> to the search string almost guarantees that the search will not return random .doc strings in the text of the message.
If the application finds a location where the .doc</A> string occurs, it uses the InStrRev VBScript function to perform a reverse lookup from the location of .doc</A> back through the string to the Word documents corresponding to the generic paper-clip icon. (The CDO Rendering library will always use the generic.gif image for attachments, because this image is hard-coded for use in the CDO code.) The code then uses the Replace function of VBScript and replaces generic.gif with the Word icon. The final parameter for the Replace function, 1, tells the code to replace only one instance of generic.gif in the string. This stops VBScript from going through the entire string and replacing all references to generic.gif.
You might be wondering why the code then takes the leftmost portion of the string up to the point where the new image string was replaced. The reason is that the Replace function does not return the entire string after making the replacements. Rather, this function returns from the point where the replacement started to the end of the string. This means that our HTML string is now missing its entire left-hand portion up to the point where we replaced the image. For this reason, the code combines the return value from the Replace function with the return value from the Left function to re-create the original string with our new replacement. Then the code increments the found variable so that we do not enter into an infinite loop, finding the same .doc extension at the same point in the string.
To render the final HTML string that we create, the application calls the Write method on the Response ASP object to send the string as HTML to the browser. The code also uses the RenderProperty method of the CDO Rendering library to display the subject of the news item from the message, as shown in the following code:
<td width="980" height="422" valign="top" rowspan="3" align="center"> <p><h1><B>From <%=Request.QueryString("cat")%> News: </B> <!-- Render the Subject --> <I><%objObjRenderer.RenderProperty ActMsgPR_SUBJECT, 0, Response%> </I></h1></p> <B><U>Details:<P></U></B> <!-- Render the body with our replacements --> <%response.write strHTML%> |