Archive

Archive for June, 2012

SharePoint: Getting a SPField with no exceptions to be thrown

June 29th, 2012 No comments

    As you probably know, a SharePoint field has a few names: display name, internal and static. The display name usually differs from the internal and static ones. In some exotic cases, the internal and static names differ from each other too. List’s fields can be reached through the SPList.Fields collection of the SPFieldCollection class.

Get SPField by different names in SharePoint 2007

In SharePoint 2007 the SPFieldCollection exposes a few methods to get a field by its known display or internal name, but not the static name. These methods are the indexer of the SPFieldCollection that accepts the field’s display name, the GetFieldByInternalName method accepting the internal name, and the GetField method accepting both display and internal names. Unfortunately, all these methods are case sensitive and throw an exception if the field with the passed display or internal name wasn’t found. I don’t like to wrap every piece of code into try-catch, so, for SharePoint 2007 applications I’m involved in, when it’s possible I use the simple method shown below:

public static SPField GetFieldByName(SPList spList, string displayNameOrInternalNameOrStaticName)
{
    displayNameOrInternalNameOrStaticName = displayNameOrInternalNameOrStaticName.ToLower();

    foreach (SPField spField in spList.Fields)
    {
        if (spField.Title.ToLower() == displayNameOrInternalNameOrStaticName)
            return spField;
        if (spField.InternalName.ToLower() == displayNameOrInternalNameOrStaticName)
            return spField;
        if (spField.StaticName.ToLower() == displayNameOrInternalNameOrStaticName)
            return spField;
    }

    return null;
}

// how to use
// ...
    using (SPSite spSite = new SPSite("some site url"))
        using (SPWeb spWeb = spSite.OpenWeb())
        {
            SPList  spList  = GetListByUrl(spWeb, "Lists/Products");
            SPField spField = GetFieldByName(spList, "product name"); // the field's real display name is Product Name
            // do something
        }
// ...

*Note: find the GetListByUrl method in the previous blog post – SharePoint: Getting SPList with no exceptions to be thrown

The GetFieldByName accepts a field’s all possible names including the static name, it’s not case sensitive and returns null if the sought-for field doesn’t exist.

Of course, I’m aware that enumerating fields takes more time than retrieving them from the SPFieldCollection’s internal hashtables so as the built-in methods do. But when time isn’t so crucial for a particular piece of code, I prefer using the GetFieldByName method. In addition I don’t have an alternative for the GetFieldByName when I know only the field’s static name.

Get SPField by different names in SharePoint 2010

In SharePoint 2010 the new TryGetFieldByStaticName method has been added to the SPFieldCollection class. So, as the method’s name implies, we get a field by its static name, and no one exception even will be thrown in case the field doesn’t exist. Thus, for SharePoint 2010 I’ve modified the GetFieldByName as follows:

public static SPField GetFieldByName(SPList spList, string displayNameOrInternalNameOrStaticName)
{
    SPField spFieldByStaticName = spList.Fields.TryGetFieldByStaticName(displayNameOrInternalNameOrStaticName);
    if (spFieldByStaticName != null)
        return spFieldByStaticName;

    displayNameOrInternalNameOrStaticName = displayNameOrInternalNameOrStaticName.ToLower();

    foreach (SPField spField in spList.Fields)
    {
        if (spField.Title.ToLower() == displayNameOrInternalNameOrStaticName)
            return spField;
        if (spField.InternalName.ToLower() == displayNameOrInternalNameOrStaticName)
            return spField;
        if (spField.StaticName.ToLower() == displayNameOrInternalNameOrStaticName)
            return spField;
    }

    return null;
}

Check whether a field exists

To check whether a field exists, we can use the following method based on the GetFieldByName:

public static bool FieldExist(SPList spList, string displayNameOrInternalNameOrStaticName)
{
    return GetFieldByName(spList, displayNameOrInternalNameOrStaticName) != null;
}
Related posts:

SharePoint: Getting a SPList with no exceptions to be thrown

June 15th, 2012 No comments

List 'some list name' does not exist at site with URL 'some site url'.

    Getting a SPList object in code, I prefer not using an indexer of the SPWeb.Lists collection (SPListCollection) as it throws the above exception every time when the list with the specified name wasn’t found in the collection. It should be noted, however, that in SharePoint 2010, there is the TryGetList method, that has been added to SPListCollection and which returns null if the list isn’t presented in the collection. But I still use my own simple methods free of ‘not found’ exceptions and compatible with both SharePoint 2007 and 2010.

Get SPList by title

The first method returns a SPList object by specified title or null if nothing is found:

public static SPList GetListByName(SPWeb web, string listName)
{
    listName = listName.ToLower();
    foreach (SPList spList in web.Lists)
        if (spList.Title.ToLower() == listName)
            return spList;
    return null;
}

// usage
// ...
    using (SPSite spSite = new SPSite("some site url"))
        using (SPWeb spWeb = spSite.OpenWeb())
        {
            SPList spList = GetListByName(spWeb, "Products");
        }
// ...

Get SPList by url

Unfortunately, list title tends to be changed in the course of time. Unlike title, list url is unchangeable in list’s life time. So, the use of url (or its part) for list search is more reliable. The second method exactly uses url to find a list:

public static SPList GetListByUrl(SPWeb web, string url)
{
    url = url.ToLower();
    foreach (SPList spList in web.Lists)
        if (spList.RootFolder.Url.ToLower().EndsWith(url))
            return spList;
    return null;
}

// usage
// ...
    using (SPSite spSite = new SPSite("some site url"))
        using (SPWeb spWeb = spSite.OpenWeb())
        {
            SPList spList = GetListByUrl(spWeb, "Lists/Products");
        }
// ...

I hope these methods would be useful for somebody else.

Related posts:

SharePoint: How to hide All Site Content links for unprivileged users

June 8th, 2012 No comments

    If you need to hide All Site Content links for unprivileged users, follow the steps shown below.

All Site Content Links

Your application’s master page is to be modified. If you use a built-in master page (for example, v4.master for 2010 or default.master for 2007), I recommend to create a full copy of the built-in one and deploy it. These steps are described in the article SharePoint: How to create a custom master page.

In the master page, locate all places where _layouts/viewlsts.aspx is pointed out. You’ll likely find several controls (for example, a MenuItemTemplate, a SPLinkButton and so on), which render All Site Content links one way or another. These controls can be divided into three groups described below.

The controls exposing the PermissionsString property

The controls expose such opportune property as PermissionsString defining a permission set the user must have in order to see the content the controls provide. For this group of controls PermissionsString is likely set by default to ViewFormPages. That is, if the user has permission to view forms, views and application pages, and enumerate lists, he will have access to All Site Content links displayed by these controls. So, set PermissionsString to ManageWeb in order that the links in question would be visible only for users able to perform administration tasks for the Web site (e.g. content managing, features activating and deactivating and so on). For example, within your copy of v4.master you’ll definitely run into such controls as MenuItemTemplate and ClusteredSPLinkButton declared as follows:

<SharePoint:SiteActions id="SiteActionsMenuMain" runat="server" ...>
  <CustomTemplate>
    <SharePoint:FeatureMenuTemplate ID="FeatureMenuTemplate1" runat="server" ...>
      ...
        <SharePoint:MenuItemTemplate runat="server" 
          id="MenuItem_ViewAllSiteContents"
          Text="<%$Resources:wss,quiklnch_allcontent%>"
          Description="<%$Resources:wss,siteactions_allcontentdescription%>"
          ImageUrl="/_layouts/images/allcontent32.png"
          MenuGroupId="300"
          Sequence="302"
          UseShortId="true"
          ClientOnClickNavigateUrl="~site/_layouts/viewlsts.aspx"
          PermissionsString="ViewFormPages"
          PermissionMode="Any" />
      ...
    </SharePoint:FeatureMenuTemplate>
  </CustomTemplate>
</SharePoint:SiteActions>

The control represents an item in the drop-down Site Actions menu.

View All Site Content Site Actions Menu Item

Another control is ClusteredSPLinkButton

<asp:ContentPlaceHolder id="PlaceHolderQuickLaunchBottom" runat="server">
  ...
  <SharePoint:UIVersionedContent id="PlaceHolderQuickLaunchBottomV4" UIVersion="4" runat="server">
    <ContentTemplate>
      <ul class="s4-specialNavLinkList">
        ...
        <li>
          <SharePoint:ClusteredSPLinkButton
            id="idNavLinkViewAllV4"
            runat="server"
            PermissionsString="ViewFormPages"
            NavigateUrl="~site/_layouts/viewlsts.aspx"
            ImageClass="s4-specialNavIcon"
            ImageUrl="/_layouts/images/fgimg.png"
            ImageWidth=16
            ImageHeight=16
            OffsetX=0
            OffsetY=0
            Text="<%$Resources:wss,quiklnch_allcontent_short%>"
            accesskey="<%$Resources:wss,quiklnch_allcontent_AK%>"/>
        </li>       
      </ul>
    </ContentTemplate>
  </SharePoint:UIVersionedContent>
</asp:ContentPlaceHolder>

The control represents a link in the Quick Launch Bar.

All Site Content Quick Launch Link

So, change the PermissionsString property of the above controls to ManageWeb.

The controls, which are wrapped in a SPSecurityTrimmedControl

The controls lies in SPSecurityTrimmedControl. SPSecurityTrimmedControl renders the content it contains (Html or other controls) depending on the current user’s permissions. It exposes the same PermissionsString property. Thus, set the property to ManageWeb. Within a master page based on v4.master there is a SPLinkButton surrounded by the SPSecurityTrimmedControl, see the following markup:

<asp:ContentPlaceHolder id="PlaceHolderQuickLaunchTop" runat="server">
  <SharePoint:UIVersionedContent UIVersion="3" runat="server">
    <ContentTemplate>
      <h3 class="ms-standardheader">
        ...
        <Sharepoint:SPSecurityTrimmedControl runat="server" PermissionsString="ViewFormPages">
          <div class="ms-quicklaunchheader">
            <SharePoint:SPLinkButton id="idNavLinkViewAll" 
              runat="server" NavigateUrl="~site/_layouts/viewlsts.aspx" 
              Text="<%$Resources:wss,quiklnch_allcontent%>" 
              accesskey="<%$Resources:wss,quiklnch_allcontent_AK%>"/>
          </div>
        </SharePoint:SPSecurityTrimmedControl>
      </h3>
    </ContentTemplate>
  </SharePoint:UIVersionedContent>
</asp:ContentPlaceHolder>

Here the SPLinkButton represents the All Site Content link in the Quick Launch Bar as well, but it’s displayed for applications migrated from SharePoint 2007 with the previous version of UI. So, set the PermissionsString of the surrounding SPSecurityTrimmedControl to ManageWeb.

All other controls

The controls from the last group are not wrapped in SPSecurityTrimmedControl and don’t provide any ability to limit the access to their contents for unprivileged users. What is needed to do is put such controls into SPSecurityTrimmedControl and set the PermissionsString property of the last to ManageWeb. Within your copy of v4.master there are a few such controls:

<Sharepoint:UIVersionedContent runat="server" UIVersion="3">
  <ContentTemplate>
    <Sharepoint:SPNavigationManager id="TreeViewNavigationManager" runat="server" ...>
      <table class="ms-navSubMenu1" ...>
        <tr>
          <td>
            <table class="ms-navheader" ...>
              <tr>
                <td nowrap="nowrap" id="idSiteHierarchy">
                  <SharePoint:SPLinkButton runat="server" id="idNavLinkSiteHierarchy"
                    NavigateUrl="~site/_layouts/viewlsts.aspx"
                    Text="<%$Resources:wss,treeview_header%>" 
                    accesskey="<%$Resources:wss,quiklnch_allcontent_AK%>"/>
                </td>
              </tr>
            </table>
          </td>
        </tr>
      </table>
      ...
    </Sharepoint:SPNavigationManager>
  </ContentTemplate>
</SharePoint:UIVersionedContent>

Here the All Site Content link is presented by SPLinkButton. It’s displayed under Quick Launch, when the previous version of UI is enabled and when the TreeView navigation option is turned on in Site Settings. Wrap the link into SPSecurityTrimmedControl. It may look like the following:

<Sharepoint:UIVersionedContent runat="server" UIVersion="3">
  <ContentTemplate>
    <Sharepoint:SPNavigationManager id="TreeViewNavigationManager" runat="server" ...>
      <SharePoint:SPSecurityTrimmedControl ID="SPSecurityTrimmedControl2" 
          runat="server" PermissionsString="ManageWeb">
        <table class="ms-navSubMenu1" ...>
          <tr>
            <td>
              <table class="ms-navheader" ...>
                <tr>
                  <td nowrap="nowrap" id="idSiteHierarchy">
                    <SharePoint:SPLinkButton runat="server" id="idNavLinkSiteHierarchy"
                      NavigateUrl="~site/_layouts/viewlsts.aspx"
                      Text="<%$Resources:wss,treeview_header%>" 
                      accesskey="<%$Resources:wss,quiklnch_allcontent_AK%>"/>
                  </td>
                </tr>
              </table>
            </td>
          </tr>
        </table>
      </SharePoint:SPSecurityTrimmedControl>
      ...
    </Sharepoint:SPNavigationManager>
  </ContentTemplate>
</SharePoint:UIVersionedContent>

Another control is declared as SPLinkButton too:

<Sharepoint:UIVersionedContent runat="server" UIVersion="4">
  <ContentTemplate>
    <Sharepoint:SPNavigationManager id="TreeViewNavigationManagerV4" runat="server" ...>
      <SharePoint:SPLinkButton runat="server" id="idNavLinkSiteHierarchyV4"
          NavigateUrl="~site/_layouts/viewlsts.aspx" 
          Text="<%$Resources:wss,treeview_header%>" 
          accesskey="<%$Resources:wss,quiklnch_allcontent_AK%>" 
          CssClass="s4-qlheader" />
      ...
    </Sharepoint:SPNavigationManager>
  </ContentTemplate>
</SharePoint:UIVersionedContent>

It’s a link in the treeview under Quick Launch, when the TreeView Navigation option is enabled in Site Settings. It can be modified to the following:

<Sharepoint:UIVersionedContent runat="server" UIVersion="4">
  <ContentTemplate>
    <Sharepoint:SPNavigationManager id="TreeViewNavigationManagerV4" runat="server" ...>
      <SharePoint:SPSecurityTrimmedControl ID="SPSecurityTrimmedControl3" 
          runat="server" PermissionsString="ManageWeb">  
        <SharePoint:SPLinkButton runat="server" id="idNavLinkSiteHierarchyV4"
            NavigateUrl="~site/_layouts/viewlsts.aspx" 
            Text="<%$Resources:wss,treeview_header%>" 
            accesskey="<%$Resources:wss,quiklnch_allcontent_AK%>" 
            CssClass="s4-qlheader" />
      </SharePoint:SPSecurityTrimmedControl>
      ...
    </Sharepoint:SPNavigationManager>
  </ContentTemplate>
</SharePoint:UIVersionedContent>

Summary

Let’s sum up. If you see a control exposing PermissionsString, set the property to ManageWeb. If a control is within a SPSecurityTrimmedControl, set the PermissionsString of the last one to ManageWeb as well. And finally, if it’s just a control, add a new SPSecurityTrimmedControl, place the first one in it and set the ManageWeb permission.