Wednesday, January 30, 2008

Controlling the "add web part" menu

Hi, Recently I had a customer that asked me to control what web parts users can add to a web part zone.
First, I thought of setting up a “suggested web parts” group for that zone, and go to web part gallery and associate the web parts he wanted to this group.
To make your web part zone focus on a specific web part group, open the page in SharePoint Designer and set the zone's property “QuickAdd-GroupNames” to: "MyGroup"
(should turn out like: <WebPartPages:WebPartZone runat="server".... QuickAdd-GroupNames="MyGroup"..... >)
Also - to hide current site lists from the popup window, in the same manner set QuickAdd-ShowListsAndLibraries="false".
Although this makes it easier to add these web parts (not having to search the list for them) the customer insisted on have 2-3 web parts "as one click to add each".
What he meant was, to have along the "add web parts" menu something like: Add CalendarPlus Add Picture Library Viewer....
Well, I found out this can be done by some javascript manupulation.
We have to follow these steps:
1 - Get the uniqueId of the web part zone.
2 - Get the add web part popup result for adding the web part you want.
3 - Call __doPostBack(uniqueId,result);
4 - Done!
Wait... I know... the web part zone id is pretty easy to figure out... but...
How can you tell the dialog result?!?
Well, I have a trick that will allow you to create a popup with that information...
1 - Open the page in sharepoint designer
2 - Add this script block at the end of the file (before the body tag ends)
function __doPostBack2(eventTarget, eventArgument)
alert("zone id = "+eventTarget + "\ndialog result = " + eventArgument);
__original(eventTarget, eventArgument);
var __original = __doPostBack;
__doPostBack = __doPostBack2;
Now, we found out all the information we needed...
All we have left to do is to add above every web part zone a new "add WP" button using the zone's ID and the web part's id you found out earlier.

The HTML would look something like this:
<input onclick="__doPostBack(this.uniqueId,this.wpid);" type="button" value="Add Calendar" uniqueid="ctl00$PlaceHolderMain$Left$ctl00" wpid="lvwpguid:06c0b9b5%252D3a45%252D404a%252Db834%252D0677341aad5c;%25D7%259C%25D7%2595%25D7%2597%2520%25D7%25A9%25D7%25A0%25D7%2594">

Well, I was amazed it actually works, but it does!

Hope it’s not too complicated, it’s been a challenge trying to put it into words…

Sunday, January 20, 2008

Cache profile for WCM internet site

Hmmm… this took me a while to understand… but the solution is rather simple.

I wanted to activate cache profile for my customer WCM site, so that only anonymous users will see a shared cached version of the page.

Naturally – all anonymous users see the same version of the page and do not have editing rights nor can they see draft versions – so typically all you need to do is:
1: Go to site actions à site settings àsite collection administration à site collection output cache
2: Set the anonymous cache profile to “public internet (purely anonymous)
3: Click OK

And you are done.

But – I noticed there were no improvement in system-load (FE and DB servers) per requests and overall performance did not improve.

I went back to site collection output cache settings page and selected the “Enable debug cache information on pages” checkbox. Enabling this will append to the HTML a debug message stating when the current page was rendered and if cache was in use.

To my surprise (or not) I found this message:
"8 Output cache not used. Reason: User can view an unpublished version of the current page."

This meant that caching was not activated for anonymous users because they had access for unpublished version of current page? Not!

After contacting the WCM team, I got an interesting reply from Kai Lee (Thanks!) saying that this message will come up if the current user (anonymous in my case) have editing access or can view draft versions ANYWHERE IN THE SITE COLLECTION!

Well, I still have to check where anonymous users have this access in my site collection (worrying…) but there is (thank god!) a nice workaround.

Going into the “public internet” cache profile allows you to set "Allow Writers to view cached content" to “true”.

This is important to understand: for authenticated users, who can edit pages and suppose to view draft versions – this is BAD. But for my scenario – where I have no interest of allowing anonymous users to view these contents anyhow – it is a good workaround and what do you know?
It solved the problem!!!

So thanks Kai Lee again,

Hope this will save some time to the rest of you…

Thanks, Shai Petel.

Tuesday, January 8, 2008

Working with sub folders in list

Hi all,

As you all know, in 2007 SharePoint introduced a new feature to lists - creating items in sub folders. But apparently the API was not modified in an easy way to support working with this feature...

In my last project I had to create sub-folders in lists and list items in these folders from code, and found that it is not so simple.

We are doing a rating solution for SharePoint items (coming soon on our web site, for more information contact, and wanted to create a new "item rating" list to store all ratings for all items on the same site.
For performence issues we wanted to create a sub folder for each list in the site and a second level folder for each item and create all ratings and comments in that folder.

The outcome of that project was 3 utilities methods that manages all that I need for working with folders and I thought it would be nice to publish them here - mainly because when I googled for it I didn’t find any good posts for that.

Here is how to use the utilities methods in your project:

SPList list = GetOrCreateList(web);//create your own...
//Create subfolder named "first level"
SPListItem folder = GetOrCreateFolderInList(list, list.RootFolder, "first level");
//Create subfolder named "second level"
folder = GetOrCreateFolderInList(list, folder.Folder, "second level");
//Create item in "second level" folder
SPListItem item = AddListItemInFolder(folder);
//Update meta data of new item
item["Rating"] = 5;
item["Comments"] = "";
item["ListID"] = list.ID.ToString("N");
item["ItemID"] = "ITEM_ID";
item["UserName"] = "shai...";

Here is the code for the utilities:

private SPListItem AddListItemInFolder(SPListItem parentFolder)
return parentFolder.ListItems.Add(parentFolder.Folder.ServerRelativeUrl, SPFileSystemObjectType.File);
private SPListItem GetOrCreateFolderInList(SPList parentList, SPFolder parentFolder, string folderName)
folderName = folderName.ToLower();
string parentFolderUrl = parentFolder == null ? "":parentFolder.ServerRelativeUrl.ToLower();
//Look in existing folders
foreach (SPListItem f in parentList.Folders)
if (f.Folder.ServerRelativeUrl.ToLower() == parentFolderUrl + "/" + folderName)
return f;//Found! return it!

//not exists - create
SPListItem folder = parentList.Items.Add(parentFolderUrl, SPFileSystemObjectType.Folder, folderName);
return folder;

Well, hope this helps you - it sure did help me :)
Shai Petel,