Sample event handler to set a field as a pr imary key (enforce no duplicates)

Got this as a request from a reader- how to prevent users from adding items with same titles as ones that already exist in the list.
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;

namespace SPSTIPS.SharePoint.EventHandlers
{
public class TitlePrimaryEventHandler:SPItemEventReceiver
{
const string TITLE_QUERY = @"<Query><Where><Eq><FieldRef Name=""Title"" /><Value Type=""Text"">{0}</Value></Eq></Where></Query>";
public override void ItemAdding(SPItemEventProperties properties)
{
if (properties.AfterProperties["Title"] != null)
{
//get the title of the new item
string currentTitle = properties.AfterProperties["Title"].ToString();
//get the web site object
using (SPWeb web = properties.OpenWeb())
{//get the current list
SPList list = web.Lists[properties.ListId];
//query the list to check if there are items with the same title
SPQuery q = new SPQuery();
q.Query = string.Format(TITLE_QUERY, currentTitle);
SPListItemCollection itemsWithSameTitle = list.GetItems(q);
//if there are items, cancel the add, and show an error to the user.
if (itemsWithSameTitle.Count > 0)
{
properties.Cancel = true;
properties.ErrorMessage = "There is already an item with the title \"" + currentTitle + "\ in this list".";
}
}}}}}
Getting user profile values that support multiple value I noticed today that the MSDN article "User Profiles Object Model Overview"
has a wrong code sample for the "correct way of retrieving a user profile property value". The code sample there will not work, because the object model in sharepoind doesnt have the methods and objects the sample code is using.
Therefore - here is my correct way:
if (Profile[this.UserProfilePropertyName].Count == 1)
return Profile[this.UserProfilePropertyName].Value.ToString();
else{
StringBuilder ret = new StringBuilder("");
UserProfileValueCollection values = Profile[this.UserProfilePropertyName];
System.Collections.IEnumerator allValues = values.GetEnumerator();
while(allValues.MoveNext())
{
ret.Append(allValues.Current.ToString());
ret.Append(";");
}
return ret.ToString();
}

Avoiding CSS caching issues

I had a problem in several projects where we changed the CSS and deployed it to the load balanced server environment, and still the old CSS file was cached somewhere, and we couldnt roll out the changes to the users.
Another problem on the same line are browsers that cache the css files, and users do not see the changes we make to the css until we tell them to clear their browser's cache.
To resolve this, we took a trick from Microsoft - notice how Microsoft always links to the css with a version number in sharepoint? this is done to avoid caching when you put a new version. My trick was to develop a custom web control (that I call the "CssInjector") that gets as a property the link to the css file, and renders that link with a random querystring. this forces the browsers (as well as IIS or other caching applications) not to cache the file.
The problem with this solutions is that it will have performance implications - slower loading of pages, so what I'd really like to do is what MS did - rely on a version number. However, I have yet to come up with the architecture that will let me manage version numbers in my solution package without having to manually setting the version every time. I thought of reading the version number from the CSS file itself, but that will introduce performance issues again - as the code will have to load and read the css file each time a page is opened.
So until I get a better solution, here is the code I am using for my web control:
using System;
using System.Web.UI.WebControls;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
namespace SharePointTips.SharePoint.WebControls
{
public class CssInjector : WebControl { private const string CSS_LINK_FORMAT = @"<link rel=""stylesheet"" type=""text/css"" href=""{0}"">"; private string _CSSFileLink = ""; public string CSSFileLink { get { return _CSSFileLink; } set { _CSSFileLink= value; } }
 protected override void Render(HtmlTextWriter writer) { if (this.CSSFileLink.Length > 0) { writer.Write(string.Format(CSS_LINK_FORMAT,this.CSSFileLink + "?k=" + GetUniqueKey(8))); } } public static string GetUniqueKey(int length) { //create an array of acceptable characters char[] chars = new char[62]; chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray(); StringBuilder result = new StringBuilder(length); Random rnd = new Random(); //create the random string in the specified length for (int i = 0;i < length; i++) { result.Append(chars[rnd.Next(chars.Length)]); } return result.ToString(); } } }
And here is how I use it in the master page: <%@ Register Tagprefix="SharePointTipsWebControls" Namespace="SharePointTips.SharePoint.WebControls" Assembly="SharePointTips.SharePoint.WebControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=xxxxxxxxxx" %>
<SharePointTipsWebControls:CssInjector runat="server" ID="SharePointTipsCSS1" CSSFileLink="/_layouts/SharePointTips/SharePointTipsCustomCss.css" />

Get a user from a list item:


To get a user we use the
SPFieldUserValue class, which accepts a SPWeb and a string value as parameters. The string value is the value from the list that contains the ID and the account name. Once we have that, we can use the SPFieldUserValue to get information about the user.
Example:

using (SPSite site = new SPSite("http://portal"))   
        {  
  using (SPWeb web = site.OpenWeb())   
     {

   SPList list = web.Lists["Example For Users"];
SPListItem item = list.Items[0];
SPFieldUserValue userValue = new SPFieldUserValue(web, item["User Name"].ToString());
if (userValue.User.LoginName == web.CurrentUser.LoginName)
                {
                    
//do something!
 }  
            }   
        }

Adding a value to a user field


This is the same, but in reverse. We use the same class (SPFieldUserValue ) and give it the values we want for the user.


using (SPSite site = 
new SPSite("http://portal"))   
        {   
            using (SPWeb web = site.OpenWeb())
               { 
                SPList list = web.Lists["Example For Users"];
  
                SPListItem item = list.Items[0];   
                SPFieldUserValue userValue = new SPFieldUserValue(web, web.CurrentUser.ID, web.CurrentUser.LoginName);   
                item["User Name"] = userValue;   
                item.Update(); 
            } 
        }

Code Practices - getting\setting values from\to the lookup and the hyperlink fields

How to get data from a field that is a lookup? how to set data into a field that is a hyperlink?
I don't believe I didn't write about this yet. It's so common!
For both cases, sharepoint object model exposes classes to help us get or set the data we want. These are the SPFieldLookupValue and the SPFieldUrlValue.

Example 1: Set the url field of a link


Use the
SPFieldUrlValue class to create an object that holds the url to link to, and the title to display:
SPList list = web.Lists["Links"];

SPListItem newLink = list.Items.Add();

SPFieldUrlValue value = 
new SPFieldUrlValue();

value.Description
= "test";

value.Url =
"http://www.microsoft.com/sharepoint";
newLink["URL"] = value;
newLink.Update();

Example 2: Get the url field of a link


Use the SPFieldUrlValue class to create an object that gets
the url and description:
SPList list = web.Lists["Links"];
SPListItem existingLink= list.Items[0];
SPFieldUrlValue value = 
newSPFieldUrlValue(existingLink[
"URL"].ToString());
stringlinkTitle = value.Description;
string linkURL = value.Url;

Example 3: Set the value of a lookup field for a known title and ID


In the following example I am using SPFieldLookupValue to set the value of a lookup field ("Group Name") to item "Program Operations", whose ID is 14:


SPList list = web.Lists["Branches"];

SPListItem newBranch = list.Items.Add();
newBranch["Title"] = "A New Branch";
SPFieldLookupValue newValue =new SPFieldLookupValue(14,"Program Operations");
newBranch["Group Name"] = newValue;
newBranch.Update();

Example 4: Get the value of a lookup field from an item


Here I am reading the value of the group name field (which is a lookup field in the branches list):
SPList list = web.Lists["Branches"];
SPListItem existingBranch= list.Items[0];
SPFieldLookupValue group = new SPFieldLookupValue(existingBranch["Group Name"].ToString());
int lookedUpItemID= group.LookupId;
string lookedUpItemTitle= group.LookupValue;

Using the SharePoint People Picker

A long time ago (beta2 time) I published a post about the people picker control (MOSS 2007 controls - have a bit of fun with the "people editor" form control!). Lately I had many comments on that post from people who have been having issues getting the value the user selected, and asked for another demo of using the control.
So here it is:
The code creates three controls - a PeopleEditor (from the Microsoft.SharePoint.WebControls namespace), a button and a textbox. The button has an click event that itirates over the users picked in the PeopleEditor and writes them to the text box. Simple!
public class PeoplePickerWebPart : System.Web.UI.WebControls.WebParts.WebPart
{
PeopleEditor pe;
TextBox t;
Button b;
protected override 
void CreateChildControls()
{
base.CreateChildControls();
pe = newPeopleEditor();
this.Controls.Add(pe);   
        b = new Button();   
        b.Text = "Click me to see the users";
this.Controls.Add(b);  
        b.Click += new EventHandler(b_Click);  
        t = new TextBox();   
        t.TextMode = TextBoxMode.MultiLine; 
        
this.Controls.Add(t); 
    }
void b_Click(object sender, EventArgs e)   
    {  
        
foreach(string ent in pe.CommaSeparatedAccounts.Split(','))  
        {  
            t.Text += ent + Environment.NewLine;  
        } 
    }

Using the SharePoint date control in a web part

After a lot of requests in comments in this blog, I figured this must be very hard to implement. So I tried, and found it quite easy. Here is a code sample of a web part that uses the sharepoint date control The code is simple - three controls - a DateTimeControl from the Microsoft.SharePoint.WebControls namespace, a button and a text box.
The button has a click event. On click, the event sets the text box to display the date that was selected in the DateTimeControl.
publicclassDatePickerWebPart : System.Web.UI.WebControls.WebParts.WebPart
   
{

DateTimeControl dtc;
TextBox t;
Button b;
protected override void CreateChildControls()
   
    {
          
base.CreateChildControls();
   
        dtc = new DateTimeControl();
   
        dtc.ID = "dtc" + this.UniqueID;
   
        
this.Controls.Add(dtc);
   
        b = new Button();
   
        b.Text = "Click me to see the date";

this.Controls.Add(b);
   
        b.Click += new EventHandler(b_Click);
   
        t = new TextBox();

this.Controls.Add(t);
   
    }

void b_Click(object sender,EventArgs e)
   
    {

        t.Text = dtc.SelectedDate.ToLongDateString();
   
    }

}
So far, I have no explanation, but I have a workaround. Make a call to the site in the code. I know - this adds process time and resources, but it beats having your users ask you about the "error tab".
In your code, add the following function, and call it just after the site creation code:
public static void OpenUrl(string url)

{

try

{

 HttpWebRequest request = (HttpWebRequest)

WebRequest.Create(url);

request.UseDefaultCredentials
= true;

HttpWebResponse
response = (HttpWebResponse)

request.GetResponse();

}

catch(Exception
ex) {

//do something with
the error

}

}
Here is an example how to use this
function:

public static void CreateSite(SPWeb parentWeb)

{

try
{
//get the templatefor the site using the GetWebTemplate function I wrote

SPWebTemplate template= SharePointFunctions.GetWebTemplate(parentWeb,

this.SiteTemplateName, ConfigVariables.SitesLCID);

//create the siteusing the class properties

newWeb = parentWeb.Webs.Add(this.ShortName,  this.SiteTitle,this.SiteDescription,ConfigVariables.SitesLCID,



template,



false,



false);

OpenSiteHomePage(newWeb.Url);     }

catch(Exception ex) {

//do something with the error

}

}

Just for me - excel macro to generate an XML

If you are here for a sharepoint tip, disregard this post- I am posting this to myself so I will not lose this code. Basically, it is an excel macro that genenrates an xml file from the current sheet. If you do want to use it, add a reference to microsoft xml in the VBA environment, and paste the code below to a module.
There are many way to improve it, but I just wrote this to quickly achieve a small task that I needed done.
Sub makeXml()

ActiveCell.SpecialCells(xlLastCell).Select

Dim lastRow, lastCol As Long

lastRow = ActiveCell.Row

lastCol = ActiveCell.Column
    Dim iRow, iCol As Long
    Dim xDoc As New DOMDocument Dim rootNode As IXMLDOMNode Set rootNode = xDoc.createElement("Root") Dim rowNode As IXMLDOMNode Dim colNode As IXMLDOMNode     'loop over the rows For iRow = 2 To lastRow Set rowNode = xDoc.createElement("Row") 'loop over the columns For iCol = 1 To lastCol If (Len(ActiveSheet.Cells(1, iCol).Text) > 0) Then Set colNode = xDoc.createElement(GetXmlSafeColumnName(ActiveSheet.Cells(1, iCol).Text)) colNode.Text = ActiveSheet.Cells(iRow, iCol).Text rowNode.appendChild colNode End If Next iCol rootNode.appendChild rowNode Next iRow xDoc.appendChild rootNode xDoc.Save ("c:\temp\temp.xml") set xDoc = Nothing End Sub Function GetXmlSafeColumnName(name As String) Dim ret As String ret = name ret = Replace(ret, " ", "_") ret = Replace(ret, ".", "") ret = Replace(ret, ",", "") ret = Replace(ret, "&", "") ret = Replace(ret, "!", "") ret = Replace(ret, "@", "") ret = Replace(ret, "$", "") ret = Replace(ret, "#", "") ret = Replace(ret, "%", "") ret = Replace(ret, "^", "") ret = Replace(ret, "*", "") ret = Replace(ret, "(", "") ret = Replace(ret, ")", "") ret = Replace(ret, "-", "") ret = Replace(ret, "+", "")     GetXmlSafeColumnName = ret End Function

Using DDWRT in xslt-based web parts

What is DDWRT?
well, its a script that microsoft packaged for it's xslt dataviews, that gives them more xslt power.
I needed to use the ddwrt functions in my content query web part, but I guess that the following approach will work in the search web parts as well.
Why do we need it?
I needed it to format the date I was getting back from the content query. The format I was getting back was ugly to the user (2007-06-27 15:52:00) and I wanted to format it, but I didn't want to write my own function.
So how to use it?
you need to add to your xslt the following namespace where the namespaces are declared (at the top of the xsl file, in the "xsl:stylesheet" tag):
xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime"

Then you can use the ddwrt's functions. For example, to format the date I used the following:
<xsl:value-of disable-output-escaping="no" select="ddwrt:FormatDate(string(@Modified), 3081, 5)" />
Note that in the FormatDate function I used 2 hard coded values - 3081 which is the LCID for Australia (so the date will be formatted to Australian date format) and 5 which specifies the what do I want to display - date, time, date and time ect. I have no idea what values give what, but I do know that 5 gives me the date and the time.

Update to ItemAdding fiasco

This is to let you know that I updated my posts about the event handlers and ItemAdding, after I did some more research and found the correct way to get and set item's properties during ItemAdding. I swear this method did not work during Beta2 when I posted my old posts, and I swear that contacts in Microsoft did tell me it was by design and not possible (I have the emails to show that).
However, it turned out that it is possible, and easy, and I posted an update and a code sample in the old posts:
Bad news - synchronous list events bug (or missing feature)
Synchronous Add List event (ItemAdding) will not give access to item properties

Adding custom fields to the Enhanced Content Quey Web Part (Beta 1.2)

I just got around to adding another important feature to the Enhanced Content Query Web Part and I uploaded it to codeplex.
I am sure you read my articles, or articles by other MVPS like Heather about showing custom columns in the content query webpart. This requires (as Heather's article shows) that you export the web part to a file, manually modify the file to add the fields you want, and import it back.
This annoyed me enough to add a property to my ECQWP that will allow you to add those fields without having to export-import.
In this version (1.2) you have two new properties in the propeties pane - Data Fields and Common View Fields. I have yet to find out how Data Fields are used, so you don't need to use that property - it is enough that you set the Common View Fields.
I will refer you to Heather's article to find out the format that the value of this property should be written in - how to get the field's internal name, and how to get the field's type. Just a quick reminder though - the value in that field is a pipe seperated value of fields, where each field is a coma seperated pair of field (internal) name and field type. For example, a text field with internal name "Comments" will be added like so:
Comments,Text
A choice field with internal name "Client_x0020_Name" will be added like so:
Client_x0020_Name,Choice
and if we want to add both of the above fields, it will look like so:
Comments,Text|Client_x0020_Name,Choice


As always, get the web part, installation instructions and source code from codeplex. Just be sure to download the most recent version.

Enhanced Content Query Web Part upgraded to Beta 1.1

I am happy to announce that the Enhanced Content Query Web Part project has progressed to Beta 1.1, with some very important fixes to major bugs. The web part now correctly prints out the item ID, has context menus for folders (not just documents) and supports displaying icons for documents. Be sure to read the documentation (installation instructions) and use the new XSLT that is provided there. The old XSLT will not solve any of the bugs I fixed.

Allowing InfoPath forms to redirect to pages in other site collections

In InfoPath 2007, when you publish a form as a web form, you have the option to specify several query string parameters. One of those parameters is the "Source" parameter, which is "The location to which the user will be redirected when the form is closed. The URL must be in the same site collection or an error will be returned."
The error mentioned is "The following location is not accessible, because it is in a different site collection:" and the url you specified.
So it seems that specifying a source from another site collection is impossible. But what if I have a site collection dedicated to forms? lets say "http://forms", to which I link from the intranet home page or another page, and I want that when the user closes the form, he will be redirected back to the page in the intranet (lets say "http://intranet") that we started from? I had to come up with a solution, and a simple one jumped to mind - a redirect page!
The trick is simple. Instead of specifying a link to "http://intranet" as the source parameter (which will cause an error), specify a link to a special page in the layouts folder, and give that page the query string that links to the page you want to return to.

Confused? here is an example:
Instead of linking to the form with this link:
http://forms/_layouts/FormServer.aspx?XsnLocation=http://forms/myforms/Forms/template.xsn&Source=http://intranet
Link to it like this:
http://forms/_layouts/FormServer.aspx?XsnLocation=http://forms/myforms/Forms/template.xsn&
Source=http://forms/_layouts/SPSTipsFormRedirector.aspx?target=http://intranet
As you can see from the links, the first one will cause an error because we are linking to another site collection, while the second one will work, because we are linking to the current site collection.
So the only thing left for me to show you is how to create the SPSTipsFormRedirector.aspx page that will handle the redirect to the page you specified in the red section in the second link:
Open your layouts folder (C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS) and create a new text file by the name SPSTipsFormRedirector.aspx. Open the file in a text editor such as notepad, and copy the following code:
<%
string target = Page.Request.QueryString["target"]; Page.Response.Redirect(target);
%>
<%
@ Page Language="C#" Inherits="System.Web.UI.Page" %>
<html>
</
html>

Repeate this in every front end server, and you are ready to write links to forms that, when closed, will go back to a page you specify in the link! Enhanced Content Query Web Part goes Beta 1! Hey Everyone!
This is to let you know that I released beta 1 of the Enhanced Content Query Web Part Project. Make sure you read the installation documentation to the end, because that is where you will find the sample XSLT that will cause the menus to appear.
Anyone want to volunteer to draw me a logo? Contact me!

Monday, June 04, 2007

Why isn't there a default value for lookup fields? Oh, lookup fields, how I hate you.
I just realized that it is not possible in the user inteface to set a default value for a lookup field. Is it that hard to do an interface that will show the user creating the field the options from the looked-up list and allow him to select a default value?
Apperantly it is. I guess Microsoft were worried about what happens if someone deletes the looked-up value.
Not content with my findings, I tried looking in the object model and discovered that you can set up a default value for a lookup field using the following code:
SPList docs = w.Lists["Documents"];

SPFieldLookup fl = (SPFieldLookup)docs.Fields["City"];

fl.DefaultValue = "1;#Mexico City";

fl.Update();


I will add this option to my Utility Pack when possible. Using the above code solves the issue almost totaly. When you use the web interface to add a list item or upload a document, the default value comes up in the lookup box. However (and this is the big one), from the office applications (both 2003 and 2007) the default value is ignored and the lookup box always begins at the "blank" option. Should I waste one of my MSDN support calls on this and ask MS for a fix? or should I just learn to live with it?

Meanings of variables in the context menu's script If you are building a web part that displays context menus for list items - just like the context menus that SharePoint builds out of the box, you need to know the meaning of the attributes that you need to add to the table that holds the item. I couldn't find any documentation or referance to it on the internet, so I decided to publish the results of my diggings.
If you look at the source html of the built-in web part,you will see that every item with a context menu looks like this:
<table height="100%" cellspacing="0"
class="ms-unselectedtitle" onmouseover="OnItem(this)" id="1"
ctxname="ctx1"
url="/Documents/Sample%20Document.docx"
dref="Documents"
perm="0x7fffffffffffffff"
type=""
ext="docx"
icon="icdocx.gif|Microsoft Office Word|SharePoint.OpenDocuments"
otype="0"
couid=""
sred=""
cout="0"
hcd=""
csrc=""
ms="0"
ctype="Document"
cid="0x0101006121B9A5B6A75F49AD8620D335B47E62"
uis="1024"
surl="">

The question is asked - what do those attribute mean? what is "cout" and what is "otype"? what values are expected there?
After some research, here is my dictionary:
  • ctxname: Name of the context object on the page that holds the context for the list that you are connecting to.
  • url: link to the document (can be either relative or absolute.
  • dref:File Directory Referance - the relative folder path the item\document is in. For example if I have a document library called "documents" with the url "http://portal/documents", the items in the root folder will have "documents" as the dref value, while items in a folder called "test" will have "documents\test".
  • perm: has to do with permissions, but I didn't figure out how it works yet.
  • type: Seems not to be used for items - but to create menu seperators or different kind of menus.
  • ext: The document extension. For example "doc", "docx", "ppt" and so on.
  • icon: This seems to control the "edit" menu item. Here you specify the icon that will be displayed next to the "edit in..." menu item, the text that will appear as the name of the application (edit in Microsoft Word) and the script that will be used to edit the item (I have no idea what possible values can be used here). All of this seperated by the "|" character.
  • otype: Current Item FSObj Type. This seems to have something to do with the check in-check out menus, that will not be displayed if this is 1, but I could not figure out what is the logic here.
  • COUId: Current Item Checked Out User Id. (ID of the user in the web site the item is from)
  • sred: Server File Redirect. Allows you to specify a URL that will be used instead of the file's url when the link is clicked or when a menu option is used. For example, if you put a link to another document, all menu actions will be redirected to that document instead.
  • cout: Current Item Checkedout To Local. If the item is checked out to a local (offline) folder and not to the database. 0 for false, 1 for true.
  • hcd: Controls if a menu item to navigate to the "/_layouts/updatecopies.aspx" will be added. I am guessing this has to do with records management (update copies of the document when it was changed).
  • csrc: Copy source link. If the item is a copy of an item in a records management site, this holds the link to the source, and adds the menu item for going to the source file (Go to source).
  • ms: Current Item Moderation Status. Also has to do with if its checked out or not, and if its draft or not.
  • ctype: The type of the item. I could not find where that is used.
  • cid: Content Type ID. The ID of the content type for the item. also seems to be used for list menus to determine the content type to create when a user clicks on the new item menu.
  • uis: Current Item UI String. Seems to hold the version number, where the major version is a multiplication of 512, and the minor version is the reminder (the script gets the minor version number by doing "%512"). For example, a value of 512 is version 1.0 while 513 is version 1.1 and 1024 is version 2. Does this mean we have a version limit of 511 minor versions in sharepoint? According to the "introduction to versioning", this is only the default and the administrator can change this. This sounds fishy considering the "512" limit is hard coded in the javascript.
  • surl: Source Url. This dictates where the page will go back to after a menu item is clicked.

Content Query Web Part xsl for showing the xml

I keep forgetting this, and I think I had to figure it out from scratch at least 4 times allready, so I may as well blog it.
The thing is - last time I figured it out I wanted to blog about it, and then saw that someone beat me to it - Heather Solomon already wrote about it in her blog, and I seem to remember someone else wrote a similar script.
The thing that throws me off every time is realizing that the XML that the content query web part builds for each item has the values in the attributes. So what you need to do is write xslt code to display the attributes.
Heather's article says exactly how to modify the files, so if you need that kind of help, go there now. Below is the code that I am using (slightly modified):
<xsl:template name="ShowXML" match="Row[@Style='ShowXML']"
mode="itemstyle">


 <xsl:for-each select="@*">

</br>

Name: <xsl:value-of
select="name()" /> Value:<xsl:value-of select="." />

</xsl:for-each>

</xsl:template>
I have just managed to do something really cool with the web part, but I have to refine it a bit before I publish it. I promise this will knock your socks off!
skip to main | skip to sidebar

Sharepoint Tips And Tricks

Sample event handler to set a field as a pr imary key (enforce no duplicates)

Got this as a request from a reader- how to prevent users from adding items with same titles as ones that already exist in the list.
using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.SharePoint;
namespace SPSTIPS.SharePoint.EventHandlers

{

public class TitlePrimaryEventHandler:SPItemEventReceiver

{

const string TITLE_QUERY
= @"<Query><Where><Eq><FieldRef Name=""Title"" /><Value
Type=""Text"">{0}</Value></Eq></Where></Query>";

public override
void ItemAdding(SPItemEventProperties properties)

{

if (properties.AfterProperties["Title"]
!= null)

{

//get the title
of the new item

string currentTitle
= properties.AfterProperties["Title"].ToString();

//get the web site
object

using (SPWeb web
= properties.OpenWeb())

{

//get the current
list

SPList list = web.Lists[properties.ListId];

//query the list
to check if there are items with the same title

SPQuery q = new
SPQuery();

q.Query = string.Format(TITLE_QUERY,
currentTitle);

SPListItemCollection
itemsWithSameTitle = list.GetItems(q);

//if there are items,
cancel the add, and show an error to the user.

if (itemsWithSameTitle.Count
> 0)

{

properties.Cancel
= true;

properties.ErrorMessage
= "There is already an item with the title \"" + currentTitle + "\ in this list".";

}

}

}

}

}

}

Getting user profile values that support multiple value

I noticed today that the MSDN article "User Profiles Object Model Overview" has a wrong code sample for the "correct way of retrieving a user profile property value". The code sample there will not work, because the object model in sharepoind doesnt have the methods and objects the sample code is using.
Therefore - here is my correct way:
if (Profile[this.UserProfilePropertyName].Count == 1)

return Profile[this.UserProfilePropertyName].Value.ToString();

else

{

StringBuilder ret
= new StringBuilder("");

UserProfileValueCollection
values = Profile[this.UserProfilePropertyName];

System.Collections.IEnumerator
allValues = values.GetEnumerator();

while(allValues.MoveNext())

{

ret.Append(allValues.Current.ToString());

ret.Append(";");

}

return ret.ToString();

}

Avoiding CSS caching issues

I had a problem in several projects where we changed the CSS and deployed it to the load balanced server environment, and still the old CSS file was cached somewhere, and we couldnt roll out the changes to the users.
Another problem on the same line are browsers that cache the css files, and users do not see the changes we make to the css until we tell them to clear their browser's cache.
To resolve this, we took a trick from Microsoft - notice how Microsoft always links to the css with a version number in sharepoint? this is done to avoid caching when you put a new version. My trick was to develop a custom web control (that I call the "CssInjector") that gets as a property the link to the css file, and renders that link with a random querystring. this forces the browsers (as well as IIS or other caching applications) not to cache the file.
The problem with this solutions is that it will have performance implications - slower loading of pages, so what I'd really like to do is what MS did - rely on a version number. However, I have yet to come up with the architecture that will let me manage version numbers in my solution package without having to manually setting the version every time. I thought of reading the version number from the CSS file itself, but that will introduce performance issues again - as the code will have to load and read the css file each time a page is opened.
So until I get a better solution, here is the code I am using for my web control:
using System;

using System.Web.UI.WebControls;

using System.Collections.Generic;

using System.Text;

using System.Web.UI;

namespace SharePointTips.SharePoint.WebControls{

public class CssInjector : WebControl

{

private const string
CSS_LINK_FORMAT = @"<link rel=""stylesheet"" type=""text/css"" href=""{0}"">";

private string _CSSFileLink
= "";

public string CSSFileLink

{

get { return _CSSFileLink;
}

set { _CSSFileLink
= value; }

}

protected override
void Render(HtmlTextWriter writer)

{

if (this.CSSFileLink.Length
> 0)

{

writer.Write(string.Format(CSS_LINK_FORMAT, this.CSSFileLink + "?k=" + GetUniqueKey(8)));

}

}

public static string
GetUniqueKey(int length)

{

//create an array
of acceptable characters

char[] chars = new
char[62];

chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();

StringBuilder result
= new StringBuilder(length);

Random rnd = new
Random();

//create the random
string in the specified length

for (int i = 0; i < length; i++)

{

result.Append(chars[rnd.Next(chars.Length)]);

}

return result.ToString();


      }

}

}
And here is how I use it in the master page:
<%@ Register
Tagprefix="SharePointTipsWebControls" Namespace="SharePointTips.SharePoint.WebControls"
Assembly="SharePointTips.SharePoint.WebControls, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=xxxxxxxxxx" %>
<SharePointTipsWebControls:CssInjector runat="server" ID="SharePointTipsCSS1" CSSFileLink="/_layouts/SharePointTips/SharePointTipsCustomCss.css" />