March 10, 2010 - 20:13, by Steven Van de Craen
Categories: .NET, MOSS 2007, Search Server 2008, SharePoint 2007, WSS 3.0
You can add pages to meeting workspaces through the User Interface (browser) and then you get the options to manage them. It is also possible to add pages through code, but there’s one catch you need to be aware of and that is the InstanceId of the meeting. This ID is used in meeting workspaces to distinguish content per instance of a recurring event. So each meeting instance has its own set of documents and list items througout each List or Library on the site.
Consider the following code:
string url = "http://moss/meeting1";
string newPageUrl = String.Empty;
using (SPSite site = new SPSite(url))
{
using (SPWeb web = site.OpenWeb())
{
SPMeeting meeting = SPMeeting.GetMeetingInformation(web);
meeting.AddPage(String.Format("Page {0}", DateTime.Now.ToString("HHmmss")), meeting.InstanceId, out newPageUrl);
}
}
By adding the page using the InstanceId of the current meeting workspace it will be added to a folder named after the InstanceId in the Pages library. This has the following effects (also applies to items in the other lists and libraries on the meeting workspace):
- the page cannot be managed through the UI (sorting, removal, etc)
- the page is visible only in a single meeting instance in case of recurring events
If you want to add a page regardless of instance you can provide a zero (0) to the instanceId parameter of the AddPage method and it will behave identical to pages added through UI.
January 29, 2010 - 15:09, by Steven Van de Craen
Categories: .NET, Content Types, Debugging, MOSS 2007, Office 2003, Office 2007, Search Server 2008, SharePoint 2007, WSS 3.0A while ago I stumbled upon a serious design limitation regarding Content Types and centralized document templates. What then followed was a series of testing, phone calls with Microsoft, finding alternative solutions and deep dive into Office Open XML.
Request from the customer
“We want to use MOSS 2007 to create a collaboration site per project for our 400+ projects. These collaboration sites all use the same Content Types and document templates. We want to centrally manage those document templates so that we don’t need to make the same change 400+ times.”
Approach
Due to sizing we architected a solution with a dozen of Site Collections that would each hold a collection of project sites. We ‘Feature’-ized our Content Types and Site Columns so that they could quickly be activated on all Site Collections and used by the child sites. Document templates would be stored in a central document library and we would link to them in the Content Types on the project sites.
First issue
Linking to document templates really doesn’t play well with the Document Information Panel (DIP). I have blogged about this here:
Centralizing Document Templates in a library- Document Information Panel shows incorrect properties
We proposed a solution where the document templates in the central library would be pushed to the Content Type resource folder on site level. The code to perform the push would have to connect to the Site Collections, copy the template to the resource folder (http://sitecollectionurl/_cts/contenttypename) and link template and Content Type together.
When a Site Content Type is associated with a List it will be a List Content Type inheriting from the Site Content Type and the document template will be copied to the List Content Type resource folder (http://sitecollectionurl/listurl/Forms/contenttypename).
Second issue
Did I tell you that the column values (metadata) have different values based on the project site ? So when a project site is created we automatically update the List Content Type Column default value with the values for that specific project site. Unfortunately this is not supported when working with Office 2007 file formats because they only react on changes to the Site Column.
Consider the following scenario:
1) Set up a document library with a Content Type that has a text column with a default value
2) Upload a new .doc or .docx as Content Type template
TEST 1) Create a new document:
.DOC: the DIP will contain the text column with the default value
.DOCX: the DIP will contain the text column with the default value
3) In SharePoint, modify the default value of the text column
TEST 2) Create a new document:
.DOC: the DIP will contain the text column with the updated default value
.DOCX: the DIP will contain the text column with the original default value
Microsoft confirmed that this is by design.
Third issue
When designing our document templates with Content Controls mapped to our SharePoint fields we didn’t know that internally in the DOCX file it uses a GUID for mapping the Content Control with the SharePoint Metadata XML. For fields (Site Columns) created in the UI or through API this is the SPWeb.ID of where they were created. For fields created declaratively through Features this is the SPList.ID of where their Content Type is associated to.
So some things to notice
- Creating a single document template with Content Controls mapped to your declaratively added Fields cannot be used in two different Document Libraries because the Content Controls lose the connection with Field (because the ID of the List is different and not updated in the Content Control)
- The solution here is to create your fields in the UI or through the API (this could be in a Feature Activating event)
- Copying a document template across Site Collections means different Web ID’s so it also affects fields created in the UI or the API
Finally
In the end we wrote some wrapper classes for Office 2007 file formats using System.IO.Packaging that would manipulate our document templates once they were copied over to a different Site Collection. We also rewrote our Features to create our Fields through the API (SPWeb.Fields.AddFieldAsXml()).
- Remove the SharePoint Metadata XML so that association of the Content Type to a List it would be regenerated automagically
- Loop through every Content Control and find to which Field they were mapped using information in it’s XPath. Then we would update the GUID’s in the Content Control to match the SPWeb.ID
Next time I’ll definitely take these design limitations into account. Lessons learned I’d say !
January 28, 2010 - 13:31, by Steven Van de Craen
Categories: .NET, MOSS 2007, Search Server 2008, SharePoint 2007
A refresher
The ListViewWebPart is used for displaying contents of a List or Document Library on both the default View pages (such as AllItems.aspx, etc) and also on other pages in the same web.
When a List View Web Part is added to a page (say the home page of that web) a hidden view (SPView) is created dedicated to that Web Part. When switching to another view in the Web Part Properties it really copies all the settings of the selected view into the dedicated hidden view. Changes made afterwards to the ‘selected view’ will not be pushed to the ‘dedicated view’.
Today’s question
How to programmatically modify the Toolbar settings for a ListViewWebPart ?
Answer
It all boils down to getting a reference to the hidden dedicated SPView and making modifications to it. There are three options that you can set the Toolbar to:

Full Toolbar
This option translates to “Standard” for the SPView.ToolbarType property.
Summary Toolbar
This option translates to “Freeform” for the SPView.ToolbarType property. Additionally you must specify a CAML string that is used for the rendering of the toolbar.
The default CAML string for a Links List looks like this:
<IfHasRights><RightsChoices><RightsGroup PermAddListItems=\"required\" /></RightsChoices><Then><HTML><![CDATA[ <table width=100% cellpadding=0 cellspacing=0 border=0 > <tr> <td colspan=\"2\" class=\"ms-partline\"><IMG SRC=\"/_layouts/images/blank.gif\" width=1 height=1 alt=\"\"></td> </tr> <tr> <td class=\"ms-addnew\" style=\"padding-bottom: 3px\"> <img src=\"/_layouts/images/rect.gif\" alt=\"\"> <a class=\"ms-addnew\" ID=\"idHomePageNewLink\" href=\"]]></HTML><URL Cmd=\"New\" /><HTML><![CDATA[\" ONCLICK=\"javascript:NewItem(']]></HTML><URL Cmd=\"New\" /><HTML><![CDATA[', true);javascript:return false;\" target=\"_self\">]]></HTML><HTML>Add new link</HTML><HTML><![CDATA[</a> </td> </tr> <tr><td><IMG SRC=\"/_layouts/images/blank.gif\" width=1 height=5 alt=\"\"></td></tr> </table>]]></HTML></Then></IfHasRights>
No Toolbar
This option translates to “None” for the SPView.ToolbarType property.
The code
I assume you are familiar with getting an instance to the ListViewWebPart and then retrieving the SPView instance using either reflection on the private SPView member or through the public Web and View GUID properties.
Next you can change the Toolbar schema XML through reflection as follows:
SPView view = ...;
Type viewType = view.GetType();
XmlNode toolbarNode = viewType.InvokeMember("GetNodeFromXmlDom", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, view, new object[] { "Toolbar" }) as XmlNode;
toolbarNode.Attributes["Type"].Value = "Standard";
view.Update();
January 21, 2010 - 17:16, by Steven Van de Craen
Categories: .NET, Forms Server, InfoPath
Using xsd.exe you can generate a Class from your form schema (xsd) and then deserialize a form to an instance of that class. This makes it a lot easier to interact with its data.

The code for serialization and deserialization might look like this:
public static T Deserialize<T>(Stream s)
{
T result = default(T);
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (XmlTextReader reader = new XmlTextReader(s))
{
result = (T)serializer.Deserialize(reader);
}
s.Close();
return result;
}
public static T Deserialize<T>(string s)
{
return Deserialize<T>(new MemoryStream(Encoding.UTF8.GetBytes(s)));
}
public static string Serialize<T>(T o)
{
string result = null;
XmlSerializer serializer = new XmlSerializer(typeof(T));
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.Encoding = Encoding.UTF8;
using (MemoryStream stream = new MemoryStream())
{
using (XmlWriter writer = XmlTextWriter.Create(stream, settings))
{
serializer.Serialize(writer, o);
}
stream.Flush();
result = Encoding.UTF8.GetString(stream.ToArray());
}
return result;
}
However you lose the original processing instructions at the top of the XML file. If you want to keep those either do custom serialization using an XmlWriter or do some kind of merge code with the original XML and the XML coming from serialization.

Quite obvious if you think about it but I keep forgetting it :)
November 12, 2009 - 11:58, by Steven Van de Craen
Categories: .NET, SharePoint 2003, SharePoint 2007
April 13, 2009 - 14:09, by Steven Van de Craen
Categories: .NET, Search Server 2008, SharePoint 2007, MOSS 2007, WSS 3.0
The system metadata can be changed to any value desired using the object model. I did notice a bug that the "Created By” (aka Author) field wouldn’t update unless also the “Modified By” (aka Editor) field was set (either to a new or it’s current value).
SPListItem item = ...;
item[“Created By”] = web.EnsureUser(“luisb”).ID;
item[“Modified By”] = item[“Modified By”];
item[“Created”] = DateTime.Now.AddYears(-3);
item[“Modified”] = DateTime.Parse(“01/01/2000”);
item.UpdateOverwriteVersion();
- I could only get it to work using UpdateOverwriteVersion and not with SystemUpdate
- You can use the internal name or ID of the field if working with a different language pack/installation
- Works both on lists and libraries (not that I doubted this really)
March 31, 2009 - 21:34, by Steven Van de Craen
Categories: .NET, SharePoint 2007, Search Server 2008, Custom Field Types, MOSS 2007, WSS 3.0, Advanced Computed Field
Introduction
This project originally started as ‘TitleLinkField’ because I needed a way to display the Title field as a hyperlink to the document in a document library, but it ended up being more than just that so I chose a more generic name for it.
I had some experience with Custom Field Types but event then I spent too many hours (even days) on figuring this one out. It started with standard functionality such as Calculated Field and Computed Field, both having their flaws and limitations. I quickly realised that Custom Field Types might be the only way to tackle the scenario at hand.
Use
When creating a field based on this type (“Advanced Computed Field”) you need to provide two properties; FieldRefs (MSDN FieldRefs Element (List)) and DisplayPattern (MSDN: DisplayPattern Element (List)). The former requires a list of referenced fields that exist in the collection of our field while the latter contains CAML used for displaying our field. See the MSDN pages on both elements for the schema and possible values.
What better way than demonstrating by showing ?
Sample 1: Title linked to document

<FieldRefs>
<FieldRef Name="ServerUrl" />
<FieldRef Name="Title" />
</FieldRefs>
<DisplayPattern>
<HTML><![CDATA[<a href="]]></HTML>
<Field Name="ServerUrl" />
<HTML><![CDATA[" title="]]></HTML>
<Field Name="ServerUrl" />
<HTML><![CDATA[">]]></HTML>
<Field Name="Title" />
<HTML><![CDATA[</a>]]></HTML>
</DisplayPattern>
Sample 2: Title with ECB

<FieldRefs>
<FieldRef Name="Title" />
<FieldRef Name="_EditMenuTableStart" />
<FieldRef Name="_EditMenuTableEnd" />
</FieldRefs>
<DisplayPattern>
<FieldSwitch>
<Expr>
<GetVar Name="FreeForm" />
</Expr>
<Case Value="TRUE">
<HTML>-</HTML>
</Case>
<Default>
<Field Name="_EditMenuTableStart" />
<SetVar Name="ShowAccessibleIcon" Value="1" />
<Field Name="Title" />
<SetVar Name="ShowAccessibleIcon" Value="0" />
<Field Name="_EditMenuTableEnd" />
</Default>
</FieldSwitch>
</DisplayPattern>
Sample 3: Random formatting

<FieldRefs>
<FieldRef Name="MyChoiceField" />
</FieldRefs>
<DisplayPattern>
<HTML><![CDATA[<span style="background-color: red;">]]></HTML>
<Field Name="MyChoiceField" />
<HTML><![CDATA[</span><input type="button" value="Click me" />]]></HTML>
</DisplayPattern>
You can learn a lot by examining the default fields in a document library or list. A tool such as Stramit SharePoint 2007 Caml Viewer is invaluable here.
Technical
A Custom Field Types derives from a parent class (MSDN: Custom Field Classes). Furthermore you have to define the ParentType field declaratively in the FieldTypes XML definition. It is advised (maybe even required) for them to be the same type.
Choose the ‘parent class’ that most closely matches your requirements. If you need a ‘text field with regular expression validation’ then go for SPFieldText because that has a lot of textbox relation functionality (especially on the New, Edit and Display Forms).
The ‘ParentType’ field seems to be the key for CAML related functionality (such as the AllItems View). In my case setting it to “Text” meant that my field didn’t retrieve the values of the referenced fields so it needed to be “Computed”.
The main issues with the standard Computed Field is that it is invisible from any view; Site Columns, Field in a Content Type, etc. and can only be created declaratively via XML/CAML or through code. Other than these issues the Computed Field did exactly what was needed; render columns in a way that I wanted via the RenderPattern. It turns out that the visibility of a Custom Field Type can be controlled in the declarative XML via the ”ShowOn…” fields.
For storing and retrieving the DisplayPattern and FieldRefs properties my custom field type exposes two methods that make calls to internal SharePoint methods:
public void SetInnerXmlForNode(string nodeName, string innerXml)
{
Type fldType = this.GetType();
XmlDocument doc = new XmlDocument();
doc.LoadXml(String.Format("<{0}>{1}</{0}>", nodeName, innerXml));
MethodInfo miSetXml = fldType.GetMethod("SetInnerXmlForNode", BindingFlags.Instance | BindingFlags.NonPublic);
miSetXml.Invoke(this, new object[] { nodeName, doc.DocumentElement.InnerXml });
}
public string GetInnerXmlForNode(string nodeName)
{
string result = null;
Type fldType = this.GetType();
MethodInfo miGetXml = fldType.GetMethod("GetNodeFromXmlDom", BindingFlags.Instance | BindingFlags.NonPublic);
XmlNode resultNode = miGetXml.Invoke(this, new object[] { nodeName }) as XmlNode;
if (resultNode != null)
{
StringBuilder sb = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.ConformanceLevel = ConformanceLevel.Fragment;
using (XmlWriter writer = XmlTextWriter.Create(sb, settings))
{
resultNode.WriteContentTo(writer);
}
result = sb.ToString();
}
return result;
}
Download and installation
If you’re interested in reusing or modifying the code feel free to do so. If you just want this installed and available on your SharePoint farm then go for the WSP and deploy via STSADM.
STSADM -o addsolution -filename VNTG.CustomFieldTypes.AdvancedComputedField.wsp
STSADM -o deploysolution -name VNTG.CustomFieldTypes.AdvancedComputedField.wsp -allowgacdeployment -immediate -allcontenturls
Word of caution
There isn’t a lot of validation on the input of the XML properties. It has to be valid XML but that’s about it. Please follow the schema for FieldRefs and DisplayPattern to make sure you don’t break other functionality. It cannot ‘bring down the farm’ or anything, but it could definitely mess up the rendering of the View.
Have fun with it !
December 19, 2008 - 16:36, by Steven Van de Craen
Categories: .NET, Search Server 2008, SharePoint 2007
The title of this blog post is referring to this screen in the List Settings:
It interests me because it
- allows you to control ownership of the item
- is only available to Lists but not to Document Libraries
- doesn't use unique permissions but some other mechanism
One thing it mentions is that it only works for users without Manage Lists permission on the list. So even if I manage to unravel its' secrets it isn't a waterproof solution (compared to unique item permissions for example). Still, if SharePoint has the notion of ownership it is definitely worth a closer look ! Maybe there's an opportunity here ?
It definitely isn't security by obscurity; if I navigate directly to an item that's not mine I still can't access it.
Peeking under the hood reveals that this setting is stored in SPList.ReadSecurity and SPList.WriteSecurity. Those MSDN articles contain all the details so I'll try not to be repetitive
ReadSecurity
Possible values:
- 1 - All users have Read access to all items.
- 2 - Users have Read access only to items that they create.
WriteSecurity
Possible values:
- 1 — All users can modify all items.
- 2 — Users can modify only items that they create.
- 4 — Users cannot modify any list item.
What about Document Libraries ?
For a Document Library these properties always have a value of "1" meaning everyone has read and write access (provided their permission level is sufficient). Since there is no interface in the Library Settings they cannot be changed. However nothing is stopping me from writing a few lines of code to update the settings, is there ?
using (SPSite site = new SPSite("http://moss/demo1"))
{
using (SPWeb web = site.OpenWeb())
{
SPList list = web.Lists["Shared Documents"];
list.ReadSecurity = 2;
list.WriteSecurity = 2;
list.Update();
}
}
Guess what ? It worked fine in the standard SharePoint Views and queries but not for WebDAV (Explorer View, etc). Probably the reason they only use it on SharePoint Lists.
An opportunity ? Not really since it doesn't act as real security in all scenario's. At least my curiosity is satisfied.
December 15, 2008 - 16:08, by Steven Van de Craen
Categories: .NET, Search Server 2008, SharePoint 2007, Debugging, Event Receivers, MOSS 2007, WSS 3.0
Today I got into some code reviewing of an Item Event Receiver using Enterprise Library for data access. The problem occurred when registering the Event Receiver for a SharePoint List using the object model (SPList.EventReceivers.Add)
Exception has been thrown by the target of an invocation.
Here's a simplified view into the code:
public class MyEventReceiver: SPItemEventReceiver
{
MyDataAccess da = new MyDataAccess();
public override void ItemAdded(SPItemEventProperties properties)
{
...
}
}
public class MyDataAccess
{
public MyDataAccess()
{
Microsoft.Practices.EnterpriseLibrary.Data.DatabaseFactory.CreateDatabase("MyDB");
}
...
}
Apparently when the Event Receiver is registered it instantiates the MyDataAccess class which in turn calls the EntLib method in its constructor. This is what causes the exception.
A solution is to instantiate the MyDataAccess class in the methods rather than on event receiver initialisation:
public class MyEventReceiver: SPItemEventReceiver
{
public override void ItemAdded(SPItemEventProperties properties)
{
MyDataAccess da = new MyDataAccess();
...
}
}
October 10, 2008 - 12:50, by Steven Van de Craen
Categories: .NET, Forms Server, SharePoint 2007
I have been struggling with this for too long and the result is still not quite satisfying but it'll have to do for now.
I have a Web Control that renders an InfoPath form using the Microsoft.Office.InfoPath.Server.Controls.XmlFormView control and want the form to open with a specific View rather than the default one.
Browsing the web I found the following candidates:
- xmlformView.XmlForm.ViewInfos.SwitchView(...)
- xmlformView.XmlForm.ViewInfos.Initial = ...
Call one of these too early in the life cycle of your Control and the XmlForm instance is not yet available. Call them too late and the ViewInfos will not be changeable. I haven't really figured this one out yet...
I thought I had it when I was doing this in the Initialize event of the xmlformView instance, but no luck. Perhaps I'm doing something wrong ?
public class MyControl : WebControl
{
Microsoft.Office.InfoPath.Server.Controls.XmlFormView xmlFormView;
protected override void OnInit(EventArgs e)
{
xmlFormView = new XmlFormView();
xmlFormView.Initialize += new EventHandler<InitializeEventArgs>(xmlFormView_Initialize);
}
protected void xmlFormView_Initialize(object sender, InitializeEventArgs e)
{
if (null != xmlFormView.XmlForm)
xmlFormView.XmlForm.ViewInfos.Initial = xmlFormView.XmlForm.ViewInfos["View 2"];
}
}
Anyway, I kinda gave up on this and tricked my way out using javascript:
"Write some javascript that runs on page load and triggers the 'switch view' javascript method."
<script type='text/javascript'>
var hid = document.createElement('input');
hid.setAttribute('id', 'hidInitialView');
hid.setAttribute('type', 'hidden');
hid.setAttribute('value', 'View 2');
setTimeout('Toolbar.HandleViewDropdown(hid);', 1000);
</script>
I have implemented this in a custom control inheriting the XmlFormView. Although I don't like doing it this way it's here to download: XmlFormView.cs
And I'm still hoping for a better solution... ;)
References:
Next >>