Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / productivity / SharePoint

SharePoint 2010 Site Actions Menu

4.90/5 (7 votes)
14 Nov 2010CPOL8 min read 103.1K   940  
Creating menu items with sub menus for the SharePoint 2010 Site Actions menu
Image1.PNG

Introduction

SharePoint has been around for a number of years and has been gaining in popularity, especially with the release of SharePoint 2010. One common task when customizing a SharePoint application is to add menu items to the Site Actions menu to provide authorized users easy access to features and functionality. In this article, I will show three ways to add your own items, with submenus, or flyouts, to the Site Actions menu.

Site Action Menu

Just about anyone who has worked with SharePoint is familiar with the Site Actions menu. It provides access to functionality, such as, enabling page editing, or with SharePoint 2010, displaying the new RibbonBar. SharePoint 2010 has also given developers more control over this menu and the items in it. The default.master and v4.master masterpages include the below markup to render the basic Site Actions menu on each page where appropriate.

XML
<sharepoint:siteactions id="SiteActions1" runat="server" 
    accesskey="<%$Resources:wss,tb_SiteActions_AK%>" 
    menunotvisiblehtml=" " prefixhtml="" suffixhtml="">
    <customtemplate>
      <SharePoint:FeatureMenuTemplate runat="server" featurescope="Site" 
      groupid="SiteActions" location="Microsoft.SharePoint.StandardMenu" 
      useshortid="true">
	<SharePoint:MenuItemTemplate id="MenuItem_EditPage" runat="server" 
    clientonclicknavigateurl="javascript:ChangeLayoutMode(false);" 
    description="<%$Resources:wss,siteactions_editpagedescriptionv4%>" 
    imageurl="/_layouts/images/ActionsEditPage.png" menugroupid="100" 
    sequence="110" text="<%$Resources:wss,siteactions_editpage%>" />

    ... more items ... 
    </SharePoint:FeatureMenuTemplate>
</CustomTemplate>
</Sharepoint:iteactions>    

One way to add additional items to the Site Action menu is, of course, to modify the master page and include another MenutItemTemplate element.

All of the properties for MenuItemTemplate can be found at http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.webcontrols.menuitemtemplate_properties.aspx [^] and most are self-explanatory and common to any WebControl. A few properties however need some explanation.

PermissionString: This property supplies a comma separated list of items from the SPBasePermission enumeration to specify what level of user permissions are required to access the menu item.

PermissionMode: This determines how to apply the permissions specified in PermissionString, either "Any" or "All".

ClientOnClickScript: This controls the client-side action that is taken when the use clicks the menu item. As can be seen in the code setting, this property will delete any settings for the ClientOnClickNavigateUrl and ClientOnClickScriptContainingPrefixedUrl properties.

C#
[DefaultValue(""), Category("Behavior")]
public string ClientOnClickScript
{
    get
    {
        return this.m_clientOnClickScript;
    }
    set
    {
        if (value == null)
        {
            value = string.Empty;
        }
        this.m_clientOnClickScript = value;
        this.ViewState["ClientOnClickScript"] = value;
        this.m_clientOnClickNavigateUrl = null;
        this.ViewState["ClientOnClickNavigateUrl"] = null;
        this.m_clientOnClickScriptContainingPrefixedUrl = null;
        this.ViewState["ClientOnClickScriptContainingPrefixedUrl"] = null;
        this.clientOnClickUsingPostBackEvent = null;
    }
}

ClientOnClickNavigateUrl: With this property, you can set either a URL to navigate to or a JavaScript function to execute when the menu item is clicked. As you can see, it sets the ClientOnClickScript property so it will take precedence over anything set in that property so the order in which the attributes are applied to the element will affect what happens when a user clicks on the menu item.

C#
[Category("Behavior"), DefaultValue("")]
public string ClientOnClickNavigateUrl
{
    get
    {
        return this.m_clientOnClickNavigateUrl;
    }
    set
    {
        if (value == null)
        {
            value = string.Empty;
        }
        value = SPUtility.GetServerRelativeUrlFromPrefixedUrl(value);
        value = value.Replace("'", @"\'");
        if ((value.StartsWith("javascript:") || value.StartsWith("%")) 
		|| value.StartsWith("?"))
        {
            this.ClientOnClickScript = "window.location = '" + value + "';";
        }
        else
        {
            this.ClientOnClickScript = "STSNavigate2(event,'" + value + "');";
        }
        this.m_clientOnClickNavigateUrl = value;
        this.ViewState["ClientOnClickNavigateUrl"] = value;
    }
}

ClientOnClickScriptContainingPrefixedUrl: Similar to ClientOnClickNavigateUrl this property allows you to set either a URL or a JavaScript function to execute. The property setter calls SPUtility.ReplaceEmbeddedPrefixedUrlsWithServerRelativeUrls to replace any builtin prefixes, such as, ~site or ~sitecollection, found in the string with the server relative URLs. Once again ClientOnClickScript is set and the order the attribute is applied in the element is important.

C#
[DefaultValue(""), Category("Behavior")]
public string ClientOnClickScriptContainingPrefixedUrl
{
    get
    {
        return this.m_clientOnClickScriptContainingPrefixedUrl;
    }
    set
    {
        if (value == null)
        {
            value = string.Empty;
        }
        value = SPUtility.ReplaceEmbeddedPrefixedUrlsWithServerRelativeUrls(value, true);
        this.ClientOnClickScript = value;
        this.m_clientOnClickScriptContainingPrefixedUrl = value;
        this.ViewState["ClientOnClickScriptContainingPrefixedUrl"] = value;
    }
}

VisibilityFeatureId: Use this property to specify a feature id (guid) the menu item is dependant on. If the feature has not been activated for the web, the menu item will not be rendered.

Adding a SubMenu

To add a submenu, you must use a SubMenuTemplate control rather than a MenuItemTemplate. It should be noted here the SubMenuTemplate can't be used to provide any actions, the ClientOnClickXXX properties of the MenuItemTemplate are not available, it is simply a container for other menu items.

XML
<submenutemplate 
id="MenuItem_Custom" 
description="Custom menu with submenu" 
runat="server" 
text="Custom Menu" 
sequence="100" 
menugroupid="90" 
imageurl="/_layouts/images/settingsIcon.png" />
  <menuitemtemplate 
     id="MenuItem_CustomSub" 
     description="A submenu item" 
     runat="server" 
     text="SubMenu" 
     sequence="110" 
     menugroupid="100" 
     imageurl="/_layouts/images/settingsIcon.png" 
     clientonclickscript="alert('Submenu item clicked')" />
</submenutemplate />

The SubMenuTemplate could, of course, contain other SubMenuTemplates to produce a multilevel menu structure.

Image2.PNG

Although this method may be appropriate for one-off circumstances, it is not a reproducible or a recommended practice. For a reusable solution, you should create and deploy a SharePoint feature.

Create Menus with Code

With the integration of Visual Studio 2010 and SharePoint 2010, creating features are now much easier than with previous versions of either tool. To begin creating a feature to modify the Site Action menu, create a SharePoint 2010 Module project in Visual Studio 2010, making sure to chose a Farm solution rather than Sandbox. After the project has been created, you will see the below structure. The Sample.txt file can be deleted since it isn't used.

Image3.PNG

The Elements.xml file should now look like this:

XML
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Module Name="Module1">
  </Module>
</Elements>

Remove the Module element and replace it with a CustomAction element as below:

XML
<elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <customaction 
   id="MANSoftDev_SiteAction" 
   sequence="1" 
   controlclass="MANSoftDev.SiteAction.SiteActionCode" 
   controlassembly="MANSoftDev.SiteAction, Version=1.0.0.0, 
	Culture=neutral, PublicKeyToken=2e15c7b1150d656d" 
   rights="ManageWeb" 
   location="Microsoft.SharePoint.StandardMenu" 
   groupid="SiteActions">
  </customaction>
</elements>

The GroupId and Location attributes tell SharePoint where the CustomAction will be placed. In this case, the location is StandardMenu group is SiteActions to place it in the Site Actions menu. A full list of Locations and Groups can be found at http://msdn.microsoft.com/en-us/library/bb802730.aspx[^]. The Rights attribute is a comma separated list of SPBasePermissions enumeration values. However, unlike the previous example, the user must have all the specified rights, not any. The ControlAssembly and ControlClass attributes are new to SharePoint 2010 and allow you to specify the class that will control this CustomAction. This is essential to the implementation. ControlAssembly is the four-part name of the assembly in which the class is implemented. ControlClass is the fully qualified name of the class. Note it must include the full namespace, just giving the class name is not enough. To produce this class, you add Class Library project to the solution and sign the assembly since it will be deployed to the GAC. After creating the project, add references to System.Web and Microsoft.SharePoint then, either rename the existing Class1 file, or deleted it and add another class that derives from WebControl to end up with this.

C#
namespace MANSoftDev.SiteAction
{
    public class SiteActionCode : WebControl
    {
    }
}

Before proceeding, it's a good idea to add the Class Library assembly to the deployment package and have it deployed to the GAC with appropriate settings added the SafeControls section in the web.config file. To do this, you need to modify the Package in the Visual Studio 2010 project. Open the Package designer and click the Advanced tab at the bottom.

Image4.PNG

From here, click the Add button and select "Add Assembly from Project Out..." to display the dialog. Select the Class Library project from the dropdown, notice the Location textbox is filled with the assembly name. Next, click the "Click here to add a new item." button in the Safe Controls grid and fill in the appropriate settings. Click the OK button to return to the previous screen and notice the information has been added to the Additional Assemblies grid. Once again, ensure the SharePoint project is not a Sandboxed solution or the assmbly will not be deployed to the GAC regardless of these settings.

Image5.PNG

Now that the assembly has been added to the deployment package, we can get on with the coding. To add the necessary MenuItemTemplate and SubMenuTemplates to recreate the previous example, you will need to override the CreateChildControls method and add the code shown below:

C#
protected override void CreateChildControls()
{
    SubMenuTemplate sub = new SubMenuTemplate();
    sub.Text = "Custom Menu";
    sub.Description = "Custom menu with submenu";
    sub.ImageUrl = "/_layouts/images/settingsIcon.png";
    sub.MenuGroupId = 90;
    sub.Sequence = 100;

    Controls.Add(sub);

    MenuItemTemplate item = new MenuItemTemplate("SubMenu");
    item.Description = "A submenu item";
    item.ImageUrl = "/_layouts/images/settingsIcon.png";
            
    sub.Controls.Add(item);

    SubMenuTemplate sub2 = new SubMenuTemplate();
    sub2.Text = "Custom Menu";
    sub2.Description = "Custom menu with submenu";
    sub2.ImageUrl = "/_layouts/images/settingsIcon.png";

    sub.Controls.Add(sub2);

    item = new MenuItemTemplate("SubMenu");
    item.Description = "A submenu item";
    item.ImageUrl = "/_layouts/images/settingsIcon.png";

    sub2.Controls.Add(item);
}

There isn't anything surprising or difficult here, it's just a matter of creating the controls and adding them to proper Controls collection in the correct order. The properties that were mentioned in the first section can still be applied here, for simplicity though no OnClientClickXXX properties have been set.

Create Menu from Elements.xml

Although creating the menu from code is relatively simple and easy, some may prefer to use a more declarative approach and define the menu structure within an Elements.xml file. However, this still requires code to add the menu items to the Site Actions menu. Start by adding another class to the Class Library project that is once again derived from WebControl and implements the CreateChildControls method. Then add another module to the SharePoint project. Remove the Sample.txt file as before and copy the contents of the previous Elements.xml file into the one just created and change the ControlClass attribute to match the newly created class.

XML
<elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <customaction 
   id="MANSoftDev_SiteAction" 
   sequence="1" 
   controlclass="MANSoftDev.SiteAction.SiteActionDeclarative" 
   controlassembly="MANSoftDev.SiteAction, Version=1.0.0.0, 
	Culture=neutral, PublicKeyToken=2e15c7b1150d656d" 
   location="Microsoft.SharePoint.StandardMenu" 
   groupid="SiteActions">
  </customaction>
</elements>

Now that you have a starting point, you can start to add the menu items as CustomAction elements in the Elements.xml file. The important attributes are the Location and GroupId. These will be used in the code to create the FeatureMenuTemplate corresponding to the menu group.

XML
<elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <customaction 
   id="MANSoftDev_SiteAction" 
   sequence="1" 
   controlclass="MANSoftDev.SiteAction.SiteActionDeclarative" 
   controlassembly="MANSoftDev.SiteAction, Version=1.0.0.0, 
	Culture=neutral, PublicKeyToken=2e15c7b1150d656d" 
   location="Microsoft.SharePoint.StandardMenu" 
   groupid="SiteActions">
  </customaction>

  <customaction 
   id="subMenu" 
   title="SubMenu" 
   description="A submenu item" sequence="100" 
   imageurl="/_layouts/images/settingsIcon.png" 
   location="MANSoftDev.CustomMenu" 
   groupid="MANSoftDev_Sub1">
  </customaction>

  <customaction 
   id="subMenu" 
   title="SubMenu" 
   description="A submenu item" 
   sequence="100" 
   imageurl="/_layouts/images/settingsIcon.png"
   location="MANSoftDev.CustomMenu" 
   groupid="MANSoftDev_Sub2">
  </customaction>
  
</elements>

In the ControlClass you once again override the CreateChildControls method to create SubMenuTemplates and FeatureMenuTemplates that will correspond to the CustomAction elements shown above.

C#
protected override void CreateChildControls()
{
    // Initialize collections
    SubMenus = new List<submenutemplate />();
    FeatureMenus = new Dictionary<string, />();

    // Create the SUbMenuTemplates
    SubMenuTemplate submenu = CreateSubMenu();
    Controls.Add(submenu);

    submenu = CreateSubMenu();
    Controls.Add(submenu);

    // Create the FeatureMenuTemplates
    FeatureMenuTemplate featureMenu = CreateFeatureMenu("MANSoftDev_Sub1");
    Controls.Add(featureMenu);

    featureMenu = featureMenu = CreateFeatureMenu("MANSoftDev_Sub2");
    Controls.Add(featureMenu);            
}

private FeatureMenuTemplate CreateFeatureMenu(string groupId)
{
    FeatureMenuTemplate featureMenu = new FeatureMenuTemplate();
    featureMenu.Location = "MANSoftDev.CustomMenu";
    featureMenu.GroupId = groupId;

    FeatureMenus.Add(groupId, featureMenu);

    return featureMenu;
}

The CreateSubMenu method is straightforward and similar to what was done in the code based method used earlier. The CreateFeatureMenu method however is where the CustomAction elements are linked to the FeatureMenuTemplate via the GroupId property. If you debugged the code at this point, you would see that the FeatureMenuTemplates contain no children; however, in the OnPreRender method, they do. The magic happens in the CreateChildControls method of the FeatureMenuTemplate control. Here, SharePoint will use the internal class SPElementProvider to extract the CustomAction elements and match them to the GroupId and create MenuItemTemplates that are added to the Controls collection.

C#
...
SPElementProvider availableProvider = SPElementProvider.GetAvailableProvider();
...

if (element2.Location != "CommandUI.Ribbon.Custom")
{
    MenuItemTemplate menuItem = new MenuItemTemplate();
    menuItem.Description = element2.Description;
    menuItem.ClientOnClickNavigateUrl = 
SPCustomActionElement.ServerRelativeUrlFromTokenizedUrl
			(element2.UrlAction, web, list, null);
    menuItem.Text = element2.Title;
    menuItem.Sequence = element2.Sequence;
    menuItem.MenuGroupId = element2.MenuGroupId;
    menuItem.ImageUrl = element2.ServerRelativeImageUrl;
    this.OnAddMenuItem(new AddMenuItemEventArgs(menuItem));
    if (menuItem.Visible)
    {
        this.Controls.Add(menuItem);
    }

    ...

During the OnPreRender event, you then iterate through the FeatureMenuTemplates and move the MenuItemTemplates to the appropriate SubMenuTemplate and add the second level SubMenuTemplate to the parent level.

C#
protected override void OnPreRender(EventArgs e)
{
    // Add the first level menu items
    while(FeatureMenus["MANSoftDev_Sub1"].Controls.Count != 0)
    {
        MenuItemTemplate menu = FeatureMenus
	["MANSoftDev_Sub1"].Controls[0] as MenuItemTemplate;
        SubMenus[0].Controls.Add(menu); 
    }

    // Add the second level menu items
    while(FeatureMenus["MANSoftDev_Sub2"].Controls.Count != 0)
    {
        MenuItemTemplate menu = FeatureMenus
	["MANSoftDev_Sub2"].Controls[0] as MenuItemTemplate;
        SubMenus[1].Controls.Add(menu);
    }

    // Add the second level submenu to the top
    SubMenus[0].Controls.Add(SubMenus[1]);

    base.OnPreRender(e);
}

As you can see, this method is a little more complicated than the previous methods and requires coordination between the Elements.xml and the code. However, it could allow one team member to define the menus while another codes the behavior or to create a method that could be reused in many projects.

Conclusion

I have presented three different methods to construct and deploy SiteAction menu items, with sub menus, for SharePoint 2010. Neither method can be said to be "better" than another and which one to use will be determined by your requirements. This article was meant to present the different techniques and give you the knowledge to choose the best approach for your project.

History

  • Initial posting: 11/14/2010

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)