Monday, October 30, 2006

Preventing event handler recursion

One of the problems I had with list event handlers was event recursion.

I wrote an event handler to handle updating items. I wanted to check some values, and then set a value in a field based on a set of rules. The problem was- when I set the value of the field and updated the item, the ItemUpdated and ItemUpdating events fired up again, and again my code updated the item and so on and so on. I also wanted to avoid having the list item change version number following my code-update. that update was supposed to be part of the version that the user just created by updating. Otherwise, every user update of the list item would bump the version number twice! Similarily, I didnt want the code to show that an administrator changed the item. I wanted it to look as if the user did the change to that field.

So after some research, here is my solution: item[fieldnamehere] = fieldvaluehere; this.DisableEventFiriing(); item.SystemUpdate(false); this.EnableEventFiring();

This piece of code updates the item without firing the current event handler events, and doing the update using the SystemUpdate(false) method ensures that version number doesnt increase and the update is considered as if it was part of the user's update.

Do you like the new look? check out the new options!

I moved to blogger beta, which means I now have label support, so you can browse my articles better using the "Labels" links on the right of the screen. Also you will note that the archive is more user friendly!.
I Just wish that they would add RSS to the labels so that people can register to each category seperatly. I guess thats why they call it labels instead of categories...maybe in the future.

I would like to draw your attention (especialy my RSS readers) to a new link on the side of my blog here - a link to a job network. This is an interesting thing our friend from www.thedailywtf.com is trying - a network of people who read blogs to help each other get jobs.
The idea is simple- if you are reading my blog, it means you care enough about sharepoint to come to my site and read about it. This probably means you are a good sharepoint employee and employers are looking for people like you.
Face it - the very fact that you are reading my blog (or any tech blog) is a recommendation by itself.
Thats what HiddenNetwork is all about - connecting active & passive people in the community. And since the employers are also going to hear about that site from bloggers, it means that you stand a better chance to find work in a decent working place.

So if you are interested, and if you are looking for developers or consultants, go there and post an ad. I dont care if you use my link or not (I do get money if you do) since I am not blogging for a living like others do, but what I do care about is how damn hard it is to get good employees these days!

Saturday, October 28, 2006

Another shameless plug - SharePoint-Desktop synchronization solution For 2007 and 2003

I have to tell you about the new product from my good friends at KWizCom (ok, not so new - but I didn't have a chance to check it before now).

Their new product (ShareGO) is a great solution to synchronize documents from SharePoint 2003 and 2007(!!!) document libraries. They add a button to Internet Explorer: This allows you to configure a 2-way sync that includes:

  • Download new/updated server files
  • Upload locally updated files/local new files
  • Manage collisions - files that were updated both on the SharePoint server and locally.
  • Provide local recycle-bin for server-deleted files

Check out their web site. They have some movies that show how the product works, and I noticed a new support option in their site- you can chat with them and ask them questions.

A last note - if you have some recommendations for products you want me to review, let me know (comments to this blog are always read!).

Thursday, October 26, 2006

GAC "Access denied" Solution + Free "post-build" Script for Event Handlers

I have been working on an event handler, and I wrote a nice little post-build script to install it everytime I build it:

path = "E:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\"
gacutil.exe /if "$(TargetPath)"
copy "$(ProjectDir)\app.config" "C:\WINDOWS\ASSEMBLY\GAC_MSIL\$(TargetName)\[ASSEMBLY_VERSION]__[ASSEMBLY_PUBLIC_TOKEN]\$(TargetFileName).config"
RecycleAppPools.vbs

  1. Note that you have to replace some things in the code -[ASSEMBLY_VERSION] and ]__[ASSEMBLY_PUBLIC_TOKEN]
  2. Note that I have a configuration file called "app.config" that I am copying into the GAC after deploying the dll.
  3. Also note that I have my "RecycleAppPools.vbs" file in the .net sdk bin folder. This script recycles all application pools automatically, and takes less time than an IISReset.
    To create this file, create a new text file in the bin folder and paste the following code (rename the file to "RecycleAppPools.vbs"):
    Set locator = CreateObject("WbemScripting.SWbemLocator") 
    Set Service = locator.connectserver(strServer, "root/MicrosoftIISv2") 
    Set APCollection = Service.InstancesOf("IISApplicationPool") 
    For Each APInstance In APCollection 
        APInstance.Recycle 
    Next 

All was well and good, but suddenly I began to get errors - the GAC wouldnt let me install the DLL and would tell me 'access denied' when I tried to delete it from the GAC or replace it.

After some internet research I found the solution - stop the indexing service.

Now my little script is running smoothly, and I am developing and debugging my event handler with no problems.

Oh, yeah - I wrote a small application to register the event handler to a list. Anyone interested? I can publish it - it supports both command line operation, and a GUI interface.

Back from vacation, and Shane gives us office web parts!

For those of my readers who have been wondering why I havent posted anything in the last several weeks, it is because I was visiting family and friends in Israel and therefore did not discover anything new about sharepoint.

I returned to find that Shane (a sharepoint mvp) has discovered something I was worried about - how to add the office web components web parts (pivot chart, pivot table and worksheet) to MOSS2007

  • First find the cab file (microsoft.office.dataparts.cab) for the web part from a 2003 server
  • Copy the file to the 2007 server
  • Run the following command on the 2007 server:
    stsadm.exe -o addwppack -filename "c:\program files\common files\micrsofot shared\web server extensions\60\wppacks\microsoft.office.dataparts.cab" -globalinstall

I will soon be back with my own sharepoint tips, so be patient!

Monday, October 02, 2006

Creating a "Search Results Web Part" that searches on parts of the words

Here is the problem - We are using the profile database to create a phonebook application. SharePoint search only finds people if you type an exact match of the person name. For example, Searching for "Isha" will not bring my profile back since my name is "Ishai".

My customer wanted a prefix search so that searching for the beggining will return all people begining with the letters. Also, searching for parts of other properties such as job description should do the same (example - searching for "cons" will return anyone who is a consultant)

After much research, here is my solution:
Basically, what I do is override the built-in search result web part, and then modify the search query it runs. The only downside for this is that the default web part doesnt use XSL to format the results and doesnt have all the cool properties that the "PeopleCoreResultsWebPart" have. But with some work you can add the needed properties yourself.
  1. Create a new web part project
  2. Override the "Microsoft.SharePoint.Portal.WebControls.SearchResults" control
  3. Override the "OnInit" event (or, alternatively you can override the "GenerateQueryString" event)
  4. Add the following code:

    protected override void OnInit(object sender, EventArgs e){
    //add the wildcard support so that we will find everyone who starts with the typed keyword
    this.QueryTemplateWherePart += @"OR CONTAINS(#WeightedProps, '""%__keywordinputnoboundaryquote__%*""')";
    //add the filter to search only for people and not documents or page
    this.QueryTemplateWherePart += @"AND ""DAV:contentclass""='urn:content-class:SPSPeople'";
    base.OnInit(sender, e);
    }

Here is a sample of a class you can use:
using System;
using System.Data;
using System.IO;
using System.Web;
using System.Collections;
using System.Xml;
using System.Xml.Xsl;
using System.Xml.Serialization;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.Office.Server.Search.Query;
using Microsoft.Office.Server.Search.WebControls;
using Microsoft.Office.Server.UserProfiles;


namespace SPSTips.SharePoint.WebParts
{
    [XmlRoot(Namespace = "SPSTips.SharePoint.WebParts")]
    public class PeopleSearchResultsWebPart : Microsoft.SharePoint.Portal.WebControls.SearchResults
    {
        protected override void OnInit(object sender, EventArgs e)
        {
            //add the wildcard support so that we will find everyone who starts with the typed keyword
            this.QueryTemplateWherePart += @"OR CONTAINS(#WeightedProps, '""%__keywordinputnoboundaryquote__%*""')";
            //add the filter to search only for people and not documents or pages
            this.QueryTemplateWherePart += @"AND ""DAV:contentclass""='urn:content-class:SPSPeople'";
            base.OnInit(sender, e);
        }
        
        
        protected override void CreateChildControls()
        {
            Label lbl = new Label();
            lbl.Text = this.QueryTemplateWherePart;
            lbl.Attributes.Add("style", "display:none");
            this.Controls.Add(lbl);
            base.CreateChildControls();
        }
   }
}
update - I found that the web part doesnt support property search and I am working on a workaround. wait for updates.