in

SharePoint Blogs

The Best Place for SharePoint-related Blogs

benrobb's blog

  • HowTo: Display parts of a page to anonymous users only

    My ex-colleague Chris O'Brien (prolific blogger at http://www.sharepointnutsandbolts.com/) has a good post on some of the hidden gems in the SharePoint web control libraries (http://www.sharepointnutsandbolts.com/2008/03/great-controls-to-be-aware-of-when.html).

    In his discussion about SPSecurityTrimmedControl he mentions some issue he has when trying to set up a section of his page to show to anonymous users, but not authenticated users. He tried:

       1: <SharepointWebControls:SPSecurityTrimmedControl runat="server" AuthenticationRestrictions="AnonymousUsersOnly">
       2:     This *should* be visible only to anonymous users but it doesn't work..    
       3: </SharepointWebControls:SPSecurityTrimmedControl> 

    This wasn't working (though it looks like it should). An alternative method is to use the standard ASP.NET control LoginView:

    <asp:LoginView runat="server">
          <AnonymousTemplate>
                               This will be visible only to anonymous users and does work...
           </AnonymousTemplate>
           < LoggedInTemplate>
                                This will be visible only to authenticated users and does work...
           </LoggedInTemplate>
    </asp:LoginView>

    This works exactly as advertised.

  • Using MSBuild to manage deployments in SharePoint - Part 3

    This is the third part of a series of posts around using MSBuild for deployments of SharePoint applications, and in this post I introduce a custom MSBuild task to perform a custom Export of content from my Development environment.

    Creating a custom MSBuild task to perform a controlled export from SharePoint

    One of the key features of MCMS which was dropped in the MOSS platform was a tool to enable the export of the *code* part of a site. In MCMS, we could choose to export templates to an SDO, and import them into a destination website without affecting content. This gave us the ability to do full lifecycle management of our code, which is something that is hard (out of the box) with MOSS.So in order to replicate this kind of functionality, we could use a tool such as Chris O'Brien's excellent SP Content Deployment Wizard (http://www.codeplex.com/SPDeploymentWizard). This allows us to choose exactly which Export Objects we want to include in the export, and save that to (a cab) file. That's great for one off deployments, but I needed to have a way of scripting this so that I could automate it via TFS builds.

    To do this, I created a class library which inherited from Microsoft.Build.Utilities.Task. I could have implemented the ITask interface, but I prefer directly inheriting from Task, since this gives me access to a Log object with no additional code.

    The full code is at the end of this post, and I'm not going to step through it: it is mostly about setting up the SPExportSettings object and then executing it using SPExport.Run(). Rather than hard code these settings, you can take advantage of an inbuit XML schema in SharePoint: "deployment-exportsettings-schema", and provide an XML document with all of the settings you need. That way you can decouple your (generic) MSBuild task from the project you are working on at the current time.

    In the example XML file (below), I have the Master Page Gallery and the Style Library as my two Deployment Objects, and when my custom task runs, I get an export CMP file with just these objects (and their dependencies and children - see the "IncludeDescendants" and "ExcludeChildren" flags in the configuration).

    <?xml version="1.0" encoding="utf-8" ?>
    <ExportSettings 
         xmlns="urn:deployment-exportsettings-schema"
         
    xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance 
         xmlns:xsd
    ="http://www.w3.org/2001/XMLSchema" 
         SiteUrl
    ="http://ben:20010" FileLocation="Export" BaseFileName="Export.cmp" IncludeSecurity="All" 
         IncludeVersions
    ="CurrentVersion" ExportPublicSchema="true" ExportMethod="ExportAll" ExcludeDependencies="false" > 
    <ExportObjects>
    <!-- Master Pages -->
        <DeploymentObject Id="323d4797-b5af-4271-92cd-eac348ebf7d5" ParentId="bab096c8-7bf6-435b-b541-4f2ecd8aace7"
                                   Type
    ="List" IncludeDescendants="All" ExcludeChildren="false" />                      
    <!-- Style Library  -->                         
        <DeploymentObject Id="d6635d08-ccb0-489e-9903-6dd07b7622b4" ParentId="bab096c8-7bf6-435b-b541-4f2ecd8aace7" 
                                   Type
    ="List" IncludeDescendants="All" ExcludeChildren="false" />            
        </ExportObjects> 
    </ExportSettings>

    These parameters are documented in MSDN here.

    Importing this into a Build Server farm is simple too; I can simply call the “stsadm –o import” command on my build server – see Part 2 of this series for more information on remote execution of STSADM via WinRS.

    This gives us a very extensible model. I can store the configuration file in a project in TFS, and then run my custom task as part of an automated build without any user interaction. My *.csproj file can have the following:

      <UsingTask AssemblyFile="C:\Ben\TFS\SPCDemo\BenRobb.MSBuild\bin\Release\BenRobb.MSBuild.dll" TaskName="BenRobb.MSBuild.SPExportTask" />
      <Target Name="AfterBuild">
        <SPExportTask ConfigurationFile="ExportSettings.xml" />
        <Copy SourceFiles="@(ExportSourceFiles)" DestinationFolder="\\mossbasic\Deployments\SPExports\Demo4" />
        <Exec Command="C:\Windows\System32\winrs -r:mossbasic C:\Deployments\Commands\Demo4\Demo4_Import.bat" />
      </Target>

    The <UsingTask> node imports my custom task, and then I can refer to the class I need directly by class name. You do need to be careful about naming clashes with out of the box tasks.

    Hopefully this series of posts will have given people ideas about how to extend MSBuild to take care of common SharePoint development tasks, and give credence to the idea that Continuous Integration and streamlined deployment processes are possible when buidling applications for SharePoint.


    Sample code:

    using System;
    using System.Xml;
    using System.Xml.Linq;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.Build.Utilities;
    using Microsoft.Build.Framework;
    using Microsoft.SharePoint.Deployment; 
    namespace BenRobb.MSBuild
    {
        public class SPExportTask : Task
        {
            private XmlDocument _settingsFile = new XmlDocument();
            private string _configurationFile = string.Empty;
            /// <summary>
            /// The ConfigurationFile is the (project relative) path to the XML file
            /// which sets up the boundaries of the SPExport. It is required for this
            /// task to run.
            /// </summary>
            [Required]
            public string ConfigurationFile
            {
                get { return _configurationFile; }
                set { _configurationFile = value; }
            }

     
            /// <summary>
            /// Execute() provides the entry point to the MSBuild task. This method calls
            /// getExportSettings to configure the SPExport, and then runs the
            /// export.
            /// </summary>
            /// <returns></returns>
            public override bool Execute()
            {
                Log.LogMessage("Exporting from SharePoint site, using configuration file {0}", _configurationFile);
                _settingsFile.Load(_configurationFile);
                XmlElement xSettings = _settingsFile.DocumentElement;
                if (xSettings != null)
                {
                    SPExportSettings exportSettings = getExportSettings(xSettings);
                    using (SPExport export = new SPExport(exportSettings))
                    {
                        export.Run();
                    }
                }
                return true;
            }
             /// <summary>
            /// getExportSettings goes through the supplied XML fragment and uses
            /// this to manage the parameters which control the export from SharePoint.
            ///
            /// It also calls addExportObjects to build up the object list which will
            /// be exported.
     
          /// </summary>
            /// <param name="xSettings">The XML element for setting parameters for SPExport</param>
            /// <returns>A populated SPExportSettings object</returns>
            private SPExportSettings getExportSettings(XmlElement xSettings)
            {
                SPExportSettings exportSettings = new SPExportSettings();
                exportSettings.SiteUrl = xSettings.Attributes["SiteUrl"].Value;
                exportSettings.FileLocation = 
                xSettings.Attributes["FileLocation"].Value;
                exportSettings.BaseFileName = xSettings.Attributes["BaseFileName"].Value; 
               
    exportSettings.FileCompression =
    true;
                exportSettings.ExportMethod = (SPExportMethodType)Enum.Parse(typeof(SPExportMethodType), xSettings.Attributes["ExportMethod"].Value, true);
                exportSettings.ExcludeDependencies = Convert.ToBoolean(xSettings.Attributes["ExcludeDependencies"].Value);
                exportSettings.IncludeSecurity = (SPIncludeSecurity)Enum.Parse(typeof(SPIncludeSecurity), xSettings.Attributes["IncludeSecurity"].Value, true);
                exportSettings.IncludeVersions = (SPIncludeVersions)Enum.Parse(typeof(SPIncludeVersions), xSettings.Attributes["IncludeVersions"].Value, true);
                exportSettings.TestRun = false;
     
                SPExportObjectCollection eoc = exportSettings.ExportObjects;
                addExportObjects(eoc);
                exportSettings.Validate();
                exportSettings.OverwriteExistingDataFile = true;
                exportSettings.CommandLineVerbose = true;
                return exportSettings;
            }

            /// <summary> 
           
    /// This method iterates through the ExportObject elements within the configuration XML
            /// file and adds them to the supplied SPExportObjectCollection.
            /// </summary>
            /// <param name="eoc">The SPExportObjectCollection to add the SPExportObject
            /// objects which are defined in the configuration XML file to.</param>
            private void addExportObjects(SPExportObjectCollection eoc)
            {
                XmlNode xSettings = _settingsFile.DocumentElement.GetElementsByTagName("ExportObjects")[0];
                foreach (XmlNode xExportObject in xSettings.SelectNodes("*"))
                {
                    Guid guidID = new Guid(xExportObject.Attributes["Id"].Value);
                    SPDeploymentObjectType deploymentObjectType = (SPDeploymentObjectType)Enum.Parse(typeof(SPDeploymentObjectType), xExportObject.Attributes["Type"].Value);
                    Guid parentID = new Guid(xExportObject.Attributes["ParentId"].Value);
                    bool b_excludeChildren = Convert.ToBoolean(xExportObject.Attributes["ExcludeChildren"].Value);
                    SPExportObject exportObject = new SPExportObject();
                    exportObject.Id = guidID;
                    exportObject.ParentId = parentID;
                    exportObject.Type = deploymentObjectType;
                    exportObject.ExcludeChildren = b_excludeChildren;
                    exportObject.IncludeDescendants = (SPIncludeDescendants)Enum.Parse(typeof(SPIncludeDescendants), xExportObject.Attributes["IncludeDescendants"].Value);
                    eoc.Add(exportObject);
                }
            }
        }
    }
  • Using MSBuild to manage deployments in SharePoint - Part 2

     This series of blog posts is taken from a session I delivered at the SharePoint Conference 2008, in March. See here to read part 1.

    Part 2 - Using MSBuild to perform simple deployment tasks

    SharePoint solutions quickly start to have lots of different types of artifact which require transfer to your destination farm. These include:

    • Master pages and page layouts
    • CSS, XSLT and image files in the Style Library
    • Server controls in the form of DLLs
    • User Controls in the CONTROLTEMPLATE directory
    • SharePoint Solutions and Features
    • Non-SharePoint filesystem assets (such as Control Adapters)


    If you had to package and deploy these manually, you would be faced with something like this:

    • Create a copy of the production environment (content, code and configuration)
    • Get latest code from TFS
    • Compile assemblies and wrap into solutions
    • Wrap up SharePoint assets into solutions and features
    • Copy the files to the correct location on the test server
    • Un-deploy pre-existing solutions
    • Add solutions to SharePoint
    • Deploy solutions to the web application
    • Perform any other manual configuration required (feature activation etc)


    And this would have to be done for each deployment, and on each environment. It is easy to see that this will cause problems - human error will become a significant factor, you won't be able to do automated testing easily, and you won't have a repeatable process that you can hand over to IT to implement on production environments.

    This is where build automation comes into its own. To provide a repeatable, testable deployment, you really have to wrap this into some kind of build script. Some people are happy to just use batch files, making use of stsadm commands, robocopy (now that xcopy is depreciated) and powershell commands. This is fine, but to make sure you are successfully tracking errors, you either have to manually run those batch files and be there when they complete, or you start using some kind of automation tool.

    I use TFS for source control, and so I tend to use MSBuild and TFSBuild to perform my automated builds, but other tools are out there, such as NAnt, CruiseControl and FinalBuilder.

    So let's go through a couple of simple, out of the box tasks in MSBuild.

    1. Where to place your custom tasks

    If you open a standard csproj file in Notepad, you will see the following block:

      <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
           Other similar extension points exist, see Microsoft.Common.targets.
      <Target Name="BeforeBuild">
      </Target>
      <Target Name="AfterBuild">
      </Target>
      -->

    These pretty much do what they do on the tin. You can also add additional targets, or override out of the box ones (such as the default "Build" target). We will be adding our bits into the AfterBuild target.

    2. How to copy files

    MSBuild comes with many out of the box tasks, including "Copy". To use this, you can add the task to an action:

    <Target Name="AfterBuild">
        <Copy SourceFiles="App_Browsers\CSSFriendlyAdapters.browser" DestinationFolder="\\[path to destination web application]\App_Browsers"/>
    </Target>

    As you would expect, you wouldn't want to have to add all of the files in your application individually - you can use item groups. For example, files you have added to your project via Visual Studio will appear in your project file in a <ItemGroup> node:

      <ItemGroup>
        <Compile Include="App_Code\Adapters\ChangePasswordAdapter.cs" />
        <Compile Include="App_Code\Adapters\CompositeDataBoundControlAdapter.cs" />
        <Compile Include="App_Code\Adapters\CreateUserWizardAdapter.cs" />
        <Compile Include="App_Code\Adapters\DataListAdapter.cs" />
        <Compile Include="App_Code\Adapters\DetailsViewAdapter.cs" />
        <Compile Include="App_Code\Adapters\FormViewAdapter.cs" />
        <Compile Include="App_Code\Adapters\GridViewAdapter.cs" />
        <Compile Include="App_Code\Adapters\LoginAdapter.cs" />
        <Compile Include="App_Code\Adapters\LoginStatusAdapter.cs" />
        <Compile Include="App_Code\Adapters\MenuAdapter.cs" />
        <Compile Include="App_Code\Adapters\PasswordRecoveryAdapter.cs" />
        <Compile Include="App_Code\Adapters\TreeViewAdapter.cs" />
        <Compile Include="App_Code\Adapters\WebControlAdapterExtender.cs" />
      </ItemGroup>

    Your copy task can easily take all of these files and copy them over to another machine:

    <Copy SourceFiles="@(Compile)" DestinationFolder="\\[path to destination web application]\App_Code\Adapters" />

     
    Finally, if you want to push some output into your build log, you can add the Output task:

    <Copy SourceFiles="App_Browsers\CSSFriendlyAdapters.browser" DestinationFolder="\\[path to destination web application]\App_Browsers">
        <Output TaskParameter="CopiedFiles" ItemName="SuccessfullyCopiedFiles" />
    </Copy>

    3. How to run commands on the local machine

    OK, so copying files is all well and good, but sometimes we need to run commands. For example, stsadm commands are often useful... There is an out of the box task we can use here:

    <Exec Command="stsadm -o installfeature -name MyFeature"/>

    This will run the command, and return, in the log for the msbuild job, the outcome of the stsadm command - hopefully "Operation completed successfully".

    4. How to run commands on a remote machine

    That is great, but we need to also run remote operations. There are a number of ways of doing this, including using PsExec (a SysInternals tool), but I have found this to be flaky - particularly when run through TFS Build. The main issue is that the control goes to the remote operation, but never returns to the MSBuild process, which just sits there until you kill it.

    I've found that Windows Remote Shell (part of Windows Remote Management) in Windows Server 2008 to be much more stable, and also significantly more efficient when running objects on remote servers. There is some configuration you need to do on both the source and target machines, and things are generally happier if you are running in a Kerberos environment, but it is all fairly straighforward. See http://msdn2.microsoft.com/en-us/library/aa384372.aspx for more information on how to configure this.

    Once you have this configured, it is trivial to run applications on remote servers from MSBuild:

    <Exec Command="C:\Windows\System32\winrs -r:SERVERNAME CMD [ARGS]" />

    If you are not using Kerberos, you will need to supply a username and password:

     <Exec Command="C:\Windows\System32\winrs -r:SERVERNAME -u:USERNAME -p:PASSWORD CMD [ARGS]" />

    To run stsadm, for example, you could run the following task:

    <Exec Command="C:\Windows\System32\winrs -r:buildserver stsadm -o installfeature -name MyFeature" />

    If you have many commands you want to wrap up (for example you want to undeploy a solution, remove it, add it and redeploy it), you can easily wrap these into a batch file, which you store within your solution. You can then copy the file to the build server and run it remotely:

    <Target Name="AfterBuild">
         <Copy SourceFiles="Demo2_CallStsAdm.bat" DestinationFolder="\\BUILDSERVER\c$\Deployments\Commands\Demo2">
          <Output TaskParameter="CopiedFiles" ItemName="SuccessfullyCopiedFiles" />
        </Copy>
        <Exec Command="C:\Windows\System32\winrs -r:SERVERNAME C:\Deployments\Commands\Demo2\Demo2_CallStsAdm.bat" />
    </Target>

    Hopefully this post will give you a grounding in using MSBuild to perform deployment tasks. The next installment of this blog series will focus on creating custom tasks to automate building SharePoint solutions.

  • Using MSBuild to manage deployments in SharePoint - Part 1

    This series of blog posts is taken from a session I delivered at the SharePoint Conference 2008, in March.

    Part 1: Why would you want to have build automation in SharePoint Solutions?

    For simple solutions, where you have little custom code, and a single, linear set of projects, you can get away with not formalising your deployment process. However, if you have a large intranet or public facing site, you will probably be faced with a solution which has:

    • Frequent changes to functionality.
    • Multiple teams working on different streams
    • Cross-project dependencies
    • Different release schedules
    • Tight control over the production environment.

    A typical site will probably start off being simple: a foundation project will determine the network topology, it will do the initial build and configuration of the servers, and it will have the initial development of the core components of the site - a master page, some custom controls and some page layouts and CSS. Typically this will have a single team of developers working towards a "big bang" launch. All being well, they will launch the site and have a few beers to celebrate. If they are contractors, or working for an agency, they may well go off to other jobs.

    But then what? 

    On a large scale website, that's never the end of the story. Large websites will never stay static: either in their content or their functionality. There will be ongoing maintainence and bug fixing. There will be change requests to amend the existing functionality. There will also be brand new requirements to provide the site with completely new features.

    Programme managers would, in an ideal world, see that these projects are carefully managed into phases, and ensure that each code drop goes through the same process as the first phase. Developers would work together to build their bits of functionality, and then at the end, have integration and testing tasks to form a release schedule. But back in the real world, how does this actually pan out? When bugs are found post launch, your team may well have SLAs to meet. Regardless, you need to fix the problem as quickly as possible. That means that you need to deploy your fixes to your site while your other projects are still running, and often while those projects have breaking changes to your site. This means that you need to start branching your code base – potentially in your configuration management, your file system / .NET assets and your SharePoint artefacts.

    Branching

    Branching is often feared by developers, but there are some key lessons which should be learnt when determining your branching strategy. This primer on branching and merging, from MSDN explains how different strategies can affect your team, but the bottom line is that you need to think carefully about your branching strategy. If your merges are taking too long (i.e. you have developers sitting round doing nothing for a week), then you are not being granular enough. You need to branch only when you really need to, and merge as soon as possible back into the main build. Leaving your merge to the end of a project is asking for trouble.

    Automate your builds 

    There are a number of key reasons why build automation should save you time in the long run.

    1. Leaving deployment testing to the end of a project increases the risk of it going wrong.
    2. Having regular builds means you test your deployment every night, rather than only a couple of times through the project. This means that deployment issues are flagged early in the dev cycle.
    3. You can easily ensure that your dev teams are always working with the most up to date version of the build.
    4. You can implement Test Driven Development techniques to aid integration testing and regression testing.

    These advantages may not pan out so much with the initial build of a site, but as you move on to maintain and enhance that site you really need to make sure you start taking advantage of build automation. 

    Build masters

    How is this going to affect your teams? What you need to do is create a new position within your team - a build master - to manage the deployment and builds. That person needs to know where all the branches are, what the deployment schedule is and every detail about the solution. This is a key, pivotal role in your project team, and should be senior. This is probably not something that can be fitted in around other work, and could well be a full-time job. Really big projects which themselves contain branches may require additional build masters.

    Now that the scene is set, in the next few posts I will walk through some examples of how to use MSBuild tasks, together with TFS, to automate your builds. 

  • Announcing AC's VS CodeRush/Refactor Tools for SharePoint Devs

    Andrew Connell has just announced some new tools for SharePoint developers to get their teeth into - these are top tools for people who need to build their own feature files from scratch:

    "I finally got sick of writing feature.xml after feature.xml and creating new GUIDs. These repetitive things as well as those that make me take my hands off the keyboard slow me down... and that drives me nuts. Over the last few months I've been polishing and tweaking some stuff I've been working on that I use on a day-to-day basis as a SharePoint developer that speed up my development. Finally, they are at a point where I want to share them for others to benefit from them as I have (and a few folks who've been kind enough to test and provide feedback)."

    Full report here: http://andrewconnell.com/blog/archive/2007/08/21/6095.aspx

    I've been using this in anger for the last week or so, and I love them...

  • MVP

    Got word on Sunday that I received the MVP award for MOSS. Thanks to those that nominated me...I appreciate it!
     
    And congratulations to Bob Fox, who has had his renewed, and Andrew Woodward (fellow UK SP User Group member) who has also received his.
     
    [I suppose this means I should spend more time on my blog now...]
  • How do you tell whether your code is executing in Edit mode?

    Answer:

    SPControlMode currentControlMode = SPContext.Current.FormContext.FormMode;

    SPControlMode can be in one of 4 states:

    • Display
    • Edit
    • Invalid
    • New

    Very useful if you are having to write code which has to be aware whether or not it is executing in edit mode on a page.

    *Two* blogs on the same day. What is the world coming to?

  • Cross list queries: SPSiteDataQuery vs. walking the OM tree

    Recently I had a requirement to do some cross list queries. The scenario was this:

    I had a list at the root of my site collection, and a content type (inheriting from Article Page) which had a lookup field using this list. I had a design requirement to get all the pages in the site collection of that content type, and some render some of the fields (including some of the list fields) to a rollup page..

    None of the out of the box controls could do this (specifically, getting values from two lists at the same time)*, so I set about writing my own control.

    * Please correct me if I'm wrong...

    I started off by iterating through the SPWeb hierarchy, getting all the pages which had the correct content type, generating XML on the fly. I then got the SPListItem which had been specified for that page and added the list values to the XML document, thinking that I could easily use XSLT to render out the results. Walking the tree went something like this:

            private void iterateWebs(SPWeb thisWeb)
            {
                if (PublishingWeb.IsPublishingWeb(thisWeb))
                {
                    PublishingWeb pWeb = PublishingWeb.GetPublishingWeb(thisWeb);
                    foreach (SPListItem pageItem in pWeb.PagesList.Items)
                    {
                        if (pageItem.ContentType = f_MyContentType)
                        {
                            addPageToXml(pageItem);
                        }
                    }
                }
                foreach (SPWeb childWeb in thisWeb.Webs)
                {
                    iterateWebs(childWeb);
                }
            }

    This worked perfectly on my dev box, but moving to a location where I had a significant number of webs, I noticed that the time taken to render was becoming slow - in the order of seconds to execute. 

    So I added some timestamp checks to work out what was taking the time. There were a number of factors, for example, the step to get the Publishing Web was fairly slow, but overall there wasn't one particular operation that took a long time, it was just the culmulative effect of iterating through the object model.

    I toyed with the idea of using some sort of caching - this wasn't something that changed frequently. But I didn't really want to get into complexities around refreshing the cache when new pages appeared. That led me to pl