asp:Feature
LANGUAGES: C#
ASP.NET VERSIONS: 2.0
The Search Is On: Part II
Creating a Custom SharePoint Web Part
By Brendon Schwartz and Matthew S. Ranlett
This two-part series guides you through the process of
creating a SharePoint Web part. We began with the out-of-the-box configurations
required to ensure that search is successful inside your Microsoft Office
SharePoint Server portals, and explored where the search result and click-through
data is kept (see Part
I). Now the challenge is to use this knowledge to build our own custom Web
part.
Building Your Query Report Control
With the system set up, you need end users to perform
searches. As those searches are performed, you ll need a customized way to
display them sorted by popularity. Luckily, this information can be extracted
and displayed on a Web page or in a Web part. Because the information is simply
in the database, there are a few options for accessing the data. You could
access the data by connecting directly to the database, you could write a Web
service, or you could even use the SharePoint classes to help build a
component. It is important to keep in mind that these internal SharePoint
stored procedures could change with any hot fix or service pack, and are not
documented as interfaces to the system. As we ll briefly explore, each one of
these options offers advantages and disadvantages.
If you connect to these stored procedures directly from
code, you must write your own user and role security and database connection
methods, provide the correct stored procedure, and provide the correct
parameters. This can be a viable option, but you ll need to provide your data
access code rights to the SharePoint database. If the SharePoint machine and
the Web server on which you are working are in the same environment, this can
usually be worked out with administration settings.
The second option, using Web services, works best with
applications that need remote connections. For instance, you could display this
information to end users in a smart client (like Microsoft Word or your own
Windows Forms application). Although this helps distribute the data, the Web
service itself would still need to make a connection to the database (either with
direct access to SQL Server or through another method, such as using the
SharePoint object model).
The third option, and the one we are going to take, is to
use SharePoint s own classes to pull information from the Search Query Results.
This method includes the requirement of having SharePoint installed on one of
the machines, but does not require you to write the database calls or the
security permission code.
To build this component, start by looking at the class
hierarchy of Microsoft.SharePoint.Portal.Analytics.UI. Figure 1 shows how the
classes are inherited. These are the classes that power the usage reports
screens and provide the fundamental classes that are used for the component.
You ll be creating a custom report control that would be at home on any of the
usage report pages; however, this control is repurposing the data the report
would use for display purposes. Having done this, you can take that data and
use it in any Web page using simple ASP.NET controls.
|
QueryTopLargeListReportControl (abstract)
|
|
QueryTopReportControl (abstract)
|
|
QueryReportControl (abstract)
|
Figure 1: Report control
hierarchy.
The component will derive from
QueryTopLargeListReportControl to get the functionality provided to all report
controls based on the search queries. When implementing
QueryTopLargeListReportControl, you are required to implement certain
properties that each class must implement; these properties are shown in Figure
2.
|
Property
|
Description
|
|
RdlFileName
|
When the report page is displayed this is the report
definition file that will be used for formatting.
|
|
StoredProcedureName
|
The stored procedure that will be called to populate the
data of the report.
|
Figure 2: Property
definitions for QueryTopLargeListReportControl.
From your previous research, you might recall that the
stored procedure used for this control is proc_MSS_QLog_TopQueries. This sample
control has the string built in to the control, but you could easily make this
a property that can be modified through the user interface in order to create a
more generic TopQueries control. The RdlFileName will be left blank because you
won t actually be using the Report Control portion of this component. If you
want to use this control as a report control, or for testing purposes, simply
set the RdlFileName to the report you want to use. The RdlFileName could be one
of the existing Microsoft ones, or even a custom one you write. Once you ve
implemented the class, your code will start to look like that shown in Figure 3.
public class DevCowTopQueries: QueryTopLargeListReportControl
{
protected override
string StoredProcedureName
{
get
{
return
"proc_MSS_QLog_TopQueries";
}
}
protected override
string RdlFileName
{
get { throw new
Exception("The method or operation
is not
implemented."); }
} }
Figure 3: New control
DevCowTopQueries.
Even though you are only required to implement two
properties, the control also needs two other properties that are specific to
this control: TitleText and StoredProcedureParameters (see Figure 4). The
TitleText property is part of the reporting and control, and is only set for
cases that the report control is used; the StoredProcedureParameters property
is critical to the stored procedure you are using. From looking at the database
stored procedure, we can tell there are three parameters required one of
which controls the number of results returned. The values used are inherited
from the base classes, and only need to be implemented if you want to override
them. For instance, the base value for TopResultCount is 10; if you want to
have this variable, or if you want to increase this number, you can override
your own property and set the value required.
protected override string TitleText
{
get
{
return "DevCow
Query Control";
}
}
protected override Collection<SqlParameter>
StoredProcedureParameters
{
get
{
Collection<SqlParameter>
storedProcedureParameters = new Collection<SqlParameter>();
Guid guid =
SPControl.GetContextWeb(this.Context).Site.ID;
storedProcedureParameters.Add(new SqlParameter("@siteGuid",
guid));
storedProcedureParameters.Add(new SqlParameter("@isSspLevel",
this.IsSspLevel));
storedProcedureParameters.Add(new
SqlParameter("@topResultsCount", this.TopResultsCount));
return
storedProcedureParameters;
}
}
Figure 4: DevCowTopQueries
properties.
Here s where your control begins to differ from the
standard Microsoft controls. The Microsoft controls keep the report data
internal to the control, but the entire idea of this control is to use that
data on other Web pages. Luckily, one of the inherited method calls is
LoadReportData, which doesn t take any parameters and returns a System.Data.DataTable.
To allow this data to be exposed to the end user, you must create a new method,
GetData:
public DataTable GetData()
{
return
this.LoadReportData();
}
You can see that the method has the identical signature to
the LoadReportData method, and is, in fact, simply a wrapper method to allow
other applications to access otherwise private data. Keep in mind that this
class does not perform any security checks or audit logging on this method, but
these security checks or audits might be required, depending on the
environment. Now that you are returning the data in a DataTable to other Web
parts, Web pages, or controls, there s no more mystery for the experienced
developer using the control to take advantage of Search Query data.
Display Top Search Terms
Now that the search data is being retrieved from the
SharePoint analytics classes, you need a way to display this data on a Web
page. Because you are already using SharePoint to pull the data back, creating
a few Web parts to make it easy to display the results and create the
functionality to use the results to perform your own search is the best option.
To set the values of the dropdown list, you first must know the names of each
column of the DataTable. You could look at the return types of the stored procedure,
but in some cases it s not obvious from the T-SQL. You could run the stored
procedure and see the values, or you could opt to create a GridView-based Web
part to dump the raw data to the screen. Here, a GridView Web part is a
valuable exercise, perhaps to display the results for administrators, as illustrated
in Figure 5. Administrators may want to see the entire view of the data that
will be displayed to the end users in another format.
Figure 5: TopSearchTermsGrid Web part.
You can see that all the columns are information from the
search pages discussed when you set up the page. These include the query
string, the search scope, and the results URL, to name a few. Additionally, you
can see information such as the number of times the query has been sought. Although
this is a simple Web part, it can be placed on any page to see the results and for
what users are searching. Once you know what the users are looking for, you can
add best bets and improve the relevance of information that should show up
based on actual user search patterns.
With all this information, you can now build a Web part
that has a dropdown list of the top search queries run on the system. Remember,
you are building components and Web parts to help the end users know the most
popular search phrases on the site, and this is one way to help them expand
their knowledge of the site without generating a lot of IT support calls. The
salient field available for use is the queryString field, which is used for
both the displayed text and the value for searching. Figure 6 shows the call to
the control you just built to get the data, as well as using the data contained
in the queryString column to populate the dropdown list values.
ddlTopChoicesClient = new DropDownList();
DevCowTopQueries tdd = new DevCowTopQueries();
ddlTopChoicesClient.DataSource = tdd.GetData();
ddlTopChoicesClient.DataTextField = "queryString";
ddlTopChoicesClient.DataBind();
this.Controls.Add(ddlTopChoicesClient);
Figure 6: Binding
to usage data.
The dropdown list allows the Web part to present the user
a pre-defined list of query string selections and also prevents them from
modifying the list. This user experience lends itself to the collaborative
nature of the Web part s intention. If the user wants to search for information
not directly related to the available search options, they ll have to perform
that search using the default search input control.
Build a GO Button
The GO button is the button that will perform the actual
search action. There are many ways you can perform a search with SharePoint. You
can type in the search URL with a query string, call the Web services API, use
the object model to perform the search, and use the built-in JavaScript. For
the purpose of demonstration, this Web part implements a pair of GO buttons,
one using the JavaScript method and the other using the URL redirection method (both
are valid methods that are easy to use for searching). The other methods might
be used if you were writing your own search results page or wanted to change
the way search results were returned.
With client-side JavaScript, you can use the built-in
functions provided with the SharePoint JavaScript library. The function we want
to use, GoSearch, is located in the search.js library. There are a number of
parameters you can pass to the function. The only parameter the GO button uses
is the queryString value retrieved from the option selected in the dropdown
list. As you can see in Figure 7, there are a few hard-coded assumptions, such
as the location of the Search Center
page and that the desired search scope is All Sites .
searchResultsButton = new Button();
searchResultsButton.Text = " GO ClientSide";
searchResultsButton.OnClientClick =
"GoSearch(null,'" + ddlTopChoicesClient.UniqueID
+
"',null,true,false,null,'null',null"
+
",null,'\u002fsearchcenter\u002fPages\u002fResults.aspx'"
+
", 'This Site','This List', 'This Folder', 'Related Sites'"
+
", '\u002f_layouts\u002fOSSSearchResults.aspx');";
this.Controls.Add(searchResultsButton);
Figure 7: Create a
client-side search button.
Once the JavaScript function is constructed, attach the
string to the OnClientClick event of the button. This will perform the query
search using the client redirect and will not require that the server have a
postback for every request to the search query.
The second GO button performs the search on the server. Possible
reasons for using this method would be to modify the URL or to log event information
every time a user uses the Top Search Query functionality. To perform the
server-side URL redirect, you must create a new dropdown list that has the URL
pre-built from the data of the TopSearch control. To create the new dropdown
control, loop through each row of the DataTable and construct the URL as
{resultsUrl}?k={queryString}&s={scope}. The URL along with parameters for
the queryString and scope are then passed to the correct URL for processing. As
you can see, a single letter is used for the parameter locations: k is for
queryString; s is for scope. Now when the user presses the button, a server-side
request is made, processing can be performed, and the redirect to the URL
occurs.
Go Directly to BestBet
We re sure that everyone has seen the functionality on
Google that lets you go directly to the most relevant search result of the
keyword entered. What if there was the same ability in a SharePoint site? Now
there can be! Using the search results, keywords, and best bets, here s a look
at how to create just such a Web part for the users.
The first step is connecting to the SharePoint Object
Model and getting a reference to the Keywords class. The Keywords class
contains a field named Term that can be matched to the search query. Note that
with this method, all the words in the search string must also be in the Term
value of the Keyword. Once you have a reference to the Keywords for the site,
you can then perform standard operations like Add, View, Create, and Delete. Every
Keyword can have a set of objects, called BestBets, associated with the Keyword
term. These best bets provide a title and URL that can be used to navigate
based on the Keyword term. This section of the article describes how to
navigate directly to the top best bet if someone clicks the I might be Lucky!
button.
Creating a new button is straightforward; simply create a
new button that has a server-side event and when the user clicks the button,
search through the keywords to find the right set and redirect the user to the
top best bet. Keyword terms are not based on search scopes like the other query
string results; for that reason, use the first dropdown list you created that
has the query string as the value to make it easy to perform look-ups. Once you
have the query string, there are built-in functions to help you narrow the
collection of keyword results. The Keywords class has a function named
FilterKeywords that takes three parameters. The first two are enumerations
based on how you want the results returned and the last one is the keyword term
you are looking for in this case, it s the query string search.
Now that you have the keyword collection of terms that
matched the search, you can perform a loop through each keyword. Each keyword
may or may not have a set of best bets associated with it, so you might have to
look at more than one keyword to find the first best bet. Once you find the
keyword with a best bet, get the URL from the best bet and direct the user to
the best bet location. If you don t find a best bet, you can take the user to
another page or simply notify the user that no best bets are available for that
term. Figure 8 shows the code that will perform the look-up of the best bet.
void bestBetButton_Click(object sender, EventArgs e)
{
SearchContext searchContext
=
SearchContext.GetContext(SPContext.Current.Site);
Keywords keywords = new
Keywords(searchContext, new Uri(SPContext.Current.Site.Url));
KeywordCollection kwc =
keywords.GetFilteredKeywords(KeywordView.AllKeywords,
KeywordFilter.Keyword,
ddlTopChoicesClient.SelectedValue);
foreach (Keyword kw in
kwc)
{
foreach (BestBet bb
in kw.BestBets)
{
Page.Response.Redirect(bb.Url.ToString());
return;
}
}
lblUserMsg.Text =
"No BestBet defined for search query.";
}
Figure 8: Redirect
to SharePoint BestBet.
Conclusion
There are many ways to extend search with SharePoint. You
can create custom search results with XSLT, connect to many other systems with
Business Data Catalog (BDC), and search through files in your enterprise like
file shares. But there is one constant users need a way to help them navigate
through a system. Building tools that use the abilities of enterprise search
can help direct users to the right information on your site, making it more
valuable as a tool to all users. Building tools that use the usage data and
search information provide a start to what you can add for your users. Using
additional capabilities like keywords and best bets will help you, as content
owner, direct your users to relevant information; now you can even see the
areas that people are using the most.
Search doesn t have to be just building pages that return
results, although the Web services provided by Microsoft will allow you to
create these pages. The real advantage is using all the tools, such as
JavaScript, URL navigation, the SharePoint object model, and more to create the
solution that is right for your organization. You can even perform these
actions from other pages or applications by creating an interface, such as a Web
service. Be sure to configure security requirements on any data that you
expose!
Building controls that take advantage of the work that has
been done by the framework can help you make an impact quickly, but keep in
mind that undocumented features can change in the future. For this reason, make
sure to test your customizations when you apply service packs and perform
upgrades.
This concludes the exploration of SharePoint Search. This series
has demonstrated how to correctly configure SharePoint Search on a MOSS 2007
portal with a variety of content sources and search scopes. You ve taken extra
steps to ensure that your users are finding the most relevant details by
identifying best-bet results for certain search terms. You know where to look
to gain insight into how search is being used through a variety of built-in
reporting pages. To further your understanding of how search data is stored and
tracked, you ve used Reflector to dive into the guts of SharePoint source code
to discover its hidden secrets. Finally, you ve applied this knowledge to
create a custom Web part, which uses the portal itself to keep its contents
fresh and relevant.
The source code accompanying
this series is available for download.
Matthew S. Ranlett,
a senior consultant with Intellinet s Information Worker team, is based out of Atlanta.
A Microsoft SQL Server MVP, Matt is co-author of Professional
SharePoint 2007 Development, and co-founder of the Atlanta
.NET Regular Guys, hosted at DevCow (http://www.devcow.com).
Brendon
Schwartz is a principal consultant with Slalom Consulting
in Atlanta specializing in
SharePoint 2007. Brendon is a Microsoft MVP, co-author of Professional SharePoint 2007 Development, and
co-founder of the Atlanta .NET
Regular Guys, hosted at DevCow (http://www.devcow.com).