Recently we build some custom display and edit forms for a list, but need to secure the view and edit rights for the listitems. We didn’t wanted to use listitem permissions, because the list contained People or Group columns, where we could derive the permissions from. It wouldn’t be user friendly if they had to define the People or Group fields and set listitem permissions. Sooner or later, this would run out of sync and end users would be confused.

So, after some research on the internet and the MSDN library, I found out that a SPFieldUserValueCollection contains an Exists method, to which you have to pass a delegate to a method. This delegate method will accept a Predicate(Of T) on a List(T). So in our case the delegate method had to accept a SPFieldUserValue. In return it will give a boolean value defining if the user is contained in the People or Group field.

A People or Group field can contain SharePoint Groups, Domain Users, Domain Groups and/or Distribution Groups. Below you can find an implementation for the first three types of values. The method can be used for a single value and multiple value People or Group Field. As soon as it returns true, it will skip checking other values in the multiple value form. You can replace the web object with SPContext.Current.Web offcourse and keep in mind that you have to implement the DomainGroupContainsCurrentUser method. Implementations of this using LDAP can be found at the internet.

Private Function CurrentUser(ByVal fuv As SPFieldUserValue) As Boolean
     If fuv.User IsNot Nothing AndAlso Not fuv.User.IsDomainGroup Then
         Return (fuv.User.ID = web.CurrentUser.ID)
     ElseIf fuv.User IsNot Nothing AndAlso fuv.User.IsDomainGroup Then
         Return DomainGroupContainsCurrentUser(fuv.User.Name)
     ElseIf web.SiteGroups.GetByID(fuv.LookupId).ContainsCurrentUser() Then
         Return True
     Else
         Return False
     End If
End Function

Calling this method is pretty simple. Suppose you have a SPFieldUserValueCollection, fuvc, then you just enter fuvc.Exists(AddressOf CurrentUser) in VB.NET. This approach can be used for every SP…Collection because they all derive from List(T).

Everyone recognizes the problem with dropdownlists which are dependant on eachother. We select something from dropdownlist A and then dropdownlist B can be filled with items based on your selection in A. Simple to build with an automatic postback and event handlers on the controls, but nobody likes the page refresh. So time to get our hands on jQuery and JSON.

Let’s suppose we have an application page in which we have two dropdownlist:
<wssawc:DVDropDownList ID=”dropdownlistA” runat=”server”></wssawc:DVDropDownList>
<wssawc:DVDropDownList ID=”dropdownlistB” runat=”server”></wssawc:DVDropDownList>

With a simple jQuery script, we can hook up the two dropdownlists, so that a change in dropdownlistA triggers the loading of items in dropdownlistB without refreshing the page. We use the jQuery selector, select[id$='dropdownlistA'], because we know that the above tag is rendered by SharePoint as a select control. The ID however is prepended with a unique part, so we let jQuery search for select controls where the ID ends on our ID. Always use the complete ID you specify in the design file to avoid multiple results in jQuery select statements.

(more…)

I updated the post on adding or deleting nodes on Quick Launch. There were some problems with the previous code when adding nodes as childnodes of existing nodes.

You can find the original post, ‘Add or delete nodes on Quick Launch’, here.

Recently I had to query a list in a meeting workspace, but I got an empty resultset for every meeting except the first one. Seems that tools like the U2U CAML Query Builder and SharePoint Manager 2007 were only able to query the items of the first meeting. Time to start googling and after some time I found a post of Sanket Shah describing the same problem I had. There is one property, ‘query.MeetingInstanceId’, you have to set on the query, if you want to get items out of a list other than those of the first meeting. It won’t work if you add the Instance ID column to your CAML query.

Code example (list is an SPList):

Dim query As SPQuery = New SPQuery()
query.Query = YourCAMLQuery
query.MeetingInstanceId = instanceID
Dim results As SPListItemCollection = list.GetItems(query)

Ever wanted to add an item to a list inside a Meeting Workspace? Well, I did and I couldn’t find instructions on how to. Don’t understand me wrong, I did know how to add an item to a list, but what about the instance id. This is pretty simple, every list in a workspace has a field, called ‘Instance ID’, type Integer. So just set the value of this field for each item to the correct ID and the item only shows under the specified meeting date.

Small code example (workspace is a SPWeb object):

Dim list As SPList = workspace.Lists(“aListName”)
Dim listItem As SPListItem = list.Items.Add()
listItem(“Title”) = “aItemTitle”
listItem(“Instance ID”) = 1
listItem.Update()

Update 8 june 2009: It only works for new items. Instance ID is a readonly field, so don’t try to reset it and update the listitem. It simply keeps the old value. There must be some special setting method implemented to have this behaviour.

When you add Content Types with Synchronous (such as ItemAdding and ItemUpdating) or Asynchronous (such as ItemAdded and ItemUpdate) Event Receivers to a document library for example, you will sometimes notice some unwanted behaviour. Whenever the Content Type is added to the document library and Content Types is enabled, it will hit these Event Receivers immediatly. This is because the template behind the content type is also added in the hidden folder ‘Forms’. A simple codeline in the Event Receiver Methods can skip further method execution:

If properties.AfterUrl.StartsWith(properties.ListTitle & “/Forms/”) Then Exit Sub

Last post, I showed you how to disable or empty the Quick Launch. In this post, I will show you how to add nodes to or delete nodes from Quick Launch programmatically. You have some options to add the nodes as first node, before another existing node, after an existing node or as last node in the navigation. Via some overloading methods, you can decide if it is going to be a parent node or a child node. The same goes for deleting nodes, delete a parent node or a child node of an existing node.

Update 17 august 2009: There were some problems with the methods below. I’ve updated all the methods.

First of all we define a small enumeration for the node position used in the AddQuickLaunchNode methods.

Public Enum NodePosition
    First = 0
    After = 1
    Before = 2
    Last = 3
End Enum

We also need some private helper methods to Add a node, Delete a node and Search a node in a SPNavigationNodeCollection, like the Quick Launch.

Private Shared Sub AddQuickLaunchNode( _
         ByRef nnc As SPNavigationNodeCollection, _
         ByVal nodeName As String, ByVal nodeUrl As String, _
         ByVal isExternal As Boolean, _
         ByVal position As NodePosition, ByVal abNodeName As String)
     Select Case position
         Case NodePosition.First
             ‘Add node as first node
            nnc.AddAsFirst(New SPNavigationNode(nodeName, nodeUrl, isExternal))
         Case NodePosition.After, NodePosition.Before
             ‘Select previous node
             Dim previousNode As SPNavigationNode = _
                GetQuickLaunchNode(nnc, abNodeName, False, position)
             If Not previousNode Is Nothing Then
                 ‘Add node before previousNode
                nnc.Add(New SPNavigationNode(nodeName, nodeUrl, isExternal), _
                    previousNode)
             Else
                 ‘PreviousNode not found, add node as last node
                nnc.AddAsLast(New SPNavigationNode(nodeName, nodeUrl, isExternal))
             End If
         Case NodePosition.Last
             ‘Add node as last node
            nnc.AddAsLast(New SPNavigationNode(nodeName, nodeUrl, isExternal))
     End Select
     ‘Update node collection
    nnc.Parent.Update()
End Sub

Private Shared Sub DeleteQuickLaunchNode( _
         ByRef nnc As SPNavigationNodeCollection, _
         ByVal nodeName As String)
     ‘Select node
     Dim node As SPNavigationNode = _
        GetQuickLaunchNode(nnc, nodeName, False)
     If Not node Is Nothing Then
         ‘Delete node
        nnc.Delete(node)
     End If
End Sub

Private Shared Function GetQuickLaunchNode( _
         ByRef nnc As SPNavigationNodeCollection, _
         ByVal nodeName As String, _
         Optional ByVal createIfNotFound As Boolean = True, _
         Optional ByVal position As NodePosition = NodePosition.After)
     Dim node As SPNavigationNode = Nothing
     Dim previousNode As SPNavigationNode = Nothing
     ‘Loop over all nodes in collection
     For Each nn As SPNavigationNode In nnc
         If nn.Title = nodeName AndAlso position = NodePosition.Before Then
             ‘Node found, assign previousnode and exit the for loop
            node = previousNode
             Exit For
         ElseIf nn.Title = nodeName AndAlso position = NodePosition.After Then
             ‘Node found, assign currentnode and exit the for loop
            node = nn
             Exit For
         End If

         ‘Set node to current node in case of position = nodeposition.before, so
         ‘that the node is already set when nn.title = nodename (can’t go back in nnc)
         If position = NodePosition.Before Then
             previousNode = nn
         End If
    Next

     If node Is Nothing AndAlso createIfNotFound Then
         ‘Node not found, creating node
        node = New SPNavigationNode(nodeName, “”)
        nnc.Parent.Children.AddAsLast(node)
        nnc.Parent.Update()
     End If

     Return node
End Function

Finally, we define four public methods for adding and deleting. One for a parent node add, one for a child node add, one for a parent node delete and one for a child node delete.

Public Shared Sub AddQuickLaunchNode( _
         ByRef web As SPWeb, _
         ByVal nodeName As String, ByVal nodeUrl As String, _
         Optional ByVal isExternal As Boolean, _
         Optional ByVal position As NodePosition = NodePosition.Last, _
         Optional ByVal abNodeName As String = Nothing)
     ‘Add Quick Launch Node on web
    AddQuickLaunchNode(web.Navigation.QuickLaunch, _
        nodeName, nodeUrl, isExternal, position, abNodeName)
End Sub

Public Shared Sub AddQuickLaunchNode( _
         ByRef web As SPWeb, _
         ByVal nodeName As String, ByVal nodeUrl As String, _
         ByVal parentNodeName As String, _
         Optional ByVal isExternal As Boolean, _
         Optional ByVal position As NodePosition = NodePosition.Last, _
         Optional ByVal abNodeName As String = Nothing)
     ‘Select parent node
     Dim parentNode As SPNavigationNode = _
        GetQuickLaunchNode(web.Navigation.QuickLaunch, parentNodeName)
     If Not parentNode Is Nothing Then
         ‘Add Quick Launch Node under parentNode on web
        AddQuickLaunchNode(parentNode.Children, _
            nodeName, nodeUrl, isExternal, position, abNodeName)
     End If
End Sub

Public Shared Sub DeleteQuickLaunchNode( _
         ByRef web As SPWeb, ByVal nodeName As String)
     ‘Delete Quick Launch Node on web
    DeleteQuickLaunchNode(web.Navigation.QuickLaunch, nodeName)
End Sub

Public Shared Sub DeleteQuickLaunchNode( _
         ByRef web As SPWeb, ByVal nodeName As String, _
         ByVal parentNodeName As String)
     ‘Select parent node
     Dim parentNode As SPNavigationNode = _
        GetQuickLaunchNode(web.Navigation.QuickLaunch, parentNodeName, False)
     If Not parentNode Is Nothing Then
         ‘Delete Quick Launch Node under parentNode on web
        DeleteQuickLaunchNode(parentNode.Children, nodeName)
     End If
End Sub

Just put all these methods in a shared class and you can add nodes to, delete nodes from, disable or clear the Quick Launch menu of a web with minimal effort and coding.

Not very technical, but a simple post on how to disable or empty the Quick Launch programmatically. In this example, web is a SPWeb object.

Disable:
web.QuickLaunchEnabled = False

Empty:
For i As Integer = web.Navigation.QuickLaunch.Count – 1 To 0 Step -1
    web.Navigation.QuickLaunch.Delete(web.Navigation.QuickLaunch(i))
Next

I had a project where I had defined several managed paths with a wildcard inclusion. Via a WebService, there were SiteCollections provisioned under these managed paths and in the RootWeb of the Root Site Collection of the Web Application, I stored a list of references (urls) to these Site Collections. Sometimes the provisioning was taking quite long and the caller of the WebService received a timeout. So I let him send me a unique identifier and stored this also in the list of references. When he called again, I checked that list with a CAML query to avoid duplicated provisionings. But with the url and the below code, I always got a nasty error message: “The site X could not be found in the Web application SPWebApplication Name=Y Parent=SPWebService”. And I was pretty sure it was there.

SPSite site = New SPSite(urlOfSiteCollection);

So I looked at the overloads and noticed you can pass a Guid as well. Changed the reference list to store the id of the Site Collection and changed my code into:

SPSite site = New SPSite(idOfSiteCollection);

Gone was the error message and it worked.

My opinion, when you have to store information of a SiteCollection, so you can use it later on to rebuild a SPSite object, you better use the id (Guid) instead of the url (String).

Recently I had to create a new Site Collection and add multiple Site Collection Administrators. I searched the internet offcourse and found a solution by Keith Richie. You can merge step 1 and 2 by using the EnsureUser method provided by the OM.

user = rootWeb.EnsureUser(“domain\loginname”)
user.IsSiteAdmin = True
user.Update()

You can use the same method to set the Primary and Secondary Site Collection Administrator.

site.Owner = rootWeb.EnsureUser(“domain\loginname”)
site.SecondaryContact = rootWeb.EnsureUser(“domain\loginname”)

This way you don’t have to pass the name and email of the users.

Next Page »