September 2008 - Posts

In the past, I have showed you how to use the KeywordQuery class as well as how to use the Enterprise Search Web Service.  Today, I wanted to add one small detail on how to use the web service to return your own custom managed properties.  Specifying your own properties is as simple as making use of the Properties element in the XML you send to the web service.  However, there are a couple of things you have to know.  First, here is what the syntax looks like.

    <Properties>

      <Property name="DocumentType" />
      <Property name="ProductId" />

    </Properties>

In the example above, I want search to return my custom DocumentType and ProductId managed properties.  However, if I specify just this, you will get the following error message.

Your query is missing required properties.  Please make sure to select the following properties: Title, Path, Description, Write, Rank, and Size.

If you remember from my KeywordQuery post, this is because when you specify any property it removes all default properties from the list.  This is easy to fix though, just add the properties it asked for.  Here is what the complete document looks like.

<QueryPacket xmlns="urn:Microsoft.Search.Query" Revision="1000">

  <Query domain="QDomain">

    <SupportedFormats>

      <Format>urn:Microsoft.Search.Response.Document.Document</Format>

    </SupportedFormats>

    <Range>

      <Count>50</Count>

    </Range>

    <Context>

      <QueryText language="en-US" type="STRING">DocumentType:"Legal" Scope:"My Document Scope"</QueryText>

    </Context>

    <Properties>

      <Property name="Title" />

      <Property name="Path" />

      <Property name="Description" />

      <Property name="Write" />

      <Property name="Rank" />

      <Property name="Size" />

      <Property name="DocumentType" />

      <Property name="ProductId" />

    </Properties>

  </Query>

</QueryPacket>

One other thing to note.  If you call the web service and use the QueryEx method (the one that returns a DataSet), each custom property will be returned as a single row string[] array.  So if you were calling QueryEx and hoping to bind it straight to a GridView, you will be sadly disappointed as your custom properties will not be displayed.  I have no idea why Microsoft implemented it this way, but that is the way it is.  The weird thing is that the default properties you specify will be returned as regular strings.  So if you are hoping to databind, calling Query and making use of LINQ to XML may be a better option.

When every page on your SharePoint server returns an error, more often than not, SharePoint was unable to enter Safe Mode.  Safe Mode is the process in which SharePoint goes through to validate that web controls are allowed to be used in SharePoint.  If something goes wrong in this process, you will get a multitude of errors including this one I mentioned in the past regarding your master page.  Recently, I encountered the following error.

Request failed.   at System.Reflection.Assembly._GetType(String name, Boolean throwOnError, Boolean ignoreCase)
   at System.Reflection.Assembly.GetType(String name, Boolean throwOnError, Boolean ignoreCase)
   at System.Web.UI.Util.GetTypeFromAssemblies(ICollection assemblies, String typeName, Boolean ignoreCase)
   at System.Web.UI.TemplateParser.GetType(String typeName, Boolean ignoreCase, Boolean throwOnError)
   at System.Web.UI.TemplateParser.ProcessInheritsAttribute(String baseTypeName, String codeFileBaseTypeName, String src, Assembly assembly)
   at System.Web.UI.TemplateParser.PostProcessMainDirectiveAttributes(IDictionary parseData)

This error occurred on every page I tried to access.  I immediately went to the logs (C:\Program Files\Common Files\Web Server Extensions\12\LOGS) to see if I could figure out what was going on.  I encourage you to always start here first when things aren't working right in SharePoint.  Although they are hard to sift through, the actual error you may be experiencing can usually be found here.  Find the most recently created file and go from there.  Just remember, there is a slight delay from when an error occurs to when it actually gets written to the log.  Anyhow, after I received the error and went to the logs, I found the following lines that looked to be the culprit.

SafeControl load exception:MyCompany.MySharePoint.dll, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null Exception: Request failed. 

Safe mode did not start successfully. Request failed.

Given this, I knew I had an issue with SharePoint going into Safe Mode as I mentioned earlier, but the error was slightly different that what I had seen in the past with the Master Page error.  I have actually found that a few things can cause this.  First, start out by checking your bin folder for your web application.  Make sure that there are not any DLLs in that folder that do not belong (such as Microsoft.SharePoint.dll).  Also make sure that all of the DLLs refernced in the SafeControls element of your web.config that are present in your web.config (excluding anything that you might have put in the GAC).  What I mean here is if you have a custom DLL and maybe you changed the signature of it or renamed it, make sure that the SafeControl element matches the name of each DLL in the bin folder.

If none of that works, start going through the project of the DLL that was listed in the SafeControl load exception.  Start examining every control you have.  Make sure the references in the compiler directives of your .ascx files match what you have in your .ascx.cs files.  Even though your solution may compile, it doesn't mean there isn't something wrong with it.  So start with your latest checkins and look at each file and see if you can get it to work.  Also one thing of note is that if you are getting this error, you can usually remove the DLL from your bin folder and get your site to render.  The tricky part is just finding which file is the offending one.

with 1 comment(s)
Filed under: ,

Recently, my favorite Yankee, Kyle Kelin, returned home to Tulsa and told me about the concept of meetups.  Basically, it is just a group of people that meet up together somewhere in a social setting and talk about technology, poker, camping, or whatever.  These seem to be really big in New York and the rest of the east coast, so we thought it would be a good idea to bring one to Oklahoma to talk about SharePoint.  So I am pleased to help announce, the first Tulsa SharePint (intentionally misspelled) meetup on Thursday, October 16th at Crawpappy's at 6:00 PM.

There won't be any speakers, presentations, or anything like that as we are not trying to compete with or replace a user group.  We are just hoping to provide a fun setting, where you can talk with other SharePoint nerds about what you are working on and how you have solved problems, etc.  We don't have any sponsorship yet, so you're on your own for food and beer, but fear not Crawpappy's is one of the cheapest places in town to eat and drink.  If you're a recruiter, software vendor, etc, and would like to help sponsor our event, please let us know. 

Hope to see all of you there.

Directions

with 3 comment(s)
Filed under: , ,

I am very glad to see that the Wildcard Search Web Part has been so well received.  It's nice to know so many people are getting good use out of it.  I have had a number of comments about it regarding what else works with it and what doesn't so I thought I would address them today in a post.  We'll start with how the Search Center works out of the box.  It uses the Enterprise Search Keyword Query syntax.  As you probably know by now, this syntax does not support wildcard searching.  To implement Wildcard Search, I had to use  Full Text SQL Query syntax which does support wildcard searching.  Unfortunately, a lot of the functionality on the search results page only works with keyword syntax.  Here is a list of things that will not work or will work differently.

  • SearchSummaryWebPart - Provides the "Did you mean..." functionality.
  • Best Bets - Keyword based so it will not work.
  • Keyword Highlighting - Obviously keyword based as well.
  • RSS - This is keyword based as well.
  • Federated Search - Works but treats your search term as a keyword ignoring the wildcard.
  • Faceted Search - Keyword based, but I am confident we could write code on both our parts to make this work.

Ok, so this may sound like a lot, but in my experience a lot of people don't use some of this functionality.  It is just a trade off between having wildcard search or not.  This really isn't a result of the Wildcard Search web part alone.  It's the result of using a Full Text SQL Query and what Enterprise Search's limitations are.  We can confirm this by using the Advanced Search results web part.  Depending on the type of query you submit, it will do a Full Text SQL Query and the same functionality is missing.  It's been my experience that customer's really do like having the wildcard functionality and rarely miss the other features.  Thanks again for all the comments.

You may have a case where you want to search for files that were recently modified.  Sure, you can use a CAML query to do this, but what if you want to look for recently changed files across multiple content sources?  Sure, you can write your own Full Text SQL Query and bind it in a web part, but there is an easier way.  The new FixedFullTextSqlQuery property in Release 2 of the Wildcard Search Web Part makes this really simple.  You just need the right query. 

Date functions in Enterprise Search SQL are similar but the syntax is a little different.  First you'll need to get the current date.  The GETGMTDATE function is what you need to get the current date.  Next you just need to use DATEADD to subtract the number of days, months, etc, that you need for your fixed query.  In this case, I'll subtract 7 days by specifying DAY and -7.  You can also specify MONTH, YEAR, HOUR, etc as well.  The last modified date is stored in a managed property called Write (took me a while to figure that one out).  That will make your condition look something like this.

WHERE Write > DATEADD(DAY, -7, GETGMTDATE())

Then, all you need to do is form your SELECT statement.  You can't use SELECT *, so you need to specify all of your columns individually.  The easiest way to get this column list is to look at the Select Columns property on your control.  My example below lists most of them.  When you are all done, this is what your query would look like.

SELECT Rank, Title, Path, Author, Write, WorkId, Size, Description, SiteName, CollapsingStatus, ContentClass, IsDocument, HitHighlightedSummary, HitHighlightedProperties, FROM Scope() WHERE Write > DATEADD(DAY, -7, GETGMTDATE())

This will query the entire index for documents modified in the last 7 days.  You may want to filter it down to a specific scope by using an additional condition in your WHERE clause.

In keeping with my commitment to continue to give back to the SharePoint community, I am pleased to announce Release 2 of the Wildcard Search Web Part for MOSS Enterprise Search.  This new version of the Wildcard Search control adds support for passing a scope to the query using the standard 'S' URL parameter.  This allows you to use the scopes dropdownlist so that you can query other content sources (i.e.: the BDC).

This release also adds a new property FixedFullTextSqlQuery which can be used to specify a static Full Text SQL Query.  You can type in any query using the Search Full Text SQL Query syntax, and it will execute provided no keyword parameter has been passed using the query string.  You can set this property through the UI, programmatically, or by using CAML.  It also can be a great way to test out a full text query.  Keep in mind though, you should use this parameter with caution as you can easily write a query that does not perform well (such as returning the whole index).

So not a huge release, but the issue with scope definitely needed to be addressed.  Many thanks for everyone who has downloaded Release 1 and left comments and feedback.  Please continue to leave comments and report any issues you may have here or on CodePlex.

Wildcard Search Web Part Release 2

Here is more information on how Release 1 works.

In the past when I thought of how to do this, I tended to over analyze it a bit.  I always assumed there was something special in how you do this, but in reality, it is not much different than adding web parts to a new page.  I am not sure if this is the best way to do it and it only works in some cases, but it's a start and maybe somebody else can help out.  The main thing I was trying to avoid was having to write code in a feature receiver to accomplish this.  In today's example, we are going to assume I have a page called default.aspx on a site and it has a web part zone called Left (like many default page templates do).  To make this happen, I basically write CAML that is just like if I was creating a new page.  This includes getting a copy of a default.aspx page (any one will do that has similar names for web part zones) into a new feature.  In this case, I am using the default.aspx file from the STS site template located at 12\TEMPLATE\SiteTemplates\sts.  In my elements file, I start by adding a Module and File block like below.

<Module Name="DWS" Url="" Path="dws">

  <File Url="default.aspx" IgnoreIfAlreadyExists="TRUE">

The module Name you specify is DWS (which I believe is the name of the list that pages are stored in).  The Path is specified as dws as well assuming I stored my local default.aspx in a folder of that name.  You can choose to use a path or not.  It shouldn't matter.  I then specify the filename with the IgnoreIfAlreadyExists attribute set to true.  I believe this results in SharePoint not trying to create a new page if one of that name already exists.  After you have your file element it is just a matter of specifying your web parts as usual.

<AllUsersWebPart WebPartZoneID="Left" WebPartOrder="1">

  <![CDATA[

              <WebPart xmlns="http://schemas.microsoft.com/WebPart/v2">

                  <Assembly>Microsoft.Office.Server.Search, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>

                  <TypeName>Microsoft.Office.Server.Search.WebControls.SearchPagingWebPart</TypeName>

                  <Title>$Resources:spscore,SearchPagingWP_Title;</Title>

                  <Description>$Resources:spscore,SearchPagingWPWP_Desc;</Description>

                  <PartOrder>1</PartOrder>

                  <FrameType>None</FrameType>

                  <AllowMinimize>true</AllowMinimize>

                  <AllowRemove>true</AllowRemove>

                  <IsVisible>true</IsVisible>

              </WebPart>

          ]]>

</AllUsersWebPart>

Every time the feature is activated, it will add the web parts you specify to the file that you specify.  This ends up working pretty well, but keep in mind there is nothing built in to delete your web parts off of your web page.  If you want to do that, you are going to have to write code in a feature receiver to remove the web parts (a post which I still need to write).  Anyhow, I guess what I am saying is that it really isn't any more complicated than adding web parts to a new page.  I am not sure if this is the best way to do it so if you have a better idea be sure to leave a comment.

with no comments
Filed under: , ,

There are a ton of tools out there to create SharePoint solution packages (.wsp files) for you, but it is still a good idea to know what those tools are creating for you exactly.  Sometimes, things don't always work right, and you may need to customize or tweak your manifest file.  If you are new to SharePoint development, the Manifest.xml file is used during the deployment of a .wsp solution package.  It tells SharePoint what files to copy, what features to install, where to put your binaries, adds SafeControl entries, as well as set code access security.  The file seems pretty straight forward but there are a lot of ways to mess it up.  As I pointed out in the past, do not put any comments in this file.  It will cause all kinds of FAIL. 

I'll start by go over the key elements of this file and point out some things you need to know about each one.  If you create any of these main elements, note, that they will always require some kind of child element, otherwise the file won't validate.  The first element we will talk about is FeatureManifests.  This name as it sounds is used to deploy features.  Your solution will install features but not activate them for you.  You can list multiple features to install here using the FeatureManifest element.  It's Location attribute specifies the path to the feature (inside the DDF file).  What that means is the path specified to your feature.xml file will match whatever you have listed as the destination in your DDF file.  We can talk about DDF files again another day if necessary.

<FeatureManifests>

    <FeatureManifest Location="MyFeature\feature.xml" />

</FeatureManifests>

The TemplateFiles element is used to copy out any additional files to the SharePoint server.  These could be .webpart files, user controls, other XML files, application pages, master pages, etc.  SharePoint will not copy any file that is not listed here (except for binaries and feature.xml and related files).  Again, you can specify multiple TemplateFile child elements to copy multiple files.  Part of what makes this file tricky is the paths.  Some are relative, some require you to specify something inside the 12 hive.  It takes some getting used to.  In this case, if you are copying additional files into a feature folder for a web part, you would specify something like FEATURES\MyFeature\mywebpart.webpart.

<TemplateFiles>

  <TemplateFile Location="FEATURES\MyFeature\MyWebpart.webpart" />

</TemplateFiles>

The Assemblies element specifies which binaries will be deployed to SharePoint and where.  You can copy multiple assemblies out at a time with a feature by specifying more than one Assembly element.  The Assembly element has a Location attribute which typically just contains the DLL name (again dependent on where the DLL is copied in your DDF file).  It also contains a DepoymentTarget attribute which specifies wether to copy it to the GAC (GlobalAssemblyCache) or the bin folder (WebApplication).  It also contains a SafeControls element with the same syntax of what is used in the web.config. 

<Assemblies>

  <Assembly DeploymentTarget="WebApplication" Location="MySolution.dll">

    <SafeControls>

      <SafeControl Assembly="MySolution, Version=1.0.0.0, Culture=neutral" Namespace="MySolution.WebParts" TypeName="*" Safe="True" />

    </SafeControls>

  </Assembly>

</Assemblies>

One thing to note here is that if you do not specify at least one assembly with a SafeControl element, SharePoint will decide that your solution does not contain a Web Application Resource and therefore will not deploy it when you specify a URL with stsadm.  So, I typically include this line even if I don't have any controls or web parts in my solution.  You will get an error like the one below.

This solution contains no resources scoped for a Web application and cannot be deployed to a particular Web application.

The last element, I am will talk about is CodeAccessSecurity.  This of course is the most complex one.  I have posted on this topic in the past and don't forget, I will be speaking about it next month.  The post I have on it covers it in more detail that I will cover today.  The one thing, I will remind you of is that it is ok to specify multiple assemblies. 

I haven't talked about the root element yet. It's pretty simple.  Just remember the SolutionId needs to be unique.  The ResetWebServer attribute can be set to false if you don't want it to reset IIS on deployment.

<Solution SolutionId="{B3270C0DF-EACA-4320-9B2A-C293435C5DF1}"  xmlns="http://schemas.microsoft.com/sharepoint/" ResetWebServer="true">

Anyhow, hopefully this will help someone out the next time they are looking at a manfiest.xml file.

Tulsa Tech Fest was a lot of fun last year and I am excited to be speaking at it again this year.  This year, I will be giving a talk on Implementing Partial Trust in SharePoint.  It should be a good talk in explaining how to create SharePoint solutions that can deploy Code Access Security settings.

It also looks like the DotNetMafia might be involved in putting together an unofficial "social event".  More details on that to come.