November 2008 - Posts

I thought I would join the herd and help spread the word about Isolator for SharePoint.  It's pretty exciting that there is actually a chance of doing TDD with SharePoint.  I look forward to trying it out.

Typemock are offering their new product for unit testing SharePoint called Isolator For SharePoint, for a special introduction price. it is the only tool that allows you to unit test SharePoint without a SharePoint server. To learn moreclick here.

The first 50 bloggers who blog this text in their blog and tell us about it, will get a Full Isolator license, Free. for rules and info click here. 

Ingenious idea of how to spread the word guys.  I like it.

with no comments
Filed under: ,

Since I deal with Enterprise Search a lot, I often want to do things in SharePoint given nothing but a URL.  Unfortunately, there isn't an API call (that I know that can take a URL can conveniently spit out an Item Id).  You can probably use a CAML query, but you would have to know which site collection to start with.  So how do we get the id?  It takes a number of steps, but it is pretty simple.  We first start by opening an SPSite object given the full URL to the item.

using (SPSite siteCollection = new SPSite(url))

{

    using (SPWeb site = siteCollection.OpenWeb())

This assumes that the code is executing somewhere where opening an SPSite object for that URL is valid.  After that we open a SPWeb object which gives us an object representing the site that contains the document.  Next it is a matter of splitting the URL to get a file and folder URL.

// site url

string siteUrl = site.Url;

 

// get the position of the last slash so that the string can be split

int lastSlashPosition = url.LastIndexOf('/');

 

// folder url

string folderUrl = url.Substring(0, lastSlashPosition);

 

// fileUrl

string fileUrl = url.Substring(lastSlashPosition + 1);

We do this by using LastIndexOf('/) (which of course assumes that the URL contains slashes).  At this point it is easy to get a reference to the file object representing the document.

// get file object

SPFile file = folder.Files[fileUrl];

This gives us a reference to the file which means we can get a reference to the item and then its Id.  Conversely if you are interested in the SPFolder object, you can just use.

// get folder object

SPFolder folder = site.GetFolder(folderUrl);

We will now get the SPListItem object which will give us its Id.

// get the list item

SPListItem item = file.Item;

 

// get the list item id

int itemId = item.ID;

 

// get the uniqueId for the list item

Guid uniqueId = item.UniqueId;

Above, I have code to return the internal list item id as well as the unique id.  There are a few steps involved, but it seems to work pretty well.  You will of course want to add proper error handling and check for nulls along the way, but this should be a good start.  This is the same way I get the information I need for the Document Link Handler for Enterprise Search.

with 7 comment(s)
Filed under:

I am excited to announce that I am speaking at the Oklahoma City Developer's Group on December 1st.  I am giving a new talk geared at helping ASP.NET developers take the leap into SharePoint development.  This talk will closely tie into my recent Introduction to SharePoint Development (web part, user control) series of posts.  This talk will show developers what they need to get started and how to tackle deployment.  We'll also cover the basics of working with the SharePoint API.  The OKC group has a lunch meeting and an evening meeting.  I look forward to seeing everyone there.

with no comments

Let's face it, not every InfoPath form you write is going to work right on the first time.  If you are dealing with a Full Trust InfoPath task from at some point you may need to debug it.  In my server in question today, it did not have InfoPath nor Visual Studio installed.  However, it did have the Visual Studio Remote Debugging Monitor installed, so I decided to give it a shot.

Remote Debugging an InfoPath form is not much different that remote debugging anything else in SharePoint.  You run the Visual Studio Remote Debugging Monitor on the remote server and use Visual Studio to attach to the w3wp process on the remote server.  There are a couple of catches though.  First, if you are running VSTA from InfoPath the first thing you will notice is that there is no Attach to Process menu item in the Debug menu.  I am sure there is a way to get around it, but I just decided to close VSTA, open up Visual Studio 2005 (you have to use VS2005 since it is a .NET 2.0 project - although you may be able to upgrade it), and opened the .csproj file for the InfoPath project.

Opening the project with Visual Studio 2005 gives us the Attach to Process menu item.  InfoPath code behinds don't generate PDB files by default.  So, you will need to go into the project properties, Build tab, and select Debug.  You then need to click on Advanced and set the Debug Info to Full.  Build the project and you will have your PDB file.  Now you need to copy the DLL and the PDB file to the SharePoint server.  I have been copying these files into the feature folder of the workflow (i.e.: TEMPLATE\FEATURES\MyWorkflow).  I have no idea if this is the correct way to deploy Fully Trusted forms, but I have yet to find any documentation on how to do it and I know this works.

Once you have done that you are ready to attach to the w3wp process.  Don't forget you can use iisapp.vbs to determine the correct w3wp process to attach to.  Set a breakpoint in your InfoPath code behind and then execute your InfoPath form or workflow.  If all goes well, your breakpoint will be hit.  If it doesn't get hit, make sure you attached to the right w3wp and make sure that the DLL and PDB on your local computer match what is on the server.

One thing to note is when you start deploying subsequent updates, the DLL for your form will become locked and you won't be able to deploy a new one.  Be aware of that if deploying via solution package as it won't give you an error and it will just fail to copy things.  Once you are done debugging, you will need to terminate the current w3wp process to release the lock and update your DLL.  Also if you are just updating the code and not the form, it is ok to just copy the DLL and PDB out and not reinstall the feature and reattach the workflow.  That should save you some time.

A common question I get with new SharePoint developers is "How do I deploy my user control in SharePoint?".  Many times they know of the SmartPart or even how to make their own, but its the matter of deploying things that may not be clear.  Before starting this, you need to read how to build and deploy a web part.  Many of the concepts in that article are used here and explained in more detail.

To build a user control, you start in very much the same way as you do with regular ASP.NET.  My recommendation is to start by creating a web application project.  You can also use a regular class library, but its easier if you use a web application project since it has the file types you need as well as references.  If you already have a class library, you can easily convert it to a web application project using a hack.

Once you have a web application project, create your user control as normal.  Build the project and then its time to deploy.  We'll first talk about where the files go on the server and then we'll look at how we can get the files there with a solution package.  Let's assume, I have a control called MyControl.ascx.  This file has to be deployed to the SharePoint server along with its binary.  If you are using a SmartPart, the ascx file to go is in a folder called UserControls located in the root of your web sites folder (by default c:\inetpub\wwwroot\wss\VirtualDirectories\80).  However with true SharePoint development you have to break the my controls go in X folder on my site way of thinking.  In SharePoint, everything is designed to be modular no matter how many web applications your server hosts. 

I recommend creating your own web part to host your user control that will work with any folder.  SharePoint itself puts all user controls in the CONTROLTEMPLATES folder in the 12 hive (C:\Program Files\Common Files\microsoft shared\Web Server Extensions\12).  You can place your controls here as well, but I recommend creating a subfolder here to keep your controls separate.  In this case, I will create a folder called MyControls and copy MyControl.ascx into it.  At this point you are probably wondering what is the path to my control if its in the 12 hive?  On any SharePoint web application, there is a virtual directory called _controltemplates.  This happens to map to the folder we need.  Therefore, the path you would reference in your web part (or any web page) would be ~/_controltemplates/MyControls/MyControl.ascx.

Now, it is just a matter of deploying the binary.  In traditional ASP.NET, you could just copy the DLL of your web application project to the bin folder of the web application.  However, with SharePoint we can't do that without specifying Code Access Security which is a pretty big undertaking when you are starting out.  For any file that is in the bin folder, SharePoint requires a security policy be set specifying exactly what permissions your DLL has.  I recommend deploying to the bin folder, but if you are just starting out, you don't want to go there yet.  To get around this for now, there are two options: 1) Change the TrustLevel in the web.config to Full or 2) Deploy the DLL to the Global Assembly Cache.  I strongly recommend against #1, so for the sake of starting out, just copy your DLL to the GAC.  You will need to strongly name your assembly.  Any time you copy the DLL to the GAC, you will need to either recycle the application pool that your web application is using or reset IIS.  I know that can be a pain, but once you implement CAS, you don't have to do that any more.

Just like with a web part, you have to add your user control as a safe control.  Just specify the assembly name and allowed namespaces.

<SafeControls>

  <SafeControl Assembly="MyControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=03afd371f1d50a3a" Namespace="MyControls" TypeName="*" Safe="True" />

</SafeControls>

At this point you are ready to try your new user control out.  To add it to a page like a web part, I recommend you follow the steps in this post to make your own SmartPart.  If you don't know how to deploy a web part, follow my post on how to deploy a web part.  Once you have your smart part added, specify the path I mentioned above (~/_controltemplates/MyControls/MyControl.ascx).  You can also add user controls to your master pages, just like you would in ASP.NET.  Just create the reference and add it to the .master.

This may sound like a lot at this point but it's really not bad.  If there is enough interest in the article, I will try and get some code together in one package so people can use it as a starting point.  That is how you manually deploy a user control, we'll move onto how we do it with a solution package.

Deploying via Solution Package

For deployment, we will pretty much follow what we did in the deployment of the web part.  For more details on building solution packages, please see that post.  In this case a feature is not necessary unless you want to automate the deployment of the user controls onto an existing page.  The cab.ddf file only contains lines to copy the binary and .ascx file.

; ** MyControls.wsp  **
.OPTION EXPLICIT     ; Generate errors 
.Set CabinetNameTemplate=MyControls.wsp     
.set DiskDirectoryTemplate=CDROM ; All cabinets go in a single directory
.Set CompressionType=MSZIP;** All files are compressed in cabinet files
.Set UniqueFiles="ON"
.Set Cabinet=on
.Set DiskDirectory1=Package

Solution\manifest.xml manifest.xml

;binaries
bin\MyControls.dll MyControls.dll

;feature files

;template files
MyControl.ascx CONTROLTEMPLATES\MyControls\MyControl.ascx

The Manfiest.xml file will contain entries to copy your ascx file out as well as copy your binary to the GAC.  It will also add the Safe Control entry that you need.  Here is what the file would look like.  The TemplateFile element is used to specify every .ascx file that you want copied to the server.  More details on what you can do with the manifest file are here.

<Solution SolutionId="{FA34A0BE-FEAA-4750-9E82-B313F62C5CF9}"  xmlns="http://schemas.microsoft.com/sharepoint/" ResetWebServer="true">

  <TemplateFiles>

    <TemplateFile Location="CONTROLTEMPLATES\MyControls\MyControl.ascx" />

  </TemplateFiles>

  <Assemblies>

    <Assembly DeploymentTarget="GlobalAssemblyCache" Location="MyControls.dll">

      <SafeControls>

        <SafeControl Assembly="MyControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=03afd371f1d50a3a" Namespace="MyControls" TypeName="*" Safe="True" />

      </SafeControls>

    </Assembly>

  </Assemblies>

</Solution>

At this point, you build and deploy the .wsp file as described in my web part post.  Again, I strongly recommend reading that post if you are not familiar with any of the deployment steps yet.  The nice thing about having your user control in a solution package is that it is now completely portable.  It can be deployed to your other SharePoint servers very easily.  Once you have mastered the process, I recommend you start deploying your binaries to the bin folder with Code Access Security.  Once you have the solution package built, adding CAS is pretty easy and it saves you from having to kill your app pool every time you do a deployment.

The Enterprise Search component of MOSS 2007 and Search Server are great products and they can do a lot of great things out of the box, however I repeatedly see the same requests from clients.  I also see people having the same issues in the forums all the time.  I would like to summarize a list of things, we as a community, would like to see addressed in the future.  With the acquisition of Fast, a lot of these could become non-issues, but in the meantime this is what people are experiencing now.  Please add your comments or let me know anything you think I missed. 

  • Wildcard Searching - Yes, Enterprise Search supports it but not out of the box.  WildcardSearchWebPart helps, but it's just a hack and this functionality should be built in.  It doesn't help with People Search though.
  • Unseal classes in Microsoft.Office.Server.Search.Web - We are given limited functionality in displaying search results out of the box, help us out by not making these classes sealed so that we can add our own functionality.  You did this with the Federated Search classes and it was a step in the right direction.
  • Provide links to document libraries, sites, and editing of results containing documents.  The Document Link Handler helps with this.
  • Provide zip and download support - LiveLink has a lot of these types of options and users want them.
  • Allow custom sorting using managed properties in the CoreResultsWebPart.
  • Include faceted search out of the box.
  • Add keyword specific features to Full Text SQL Queries - It turns out people want wildcard searching and a variety of other features that word only when performing a keyword search.  Things like Best Bets, RSS, Keyword Highlighting, and Search Summary do not work when using Full Text SQL.
  • Display more accurate result counts - I know this is the nature of a way a search engine works.  In the case where I am using the BDC to index a table full of widgets, the end user always knows exactly how many widgets there are and he or she starts to ask questions.
  • Fix the page where you map crawled properties - It is not resizable and you can't see what you are picking when the crawled property has a long name.  It would also be nice if you could select more than one at a time.
  • Stop using WSS Search in MOSS - You can work around this, but there should be no reason why a call is made to OSSSearchResults.aspx on a MOSS server.  The WSS search page offers next to 0 customization options.
  • Provide a way to see which IFilters you have installed.
  • Provide and stsadm command to import/export Content Sources, Managed Properties, Crawled Properties, and Scopes.  Thank you SSSPPC for helping with that right now.

Ok, I know I am asking a lot, but just take a look at the forums, these are real issues people deal with every day.  I still enjoy working with the product every day and am so thankful that the community has given so much to make it better.  I am very excited what the next version of search will have in store for us.  However, these are things that should be addressed in one way or another lest companies start going out and buying Google Search Appliances.    If you have anything you would like to add, please leave a comment.  Thanks.

I ran into this issue a while back so I thought I would post on it.  I had a scenario where we wanted to create a simple content type to use as a base content type for all future content types at a comapany (i.e.: Company Base Document).  At the time we did not know what site columns we wanted included, so we didn't include any FieldRefs to site columns.  We just inherited from this type into another more project specific document type.  It seemed like a good idea at the time, but it of course caused a number of problems.

It appears that if you do not specify any FieldRef elements in your content type definition that any other fields further up in the inheritance tree will also not get inherited.  This caused things like the Title field (inherited from item) to not be included in our content type.  The lesson to be learned here is a) plan better and have your fields defined first and b) make sure that you always specify at least one FieldRef in your content type definition.  I am sure there is a technical reason for this, but I thought I would throw it out there in case someone else runs into the issue in the future.

Also if you haven't checked out my latest addon for Enterprise Search, go check it out.  I am pretty excited about it and the users I have shown it to so far have found a lot of value in it.

with 3 comment(s)
Filed under: ,