Programming Microsoft Outlook and Microsoft Exchange, Second Edition (DV-MPS Programming)

[Previous] [Next]

Site Server provides some powerful indexing and search capabilities. However, Site Server does not provide a targeted way for users to search the information contained in its catalogs. Fortunately, Site Server has an object model that you can use to build search applications that allow users to target the information in their searches. We'll take a look at this object model and the different user interfaces from which you can use it to allow users to search Exchange Server information.

Site Server Search Object Model

Site Server includes an object model that makes building your search applications easier. This object model consists of two objects: Query and Util. The ProgID for the Query object is MSSearch.Query; for the Util object, it's MSSearch.Util. While the Util object library is interesting, only a few of its methods are useful for Exchange Server developers. Therefore, we won't look at the Util object library; instead, we'll focus on the Query object library because this is primarily the one you'll use to build search applications. Figure 17-5 shows the object model for the Query object library.

Figure 17-5. The Query object library with methods and properties.

Query Object Model

Using the Query object model, you can create powerful search applications. You use the Query object model to create and execute queries against a Site Server catalog. This object model extends the ADO object model, providing a mechanism by which you can count, retrieve, and view the results of your queries.

Let's take a look at the properties of the Query object.

Now let's look at the methods of the Query object:

ADO Recordset Extensions

In addition to the standard methods of the Recordset object, such as Move, and the properties of the Recordset object, such as BOF and EOF, Site Server adds some extensions that are useful for search applications. However, Site Server does not support the full range of ADO Recordset methods and properties. Table 17-1 lists which methods and properties are supported and unsupported.

Table 17-1. ADO Recordset methods and properties supported and unsupported by Site Server.

Supported Unsupported
Methods Close, GetRows, Move (forward-only), MoveNext, Supports AddNew, CancelBatch, CancelUpdate, Clone, Delete, MoveFirst, MoveLast, MovePrevious, NextRecordset, Open, Requery, Resync, Update, UpdateBatch
Properties BOF, CacheSize, CursorType, EOF, Filter, MaxRecords, Source, Status ActiveConnection, AbsolutePosition, AbsolutePage, Bookmark, EditMode, LockType, PageCount, PageSize, RecordCount

Site Server adds five extended properties to the Recordset object, and these are described in the following list. To use these properties, use the following syntax: Recordsetobj.Properties("ExtendedProperty").

Building an ASP Search Application

The most common way that developers build search pages for Site Server is by using ASP applications. This allows you to provide your search application with a Web interface while taking advantage of the Site Server object model's easy-to-use, Webbased methods. We'll take a look at leveraging public folders for a knowledge base application and providing a rich search of this knowledge base using Site Server.

Before we dive into the code, I'd like to point out a few things about the sample application. This application includes a custom Outlook form for submitting information into the knowledge base and for reading information from the knowledge base. Figure 17-6 shows this form.

To make it easy for Web users to work with the knowledge base application, the form is also converted into an ASP-based form using the Outlook HTML Form Converter. This makes it possible for non-Outlook users to view the knowledge base articles. Figure 17-7 shows this version of the form.

The knowledge base form in Figure 17-6 and Figure 17-7 implements some custom Outlook fields such as Category, which specifies the type of information contained in the knowledge base article—for instance, best practice, idea, issue, or resolution. The form also uses a field called Product, which specifies the product called out in the knowledge base article—for example, Exchange Server, Outlook, or Office. Finally, the form uses a field called Industry, which specifies the industry that the knowledge base article refers to. You'll see how Site Server allows you to search these custom properties with minimal effort. Figure 17-8 shows the ASP solution for this search.

Figure 17-6. The Outlook form for the knowledge base application.

Figure 17-7. The HTML version of the Outlook knowledge base form.

Figure 17-8. The ASP solution that allows you to search the knowledge base application.

Conventional Query String Variables

When building custom search applications, you first need to look at Site Server's capabilities for conventional query string variables, which are two-letter tags that you can specify along with a Web address. From these two-letter tags, Site Server can create and populate query variables used in searching catalogs. Even though these variables are targeted toward Web search pages, you can leverage them in other applications, as you'll see momentarily.

The conventional query string variables are designed to identify the property you want to query and the value to use for the query criteria. You can also use these two-letter tags to specify properties on the Query object. For example, the "ct" tag identifies the catalog to use for the search. In the "qn" tag, q specifies the conventional query string variable and n specifies a number. You should use the "qn" tag in conjunction with the "cn" tag because the "cn" tag specifies the column you want to search for the "qn" value. You should use the "on" tag only with numeric and date columns. Here is an example query string from a typical search:

q1=&c2=@DocAuthor&q2=&c3=@filewrite&o3=%3E%3D&q3=1y&c5=

@MessageEmailAddress&q5=&o4=@DocTitle&q4=&c6=@MessageFolder&q6=

&ct=Discussion&c7=@META_Type&q7=&c8=@META_Industry&q8=&c9=

@META_Product&q9=

Table 17-2 specifies the different values you can have for these query string tags; each corresponds to a Query object property. In addition to tags that correspond to Query object properties, this table also includes query string tags that correspond to multicolumn searches. This is where the "qn/cn" tags come into play. If you use the "qu" tag to set the Query property on the Query object, you should not use the multicolumn search tags as well.

Table 17-2. Tags for conventional query string variables.

Tag Description
"ae" Allows enumeration. If you pass a nonzero digit, enumeration will be allowed for the query.
"ct" Identifies the catalogs to search. You should pass a string specifying catalogs separated by commas for multiple catalog searches.
"mh" Specifies the maximum number of hits. By setting this to 0, you specify that all records should be returned.
"op" Indicates what to optimize for. The value x specifies performance, r specifies recall, and an optional h specifies no hitcount.
"ou" Specifies the full query that you want to perform. For example, you could pass "( @FileWrite < 1999/11/22 )" using this tag to search for all items written before 11/22/1999.
"sh" Specifies the starting result number for each catalog.
"sd" Associated with the SortBy property on the Query object. Sorts in descending order—for example, sd=rank.
"so" Sorts in ascending order, for example, so=rank.
"cn" Specifies a column that you want in your query. If you leave this blank but specify a corresponding "qn" tag, Site Server will assume that you want to query the contents of the full-text index for the catalog. You should precede your column names with an at sign (@) when using this tag—for example, @FileWrite.
"on" Operator that specifies which comparison operator you want to use. You should use this tag for numeric and date queries only. The valid operators for this tag are =, !=, >, >=, <, and <=.
"qn" The query term you want to search for. If you specify a "qn" tag without a "cn" or an "on" tag, Site Server will search the full-text contents for this query term.

Using Operators with Date Values

Site Server provides the ability to perform searches on dates using both relative and absolute values. Relative dates express the current date and time. You express these dates using the minus sign (-) and an integer unit followed by a time unit. The time units can be years (y), months (m), weeks (w), days (d), hours (h), minutes (n), and seconds (s). For relative dates, you can use the operators less than (<), greater than (>), less than or equal to (<=), or greater than or equal to (>=). An example of a relative date query is @FileWrite >= -2y. This query would return any documents that were last saved on a date greater than or equal to the current date/time two years ago. Relative date queries are probably the most common queries that you'll perform.

Site Server also supports absolute date queries. When using this type of query, you must specify a date in one of these formats:

You might not want to use absolute date queries because of this formatting and because Site Server assumes the date you pass in is relative to the Universal Time Coordinate (UTC), formerly Greenwich Mean Time (GMT). Your users might get confused if they enter a specific date and values they didn't expect to meet that date are returned because of time zone differences.

Note that the equal sign (=) should not be used with absolute dates. Site Server will try to perform an exact match on the date and time, including milliseconds, for your documents. Instead, if you need to target a specific time period, use less than (<) and greater than (>). For example, to show all documents saved on November 25, 1999, you would use the following query syntax:

@FileWrite < 1999/11/26 AND > 1999/11/24

Exchange Server Property Set

Before you start building a search application, you need to understand which Exchange Server properties Site Server can index. By default, Site Server indexes only five Exchange Server-specific properties and maps a number of its built-in properties to Exchange Server values. Let's take a look at the Exchange Server property set in Site Server and see how to search custom Exchange Server properties.

The five Site Server properties that directly support Exchange Server are MessageClass, MessageDisplayName, MessageDisplayCC, MessageFolderName, and MessageFolderPath. All these properties are indexed, but the only one that is retrievable as a column, by default, is MessageFolderName. Although you can query on the other properties, you can't retrieve them for display in your resultset. Table 17-3 describes each of these properties in more detail.

Table 17-3. Built-in Exchange Server properties.

NameDescription
MessageClass Message class of the item. This can be IPM.Note, IPM.Post, or any valid Exchange Server message class. This property is useful to query on if you want to limit your search to only messages, posts, documents, or other types of items stored in an Exchange Server public folder.
MessageDisplayName Display name of the recipient. For posts, you might want to use MessageFolderName instead to allow users to search by the folder the item was posted into.
MessageDisplayCC Display name of any carbon copy recipients.
MessageFolderName Name of the folder the message was posted into.
MessageFolderPath Path to the folder that the message was posted into. You might want to specify a wildcard to search for items posted into a particular section of your public folder hierarchy.

The built-in Site Server properties shown in Table 17-4 are mapped to Exchange Server values for Exchange Server data sources. These properties are indexed and retrievable by Site Server.

Table 17-4. Built-in Site Server properties.

NameDescription
DocTitle Subject of the message.
DocAddress URL of the message. This property leverages the Outlook Web Access server name you put into the Site Server administration program.
Description First 300 characters of the message. You can use this property to provide an autopreview feature for your search applications.

Free Documents in Public Folders

Besides supporting built-in Exchange Server properties, you can have Site Server index Office document properties of the free documents in public folders. These properties include the author and category properties. Note that in order to provide Office document support for documents stored in Exchange Server, you need to have Site Server Service Pack 3 installed on a computer running Site Server.

Exchange Server and Outlook Custom Properties

Site Server also supports automatic searching on Exchange Server or Outlook custom properties. You don't have to specify custom properties to search on them; Site Server will automatically provide this capability to your search application. Site Server uses the HTML META property set for searching on these properties. To use the META property set, you must preface the name of your custom property with a @META_propertyname. For example, if your custom property in Exchange Server is named Username, to search on that property for a value of Tom, you would specify in your query @META_Username Tom. If your Exchange Server custom property contains spaces, wrap your META tag in quotes, as in "@META_User name".

By default, Site Server indexes string and date properties but not numbers. Furthermore, these properties are not retrievable. This means you can search using custom properties, but you cannot display the values contained in those properties as part of the results returned in the ADO recordset. Your best bet when using Site Server and Exchange Server together is to try to use only string properties wherever possible because you'll have to modify the definecolumns.txt file to search date properties.

When using custom properties, you can also tag HTML documents with the same display name for your custom properties using the HTML META property format. This allows you to use the META property search capabilities across HTML and Exchange Server documents in your Site Server searches. You'll see how to use the capabilities of Site Server and Exchange Server custom properties later in this chapter.

NOTE


You can find a list of all Site Server columns in the Site Server help file included on the companion CD.

Security Requirements for the ASP Search Application

When setting up your virtual directory to allow searches against Exchange Server information, you must authenticate the user before Site Server can return information to the browser. This authentication is required so that the search doesn't return information that a particular user shouldn't see. Because of this authentication requirement, you should disable anonymous access to the virtual directory where you place your search pages and require Basic or Windows NT Challenge/Response authentication, or you should add code to your search page to authenticate the user directly in Microsoft Visual Basic Scripting Edition (VBScript) or JavaScript code.

If you don't authenticate the user in your code or through the directory security in Microsoft Internet Information Services (IIS), such a user will be able to search and view any Exchange Server information contained in the Site Server catalog that anonymous users are allowed to view. The only problem you might run into would be when search results are returned to the user and that user tries to browse those search results using Outlook Web Access. If you haven't added the particular public folder where that search result resides into the anonymous folders in the Exchange Server administration program, the user won't be able to see the results.

Getting Input from the User

The first section of search.asp processes existing input from the user. Since the application divides the search results into groups of 10 hits, it uses session variables in ASP to remember the criteria the user entered for his search across multiple search result pages. If you customize this application to search on additional properties, be sure to modify this section so that when the user clicks to see more search results, the search criteria are not lost at the top of the HTML page. The code that follows gathers the input from the user and sets the ASP session variables appropriately if the page being displayed is a More Results page of a query:

<% 'Process Input Section %> <% if Request.QueryString <> "" then %> <% 'Initialize or set nonpredefined variables 'from information posted to page 'Purpose of DisplayText variable is to have only the words from 'the query for display in the introductory sentence and in the More 'Results link if Request("DisplayText")= "" then DisplayText= Request("q1") else DisplayText = Request("DisplayText") end if 'Purpose of RecordNum variable is to display which records 'are displayed on each results page if Request("RecordNum") = "" then RecordNum=1 else RecordNum=Request("RecordNum") end if 'Purpose of session variables is to store information for use in 'editing a search. Only information from the initial query is stored: 'these variables are not updated on More Results pages. 'If you add search criteria in your search page, make sure 'to add their initial values to the Session object. 'Server.HTMLEncode is required to store queries with double quotes. if Request("sh") = "" then Session("q1")=Server.HTMLEncode(Request("q1")) Session("q2")=Request("q2") Session("ct")=Request("ct") Session("c3")=Request("c3") Session("o3")=Request("o3") Session("q3")=Request("q3") Session("o4")=Request("o4") Session("q4")=Request("q4") Session("c5")=Request("c5") Session("q5")=Request("q5") Session("c6")=Request("c6") Session("q6")=Request("q6") Session("c7")=Request("c7") Session("q7")=Request("q7") Session("c8")=Request("c8") Session("q8")=Request("q8") Session("c9")=Request("c9") Session("q9")=Request("q9") end if 'Read session object into variables q1=Session("q1") q2=Session("q2") ct=Session("ct") c3=Session("c3") o3=Session("o3") q3=Session("q3") o4=Session("o4") q4=Session("q4") c5=Session("c5") q5=Session("q5") c6=Session("c6") q6=Session("q6") c7=Session("c7") q7=Session("q7") c8=Session("c8") q8=Session("q8") c9=Session("c9") q9=Session("q9") %> <% end if %>

The second section of the search.asp file is the search initiation section. This section displays the form in which the user can specify the criteria for the search. Notice that the inputs on the form are assigned specific names. This allows the ASP page to leverage the conventional query string variables. Be aware that Site Server supports only nine columns for the conventional query string variables. So don't try adding a c10/q10 combination to the ASP search page. It won't work.

You can see the use of the HTML META property set in this next section of code. The search.asp file allows users to search using three custom properties from the knowledge base application. You can also see the use of the Exchange Server property set.

<% 'Search Initiation Section 'Show the search form. If the search is being edited, read 'the information that has been previously stored in the 'Session object. %> <p> <table cellpadding=5 border=0> <form action="Search.asp" method="get"> <tr> <td nowrap><font size=2> <% L_QueryLabel_text = "Search for:" %> <% = L_QueryLabel_Text %>&nbsp; </font> </td> <td> <input type="hidden" name="c1" value="@All"> <input type="text" name="q1" size="25" maxlength="100" value="<% = q1 %>"> </td> <td width=5></td> <td> <% 'Author L_AuthorLabel_text = "Sent by" %> <font size=2><% = L_AuthorLabel_text %>:</font> </td> <td> <input type="hidden" name="c2" value="@DocAuthor"> <input type="text" maxlength=100 size=25 name="q2" value="<% = q2 %>"> </td></tr> <tr> <td> <font size=2> Sent in the last: </font> </td> <td> <input type=hidden name="c3" value="@filewrite"> <input type=hidden name="o3" value=">="> <select name="q3"> <option value="" <%if Request("q3")="" then %>selected<%end if%> > <option value="-1d" <%if q3="-1d" then %>selected<%end if%> >day <option value="-1w" <%if q3="-1w" then %>selected<%end if%> >week <option value="-1m" <%if q3="-1m" then %>selected<%end if%> >month <option value="-1y" <%if q3="-1y" then %>selected<%end if%> >year </select> </td> <td></td> <td><font size=2> Sent to: (alias) </font></td> <td> <input type=hidden name="c5" value="@MessageFolderName"> <input type=text name="q5" size=25 maxlength=50 value="<% = q5 %>"> </td> </tr> <tr><td> <font size=2>Subject:</font> </td> <td> <input type=hidden name="o4" value="@DocTitle"> <input type=text name="q4" size=25 maxlength=50 value="<% = q4 %>"> </td> <td></td> <td><font size=2> Folder: </font></td> <td> <input type=hidden name="c6" value="@MessageFolderName"> <input type=hidden name="q6" size=25 maxlength=50 value="<% = q6 %>"> <select name="ct" value="ct"> <% 'Enter new catalog names here %> <!-- Use the following syntax for to search all catalogs <option value="Discussion,ProductKnowledge,Business,list_Servers"> All Indexed Folders --> <!-- You need to put your catalog name here --> <option value="Discussion"> \Discussion </Select> </td> </tr> <tr> <!-- Add search by category --> <td><font size=2> Category: </font></td> <td><input type=hidden name="c7" value="@META_Type"> <select name="q7"> <option value="" <%if q7="" then %>selected<%end if%> > <option value="Best Practice" <%if q7="Best Practice" then %> selected<%end if%> >Best Practice <option value="Idea" <%if q7="Idea" then %>selected<%end if%> >Idea <option value="Issue" <%if q7="Issue" then %> selected<%end if%> >Issue <option value="Resolution" <%if q7="Resolution" then %> selected<%end if%> >Resolution </select> </td> <td></td> <!-- Add search by industry --> <td><font size=2> Industry: </font></td> <td><input type=hidden name="c8" value="@META_Industry"> <select name="q8"> <option value="" <%if q8="" then %>selected<%end if%> > <option value="Communication and Entertainment" <%if q8="Communication and Entertainment" then %> selected<%end if%> >Communication and Entertainment <option value="Distributed Services" <%if q8="Distributed Services" then %> selected<%end if%> >Distributed Services <option value="Financial Services" <%if q8="Financial Services" then %>selected<%end if%> >Financial Services <option value="Government" <%if q8="Government" then %> selected<%end if%> >Government <option value="Healthcare" <%if q8="Healthcare" then %> selected<%end if%> >Healthcare <option value="Information Systems( Hardware/Software)" <%if q8="Information Systems( Hardware/Software)" then %>selected<%end if%> > Information Systems(Hardware/Software) <option value="Manufacturing" <%if q8="Manufacturing" then %> selected<%end if%> >Manufacturing <option value="Professional Services" <%if q8=" Professional Services" then %>selected<%end if%> > Professional Services <option value="Transportation" <%if q8="Transportation" then %> selected<%end if%> >Transportation </select> </td> </tr> <tr> <!-- Add search by product --> <td><font size=2> Product: </font></td> <td><input type=hidden name="c9" value="@META_Product"> <select name="q9"> <option value="" <%if q9="" then %>selected<%end if%> > <option value="(Other)" <%if q9="(Other)" then %> selected<%end if%> >(Other) <option value="Access" <%if q9="Access" then %> selected<%end if%> >Access <option value="Excel" <%if q9="Excel" then %> selected<%end if%> >Excel <option value="Exchange Server" <%if q9="Exchange Server" then %> selected<%end if%> >Exchange Server <option value="Mail" <%if q9="Mail" then %>selected<%end if%> >Mail <option value="Office" <%if q9="Office" then %> selected<%end if%> >Office <option value="Outlook" <%if q9="Outlook" then %> selected<%end if%> >Outlook <option value="PowerPoint" <%if q9="PowerPoint" then %> selected<%end if%> >PowerPoint <option value="Visual Basic" <%if q9="Visual Basic" then %> selected<%end if%> >Visual Basic <option value="Visual C++" <%if q9="Visual C++" then %> selected<%end if%> >Visual C++ <option value="Word" <%if q9="Word" then %>selected<%end if%> >Word </select> </td> <td colspan=5 align=right> <% 'Links to tips and search button L_SearchButton_label = "Search" L_SearchTips_link = "Tips" %> &nbsp;&nbsp;&nbsp; <input type="submit" value="<% = L_SearchButton_label %>"> &nbsp;&nbsp;&nbsp; <font size=2><a href="tips.htm"><% = L_SearchTips_link %></a> </font> </td> </tr> </table> </form> <% 'End of Search Initiation Section %>

The next section of the code checks whether the user has an ActiveX control installed on his computer to launch Outlook to view the item. This ActiveX control is provided with Site Server. The filename for the control is exciol.ocx, and the CodeBase property for the control points to C:\siteserver\knowledge\search\controls\exciol.ocx by default.

This control has one method that you can call: DisplayMsg. The DisplayMsg method takes the URL to the item, which you can obtain from the DocAddress property in the ADO recordset of results. Then the ActiveX control will automate Outlook to display the item. If the user doesn't have Outlook, or if you don't want to let the user view the results using Outlook, she can use Outlook Web Access to view the item.

<% if Request.QueryString <> "" then %> <% 'If site visitors use Outlook to view Exchange Server messages, 'the Exciol control is installed on their computers if ExchangeViewer="both" or ExchangeViewer="outlook" then %> <object id="Exciol" height=0 width=0 CLASSID="CLSID:DAFD7A40-73FF-11D1-A811-00AA006EAC9D" CODEBASE="/siteserver/knowledge/search/controls/ exciol.ocx#version=5,5,2148,0" TYPE="application/x-oleobject"> </object> <script language="vbscript"> Sub DisplayMsg(EntryID) Exciol.DisplayMsg(EntryID) End Sub </script> <% end if %> <% if ExchangeViewer="both" or ExchangeViewer="owa" then %> <% 'When linking to Outlook Web Access, open the link in a new window %> <script language="javascript"> function openNewWindow(fileName,theWidth,theHeight) { window.open(fileName,"NewWindow","toolbar=0,location=0,directories=0, status=1,menubar=1,scrollbars=1,resizable=1,width="+theWidth+", height="+theHeight) } </script> <% end if %>

The next section of code creates the user's query by using the Site Server Query object. As you saw earlier, the Query object has a number of properties that you can use to create complex queries against Site Server catalogs. In the search.asp code, the first step is to create the Query object by calling Server.CreateObject with the ProgID of the Query object, MSSearch.Query. Since the application passes all the information about the query along the URL query string, you can use the method SetQueryFromURL to have the Query object automatically parse the URL query string and set the Query object properties according to the specified conventional query string variables.

<% 'Set query object properties set Q = Server.CreateObject("MSSearch.Query") 'Define all required query properties: the query itself, and all columns 'that will be used in the results Q.SetQueryFromURL(Request.QueryString) Q.Catalog = request("ct") Q.OptimizeFor="nohitcount,performance" Q.MaxRecords = 10 Q.Columns = "DocAuthor,DocTitle,DocAddress,Description,Size,FileWrite" %>

The next step in creating the search results is to actually create the recordset of items that match your criteria. To do this, you need to call the CreateRecordset method on the Query object. This method will create an ADO sequential, forward-only recordset that you can use to display your results. As you can see in the code, different properties are pulled from the Site Server catalog to display the resultset. The code uses standard ADO methods and properties to scroll through the resultset.

<% 'Create the recordset holding the search results on error resume next set RS = Q.CreateRecordSet("sequential") if err <> 0 then createerror = err.description end if 'If the query can't be executed, print out the error description if err then Response.write "&nbsp;&nbsp;" & createerror 'If no matches are found, display a message elseif RS.BOF and RS.EOF then Response.write "<p>" if Q.QueryIncomplete=true then L_TooComplex_Error = "The query is too complex. " & _ "Try using a simpler query.For information on search " & _ "syntax, see <a href=tips.htm>Search Tips</a>." Response.write L_TooComplex_Error & "<p>" else L_NoMatch_Error = "No messages matching your query were " & _ "found. For suggestions on how to broaden your search, " & _ "see <a href=tips.htm>Search Tips</a>." Response.write L_NoMatch_Error & "<p>" 'Display link for a new search L_NewSearch_link = "New Search" %> &nbsp;&nbsp;<a href="Search.asp"><% = L_NewSearch_Link %></a> <% end if else 'If query could be executed, display results 'Set up the table for displaying results %> <table cellpadding=0> <tr> <td colspan=2><font size=2> <% 'Make introductory sentences match the information 'displayed if Request("sh") = "" then L_Match_text = _ "Here are the messages found matching the query" else L_Match_text = _ "Here are more messages found matching the query" end if Response.write L_Match_text & "<b> " & _ DisplayText & "<b>." %> <br>&nbsp; <% if ExchangeViewer="both" then L_OWA_Info = "For Microsoft&reg; Exchange messages, " & _ "click the author's name to view the message " & _ "using Microsoft Outlook. Click the " & _ "<img srC=html.gif width=16 height=16 " & _ "border=0 align=middle align=middle> " & _ "icon to view the message using Microsoft " & _ "Outlook Web Access." Response.write L_OWA_Info end if %> <br>&nbsp; </font></td> </tr> </table> <table cellpadding=0 cellspacing=0> <tr bgcolor=cccccc> <% L_KBytes_text = "KB" L_Received_text = "Received" L_Subject_text = "Subject" L_Size_text = "Size" L_From_text = "From" %> <td></td> <td><font size=2><b><% = L_From_text %></b></font></td> <td></td> <td><font size=2><b><% = L_Subject_text %></b></font></td> <td></td> <td><font size=2><b><% = L_Received_text %></b></font></td> <td></td> <td><font size=2><b><% = L_Size_text %></b></font></td> <td></td> </tr> <% 'Set up loop to iterate through results Do while not RS.EOF 'Determine format type; set up title for and URL for links if InStr(RS("MimeType"), "text/exch") then DocType="exchange" else DocType="doc" end if 'If message title is blank, use "No subject" instead if RS("DocTitle") <> "" then Title = RS("DocTitle") else L_Untitled_text = "No subject" Title = L_Untitled_text end if 'Provide alternate text if no author exists. HTMLEncode is 'required to handle author fields with < or >. if RS("DocAuthor") <> "" then Author = Server.HTMLEncode(RS("DocAuthor")) else L_NoAuthor_text = "Author unknown" Author = L_NoAuthor_text end if 'Set up link itself. Link depends on whether item is document or 'Microsoft Exchange Server message. if DocType="doc" then Link = RS("DocAddress") Image = "html.gif" elseif DocType="exchange" then Image = "owa.gif" if ExchangeViewer="owa" then Link = "JavaScript:self.openNewWindow(" & chr(34) & _ RS("DocAddress") & "&usemainwnd=1" & chr(34) & ", 640,500)" elseif ExchangeViewer="outlook" or ExchangeViewer="both" then Link = "VBScript:self.DisplayMsg(" & chr(34) & _ RS("DocAddress") & "=1" & chr(34) & ")" end if if ExchangeViewer="both" then OWALink = "JavaScript:self.openNewWindow(" & chr(34) & _ RS("DocAddress") & "&usemainwnd=1" & chr(34) & ", 640,500)" end if end if %> <% 'Create table row for each result %> <tr><td>&nbsp;</td></tr> <tr> <% if DEBUGINFO=true or Request("debug") <> "" then %> <% 'Display column with record number %> <td valign=top><font size=2><% = RecordNum %>.</font></td> <% end if %> <td valign=top> <a href='<% = Link %>'><img src="<% = Image %>" hspace=6 border=0> </a> </td> <td width=20% valign=top><font size=2> <a href='<% = Link %>'><b><% = Author %></b></a> <% if ExchangeViewer = "both" and DocType="exchange" then %> <a href='<% = OWALink %>'> <img src="html.gif" height=16 width=16 border=0 align=top></a> <% end if %> </font></td> <td width=5>&nbsp;</td> <td width=55% valign=top><font size=2> <b><% = Title %></b> </font></td> <td width=5>&nbsp;</td> <td valign=top nowrap><font size=1> <% = RS("FileWrite") %> </font></td> <td width=5>&nbsp;</td> <td valign=top><font size=1> <% iSize = CInt(CLng(RS("Size"))/1024) %> <% Response.write iSize & " " & L_KBytes_text%> </font></td> </tr> <tr> <td></td> <td colspan=6><font size=2> <table cellpadding=0 cellspacing=0> <td width=30>&nbsp;</td> <td><font size=1> <% = RS("Description") %> </font></td> </tr> </table> </td> </tr> <% 'Increment the results RS.MoveNext RecordNum = RecordNum + 1 Loop %> <% 'If query times out, display message stating that more results are 'available %> <% if Q.QueryTimedOut = TRUE then Response.write "<tr><td></td><td colspan=8><font size=2><b>" L_QueryTimedOut_error = "Not all matching messages were returned." & _ "To see all the messages, at the top of the page, " & _ "click Search again." Response.write "&nbsp;<p>" & L_QueryTimedOut_error Response.write "<b></td></tr><font size=2>" end if %> </table>

The final section of code implements the More Results functionality at the bottom of the Web page. Since 10 results appear on a page, you need to implement this functionality so that the user can look at all the results returned in the query. This section of code uses the MoreRows property to determine whether there are more results than currently displayed. If there are more rows, the StartHit property is set on the Query object to the start hit on the next page. Then the code generates the hyperlink for the More Results text to pass the same query string to search.asp so that the application performs the same query on the catalog but returns the next resultset.

<hr> <table cellpadding=4> <tr><td><font size=2> <% 'Display a More Results link if there are more results pages if RS.Properties("MoreRows") = true then Q.StartHit = RS.Properties("NextStartHit") 'Repeat query with new start hit. The query must include any 'custom variables you've used: in this case, Text and RecordNum. Response.write "<a href=search.asp?" _ & Q.QueryToURL & "&" _ & "Category=" & Request("Category") & "&" _ & "DisplayText=" & Server.URLEncode(DisplayText) & "&" _ & "RecordNum=" & RecordNum _ & ">" L_MoreResults_link = "More Results" Response.write L_MoreResults_link & "</a>&nbsp;&nbsp;" end if %> <% 'Display link for a new search %> <% L_NewSearch_link = "New Search" %> &nbsp;&nbsp;<a href="Search.asp"><% = L_NewSearch_Link %></a> </font></td></tr> </table>

Категории