Welcome to MSDN Blogs Sign in | Join | Help

Enable the Developer Dashboard using the Object Model / Powershell

The SharePoint 2010 developer dashboard has three display levels (see SPDeveloperDashboardLevel Enumeration.):

  • Off (default): The dashboard is not displayed, and there is no UI element to turn it on
  • On: The dashboard is displayed, and there is no UI element to turn it off:

1

  • OnDemand: A UI element enables you to turn it on or off

2

There are a few ways to set the developer dashboard level.  One option is to use stsadm –o setproperty –pn developer-dashboard –pv “On”, but in this article, I’ll show you how to control the dashboard through the object model using PowerShell. 

To configure the dashboard, use the SPWebService.ContentService object to set properties on the SPDeveloperDashboardSettings class.  Here is the PowerShell script to set the level:

param($level = $(throw "level is required.  Level is On, Off or OnDemand"))

# import assemblies
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
[
void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Administrration")

# set the level
$contentService = [Microsoft.SharePoint.Administration.SPWebService]::ContentService
$contentService.DeveloperDashboardSettings.DisplayLevel = ([Enum]::Parse([Microsoft.SharePoint.Administration.SPDeveloperDashboardLevel], $level))
$contentService.DeveloperDashboardSettings.Update()

Write-Host ("Developer Dashboard Level: " + $contentService.DeveloperDashboardSettings.DisplayLevel)

Here is a complimentary script to view the current settings:

# import assemblies
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Administrration")

# get the level
$contentService = [Microsoft.SharePoint.Administration.SPWebService]::ContentService
Write-Host $contentService.DeveloperDashboardSettings.DisplayLevel
Posted by johnwpowell | 0 Comments

Visual Studio Extensions for Windows SharePoint Services (VSEWSS) Error: Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information

One error you may see when packaging solutions with VSEWSS is:  Microsoft.SharePoint.Tools.Utilities.VSeWSSServiceException VSeWSS Service Error: Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.  Like most error messages from VSEWSS, there is not enough information provided to resolve the issue, so I hope to save you some time by providing a workaround that will permanently fix this problem. 

When VSEWSS packages your solution, it uses reflection to enumerate the packaged assemblies.  It works fine until it encounters an assembly that implements an interface that is defined in another assembly as is often the case in the Microsoft.Practices.* assemblies.  I am unsure of exactly why, but the extensions are unable to resolve the dependent assemblies.  To work around the issue, you can copy the dependent assemblies to the GAC, but I don’t think this is the optimal solution and doing so will cause pain during development and debugging.  Another option is to copy the dependent assemblies to the VSEWSS service bin directory post build.  After trying this for awhile, and maintaining the script, I decided it would be much simpler to just copy all assemblies in my solution to the VSEWSS service bin directory on post build.  Here is the script:

@rem======================================================================
@rem
@rem    This script copies assemblies to the VSEWSS bin directory
@rem    to eliminate the packaging type load exception that occurs
@rem    when an interface is defined in a separate assembly.
@rem    
@rem    usage call $(ProjectDir)Scripts/CopyAssembliesToVSEWSSBin.bat $(TargetDir)
@rem
@rem======================================================================

@echo off

@echo ========== Locating VSEWSS bin directory ==========
@set vsewssbin=%programfiles%\Microsoft SharePoint Developer Tools 9.0\svc\bin
if not exist "%vsewssbin%" set vsewssbin=%ProgramW6432%\Microsoft SharePoint Developer Tools 9.0\svc\bin
@echo VSEWSS bin: %vsewssbin%

@echo ========== Copying assemblies to VSEWSS bin directory ==========
@xcopy "%1*.dll" "%vsewssbin%" /R /Y

To use the script, create a bat file in your project and call it from a post build event. It does take 64-bit installations into account, and it will also work on your Team Build server.

A Simple Build Script for Packaging Visual Studio Extensions for Windows SharePoint Services (VSEWSS 1.3) Projects with Team Build

In a previous post, I provided a walkthrough for automating builds for VSEWSS 1.3 projects.  The script I used was based on the SharePoint Guidance Drop 11, and after using it, I felt it could be simplified.  I’m a proponent of implementing the “simplest possible thing that will work,” and the following build script contains the minimal functionality needed to automate the building and packaging of your SharePoint solutions.

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">

  <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\TeamBuild\Microsoft.TeamFoundation.Build.targets" />

  <ProjectExtensions>
    <ProjectFileVersion>2</ProjectFileVersion>
  </ProjectExtensions>

  <PropertyGroup>
    <RunTest>false</RunTest>
    <RunCodeAnalysis>Never</RunCodeAnalysis>
    <SkipWorkItemCreation>true</SkipWorkItemCreation>
  </PropertyGroup>

  <PropertyGroup>
    <ContosoSolutionDir>c:\NightlyBuild\main\</ContosoSolutionDir>
    <ContosoSolutionName>Contoso.sln</ContosoSolutionName>
    <ContosoWSPName>Contoso.Deployment.wsp</ContosoWSPName>
    <ContosoDeploymentProjDir>c:\NightlyBuild\main\Contoso.Deployment\</ContosoDeploymentProjDir>
    <IDEPath>C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\</IDEPath>
  </PropertyGroup>
  
  <ItemGroup>
    <SolutionToBuild Include="$(BuildProjectFolderPath)/../../main/Contoso.sln">
    </SolutionToBuild>
  </ItemGroup>

  <ItemGroup>
    <ConfigurationToBuild Include="Debug|Any CPU">
      <FlavorToBuild>Debug</FlavorToBuild>
      <PlatformToBuild>Any CPU</PlatformToBuild>
    </ConfigurationToBuild>
  </ItemGroup>

  <Target Name="AfterCompile">
    
    <!-- The extensions modify files in the pkg directory, so those files cannot read only-->
    <BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)" BuildUri="$(BuildUri)" Message="Making the pkg directory editable.">
      <Output TaskParameter="Id" PropertyName="StepId" />
    </BuildStep>
    <Exec Command="attrib -R &quot;$(ContosoDeploymentProjDir)pkg\*.*&quot; /S /D" />
    <BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)" BuildUri="$(BuildUri)" Id="$(StepId)" Status="Succeeded" />

    <!-- Build using /package switch  -->
    <BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)" BuildUri="$(BuildUri)" Message="Packaging wsp.">
      <Output TaskParameter="Id" PropertyName="StepId" />
    </BuildStep>
    <Exec Command="&quot;$(IDEPath)devenv&quot; &quot;$(ContosoSolutionDir)$(ContosoSolutionName)&quot; /Deploy debug /Package" />
    <BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)" BuildUri="$(BuildUri)" Id="$(StepId)" Status="Succeeded" />

    <!-- Copy package to drop folder -->
    <BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)" BuildUri="$(BuildUri)" Message="Copying wsp to drop folder.">
      <Output TaskParameter="Id" PropertyName="StepId" />
    </BuildStep>
    <Copy SourceFiles="$(ContosoDeploymentProjDir)bin\debug\$(ContosoWSPName)" DestinationFolder="$(DropLocation)\$(BuildNumber)" />
    <Copy SourceFiles="$(ContosoDeploymentProjDir)bin\debug\setup.bat" DestinationFolder="$(DropLocation)\$(BuildNumber)" />
    <BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)" BuildUri="$(BuildUri)" Id="$(StepId)" Status="Succeeded" />

  </Target>
  
</Project>

Visual Studio Extensions for Windows SharePoint Services Error: System.ArgumentException, Value does not fall within the expected range

A common error you may encounter when using VSEWSS 1.3 is “System.ArgumentException, Value does not fall within the specified range.”  This post outlines the common causes and resolutions for the issue. 

One suggestion I have seen on the web is to delete the pkg directory since it is auto-generated each time.  It is important that you do not do this, because some information about the solution and features is stored in files in the pkg directory.  In fact, when doing team development with VSEWSS, the pkg directory must be included in the project and source control so other developers can properly package the solution. 

The following outlines the common causes and resolutions:

Remove ASP.NET Web Sites from the Solution

In the VSEWSS 1.3 release notes, there is a known issue: “You may receive the error “Value does not fall within the expected range.” When deploying a SharePoint solution that also contains an ASP.NET Web Project.”  The resolution is to “use a separate Visual Studio solution for each of the ASP.NET and SharePoint projects.”  If you need a web project, use a web application project instead of a web site.

Ensure Files Added as a Link Were Not Moved, Renamed or Deleted

The most common cause for the ArgumentException is that a file referenced in manifest or rootfiles is missing.  One of the things I like to do is develop in a separate projects and link items into the deployment project.  This allows me to use a web application project and test changes without having to deploy them to SharePoint first.  The downside of linking files is that the links aren’t updated if the source file is moved, renamed or deleted.  For example, consider the following project where I have an image file to deploy.  Note the shortcut icon indicates the image was added as a link:

image

If I rename the source file in the Contoso.Web project to “pageerr.gif,” VSEWSS packaging will fail because the file is referenced in the manifest and rootfiles, but cannot be found:

image

To resolve the issue, look for linked files that have a exclamation icon (which can be difficult to see) and re-link them.

Verify your Installation is not Corrupt

This is the least likely cause, but I experienced this one time.  To test your installation, create a new SharePoint project using the Empty template.  Package the solution from the solution view.  Switch to the wsp view and refresh.  If there is something wrong with your installation, you will see a message like this:

image

To fix the issue, uninstall the extensions, download the latest version of the extensions, and re-install.  Re-test creating an empty project.

Conclusion

The Visual Studio Extensions for Windows SharePoint Services can substantially increase your SharePoint development productivity.  However, when things go wrong, the extensions can be a productivity killer.  The error messages often do not contain enough detail to identify the cause and solution.  I hope this post saves you many hours of troubleshooting when you receive an ArgumentException.

Automate SharePoint Solution Builds with Visual Studio Extensions For Windows SharePoint Services (VSEWSS) 1.3

In this walkthrough, I’ll demonstrate how to achieve automated builds and continuous integration by creating a build script for VSEWSS 1.3 solutions.  The patterns and practices SharePoint Guidance  (11 drop) contains an example build script that this article is based on, but I found some issues with it in practice that I’d like to share.  Before we get started, I’d like to talk about the design of the extensions and some of the problems you may encounter with them. 

VSEWSS 1.3 stores information used to build the wsp file in a directory named “pkg” that is not included in the project.  This works fine for individual development on a single machine, but not so well for team development and build automation.  The pkg directory must be manually included in the project so files needed to build the wsp are included in source control.  If you forget to do this, your teammates won’t be able to build the wsp, and you risk losing deployment configuration.  Furthermore, if you later add a new feature, you also have manually add the new feature subdirectory to the project.  In addition to storing configuration information, the pkg directory is also where solution files (feature.xml for example) are automatically generated.  When you package the wsp (or refresh in the wsp view), the files in the pkg directory must be checked out to be re-generated.  This also causes problems on the build server because the files in the pkg directory are read-only because they are checked into source control.  An improved design would be to store wsp configuration information in a folder/file that is part of the project and use the pkg directory only for generating packages based on the wsp configuration files.  So now that you are aware of some of the team development “gotchas” with VSEWSS 1.3, I’ll show you, step-by-step, how to configure automated builds.

If you just want to see the build script, get it hereUpdate: get a simplified build script here.

If you have not installed TFS Build, you will need to install it using the TFS installation media.  You will also need to configure a service account such as TFSBuild, and add the account to the TFS server group Team Foundation Licensed Users and the Team Project(s) group Build Services.

To get started, configure the build agent.  In the team project select Builds > Manage Build Agents…

Manage Build Agents

Enter the build agent properties for your build server

Build Agent Properties

Create a new build definition by selecting Builds > New Build Definition…

New Build Definition

Enter a name for the build definition

Build Definition General 

Configure the workspace.  Note: all files in the source control folder will be downloaded to the local folder you specify, so be sure the location you specify has enough free space

Build Definition Workspace

Create the project file (TFSBuild.proj) by selecting the version control folder where you want it stored and selecting Create…

Build Definition Project File

Select the Visual Studio solution to build and click Next

MSBuild Project File Creation Wizard Selections

Select the configuration(s) to build and click Next

MSBuild Project File Creation Wizard Configurations

If you want to run unit tests or code analysis during the build, configure it and click Finish

MSBuild Project File Creation Wizard Options

Now that the project file is created, click OK

Build Definition Project File

Configure how many builds you would like to retain and click OK.  Note: you should at least keep the latest of failed or partially succeeded builds or the build log file you need to troubleshoot will be deleted

Build Definition Retention Policy

Enter a share where the build files will be dropped and click OK

Build Definition Defaults

Configure what triggers the build, either manually, on a schedule or when files are checked in (continuous integration)

Build Definition Trigger

Test the build by highlighting the build definition and selecting Queue New Build…

Queue New Build

Click Queue

Queue Build

At this point you should have a working build, but it doesn’t package the wsp file yet

Build Results

Next, we’ll modify the build script to package the wsp file.  The build steps are as follows:

  1. Clean up any workspaces / files left by the previous build packaging
  2. Build the Visual Studio solution
  3. Delete the workspace that was created by the build
  4. Create a workspace for the wsp packaging build
  5. Open a second instance of the IDE and package the solution using the /package switch
  6. Copy the wsp to the drop folder

In addition to this process, a nice to have is to display the packaging steps in the GUI, so we’ll use the BuildStep task to accomplish that

To get started, get the latest version of the Team Build Types folder

Get Latest Version Team Build Types

Check out the TFSBuild.proj file.  Note: when developing the build script, you will have to check it out to work on it, and check it back in to test it

Check Out TFSBuild.proj

Add the custom tasks used by the build.  Although we could accomplish what we need to using TF commands, these tasks are installed with TFS and work well

<Project DefaultTargets="DesktopBuild" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">

  <!-- These tasks are used by the Team Build process defined in this file -->
  <UsingTask TaskName="Microsoft.TeamFoundation.Build.Tasks.DeleteWorkspaceTask"
           AssemblyFile="$(TeamBuildRefPath)\Microsoft.TeamFoundation.Build.Tasks.VersionControl.dll" />
  <UsingTask TaskName="Microsoft.TeamFoundation.Build.Tasks.CreateWorkspaceTask"
             AssemblyFile="$(TeamBuildRefPath)\Microsoft.TeamFoundation.Build.Tasks.VersionControl.dll" />

Add the variables needed for wsp packaging.  Note: there is probably a better way to determine the location of the IDE

  <PropertyGroup>

    <!--  
    Variables added for VSeWSS 1.3 builds 
    -->

    <!--  IDEPath
    The path in which DevEnv and TF reside 
    -->
    <IDEPath>C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE</IDEPath>

    <!-- TempWorkspaceName
    Workspace name, must not match an existing namespace name 
    -->
    <TempWorkspaceName>NightlyBuildTempWorkspace</TempWorkspaceName>
    
  </PropertyGroup>

Add the solution to build.  Note: this will build the binaries, but will not generate the wsp file

  <ItemGroup>

    <SolutionToBuild Include="$(BuildProjectFolderPath)/../../Intranet/Intranet.sln">
        <Targets></Targets>
        <Properties></Properties>
    </SolutionToBuild>

  </ItemGroup>

Add the target to clean up any files left by the previous build packaging (if the previous build failed or was cancelled).  This target is called during the build just before the workspace is created to build the solution

  <!-- Before the build workpace is initialized -->
  <Target Name="BeforeInitializeWorkspace" >

    <!-- Delete temporary workspace (if left by previous build) -->
    <BuildStep
        TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
        BuildUri="$(BuildUri)"
        Message="Deleting temporary workspace &quot;$(TempWorkspaceName)&quot; (if left by previous build).">
      <Output TaskParameter="Id" PropertyName="StepId" />
    </BuildStep>

    <DeleteWorkspaceTask
          TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
          BuildUri="$(BuildUri)"
          Name="$(TempWorkspaceName)"
          DeleteLocalItems="true" />

    <DeleteWorkspaceTask
          TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
          BuildUri="$(BuildUri)"
          Name="$(TempWorkspaceName)"
          DeleteLocalItems="false" />

    <BuildStep
              TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
              BuildUri="$(BuildUri)"
              Id="$(StepId)"
              Status="Succeeded" />

    <!-- Error Occurred -->
    <OnError ExecuteTargets="MarkBuildStepAsFailed" />

  </Target>

Add the error handling target.  If any of the other steps fail, they will call this target using ExecuteTargets

    <!-- Handles custom errors -->
  <Target Name="MarkBuildStepAsFailed">
    <BuildStep
        TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
        BuildUri="$(BuildUri)"
        Id="$(StepId)"
        Status="Failed" />
  </Target>

In the AfterCompile target, add the step to delete the workspace that was automatically created by the build

<!-- After the solutions are compiled -->
  <Target Name="AfterCompile" >

    <!-- Delete build workspace -->
    <BuildStep
            TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
            BuildUri="$(BuildUri)"
            Message="Deleting build workspace &quot;$(WorkspaceName)&quot;.">
      <Output TaskParameter="Id" PropertyName="StepId" />
    </BuildStep>

    <DeleteWorkspaceTask
          TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
          BuildUri="$(BuildUri)"
          Name="$(WorkspaceName)"
          DeleteLocalItems="false" />

    <BuildStep
              TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
              BuildUri="$(BuildUri)"
              Id="$(StepId)"
              Status="Succeeded" />
    <!-- END Delete build workspace -->

Just below that, add the build step to create the temporary workspace

    <!-- Create temporary workspace -->
    <BuildStep
        TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
        BuildUri="$(BuildUri)"
        Message="Creating temporary workspace &quot;$(TempWorkspaceName)&quot;.">
      <Output TaskParameter="Id" PropertyName="StepId" />
    </BuildStep>

    <CreateWorkspaceTask
          TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
          BuildUri="$(BuildUri)"
          BuildDirectory="$(BuildDirectory)"
          SourcesDirectory="$(SolutionRoot)"
          Name="$(TempWorkspaceName)"
          Comment="Temporary workspace">
    </CreateWorkspaceTask>

    <BuildStep
              TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
              BuildUri="$(BuildUri)"
              Id="$(StepId)"
              Status="Succeeded" />
    <!-- END Create temporary workspace -->

Add the step to package the solution.  This first marks all files in the pkg directory as not read only--the packaging will not be able to overwrite these files otherwise.  Then it runs the IDE and uses the /package switch to generate the wsp.  Finally, it copies the wsp to the drop folder.  If you had multiple wsp files to build, you could repeat the steps below or refactor into a target

    <!-- Place projects to package here -->

    <!--  Build and package solution -->
    <BuildStep
        TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
        BuildUri="$(BuildUri)"
        Message="Packaging &quot; Contoso Intranet solution&quot;.">
      <Output TaskParameter="Id" PropertyName="StepId" />
    </BuildStep>

    <!-- The extensions modify files in the pkg directory, so those files cannot read only-->
    <Exec Command="attrib -R &quot;$(BuildDirectory)\Contoso\Intranet\Deployment\pkg\*.*&quot; /S /D" />

    <!-- Open a second instance of the dev environment and build using /package switch  -->
    <Exec Command="&quot;$(IDEPath)\devenv&quot; &quot;$(BuildDirectory)\Contoso\Intranet\Intranet.sln&quot; /deploy debug /package" />

    <!-- Copy to drop location -->
    <Exec Command="xcopy &quot;$(BuildDirectory)\Contoso\Intranet\Deployment\bin\debug\Contoso.Intranet.wsp&quot; &quot;$(DropLocation)\$(BuildNumber)\&quot; /E /Y /R" />

    <BuildStep
              TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
              BuildUri="$(BuildUri)"
              Id="$(StepId)"
              Status="Succeeded" />
    <!-- END Build and package solution -->

Add the build step to delete the temporary workspace we created

    <!-- Delete temporary workspace -->
    <BuildStep
        TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
        BuildUri="$(BuildUri)"
        Message="Deleting temporary workspace &quot;$(TempWorkspaceName)&quot;.">
      <Output TaskParameter="Id" PropertyName="StepId" />
    </BuildStep>

    
    <DeleteWorkspaceTask
          TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
          BuildUri="$(BuildUri)"
          Name="$(TempWorkspaceName)"
          DeleteLocalItems="false" />

    <BuildStep
              TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
              BuildUri="$(BuildUri)"
              Id="$(StepId)"
              Status="Succeeded" />
    <!-- END Delete temporary workspace -->

Check the project back in to source control, and queue the build to test it

RV-AIR: A Five-Step Process to Troubleshoot and Fix Just About Anything

Effective troubleshooting is an important skill not only in the information technology field, but in many other professions.  Auto mechanics, electricians, counselors and doctors are essentially professional troubleshooters.  They identify problems, theorize solutions from their knowledge and experience, and systematically test them whether they be a new alternator or a prescription for high blood pressure medication.  I’m not a doctor, but being a good troubleshooter makes me feel like one.  In this article, I’ll share an age-old process for troubleshooting, “RV-AIR,” that was taught to me as an electronics engineer in the armed forces. 

Recognize

The first step of effective troubleshooting is to recognize that a problem exists.  Obvious, yet often ignored, problem recognition is the single most important troubleshooting step.  That’s because the way in which we discover a problem impacts how we fix it, and can be the difference between treating the symptoms and curing the “disease.”  Problem recognition can be categorized as reactive or proactive, and describes our capability on a continuum from immature to mature respectively.  Reactive recognition is discovering a problem only when someone reports it.  Some might call this “fire fighting.”  When we are in a reactive state, we don’t know when or from where the next problem will arise, and the time we spend “putting out fires” takes us away from what we want, or need, to work on.  On the other end of the spectrum is proactive recognition in which problems are identified as soon as or before they occur.  Using the doctor-patient analogy, proactive recognition is a preventative checkup that identifies high blood pressure and prevents a heart attack, and reactive recognition is discovering high blood pressure after having the heart-attack.  For software systems, proactive recognition is a health monitoring solution such as Microsoft System Center Operations Manager, but the key concepts of proactive maintenance and health monitoring transcend systems and disciplines.

Verify

Once a problem has been reported either by a person or a system, the next step is to verify it.  It is very difficult to troubleshoot a problem that cannot be reproduced, and it is equally frustrating to waste time troubleshooting something that was never broken in the first place.  As much as we like our customers, the PEBKAC (Problem Exists Between Keyboard and Chair) factor applies.  I’m not saying that user’s are stupid, but experience dictates that you should trust your customers, but not their problem reports.  Let me illustrate this point with an example.  Your newly licensed teenage daughter calls you in a blind panic that the expensive car you lent her won’t go into gear.  What could be wrong?  You begin theorizing, “maybe she ran over something,” “maybe when I got my fluid changed, they didn’t put the plug back in.“  Then you jump to a diagnosis and a decision, “The transmission is broken, so I better call a tow truck and make an appointment at the dealership.”  You have the car towed to the shop and spend hundreds of dollars to discover that nothing is wrong; your daughter simply forgot to press the brake pedal before putting it into gear!  The moral of the story is that problem verification saves time, money and relationships.

Analyze

After a problem has been recognized and verified, the next step is to analyze it.  During this step, the symptoms of the problem are analyzed and a set of possible causes, or theories, are identified.  The result is a ranked list of potential solutions that will be systematically proved or disproved in the next step.  Knowledge and experience plays a large role in how you arrive at your theories and the more troubleshooting you do, the better you will get at it.  Strive  to identify root causes and permanent solutions.  Leverage the experience of others by thoroughly researching the problem on your own first before asking others.  Effective troubleshooters are effective researchers, and they know where to look, how to filter out noise and identify useful nuggets of information.  From your research and analysis, you will have a list, at least in your head, of the potential causes.  Rank the causes from most to least likely and identify a solution for each.

Isolate

After analyzing the problem and identifying the most likely causes and solutions, the isolate step is a systematic process of elimination.  For each of your theories, from most to least probable, test the solution in a non-production environment (if possible) so that you don’t introduce new problems.  Go slow and apply only one change before each test.  Beware of false positives and validate the root problem is solved and not just the symptoms.  If you are unable to find a solution, then you may need to collect additional data and return to analysis.  Once you find a solution, the next step is to create a plan for applying the fix.

Repair

The final troubleshooting step is to permanently repair the problem by applying the solution identified in the isolate step. 

Conclusion

RV-AIR is a proven process for finding and fixing problems and is equally effective for tracking down a memory leak as it is for finding a shorted electrical outlet in your house.  The key to effective troubleshooting is to recognize, verify, analyze, isolate and repair.  Happy troubleshooting!

Posted by johnwpowell | 3 Comments

Make the RSS Feed Viewer Web Part Dynamic Using Filters and Web Part Connections

One of the shortcomings of the RSS Viewer web part is that it can only be bound one feed.  In this walkthrough, I’ll show you how to make the RSS Viewer web part dynamic with filter web parts and web part connections to enable binding to multiple feeds.

 

Create the RSS Feeds List

In View All Site Content, select Create

image

Select Custom List and enter a name such as “RSS Feeds.”  Next, add two columns to the list.  The first column will hold the feed URL and the second column is a one-line description of the feed that will be displayed in the filter list.  Name the column “Feed URL,” set the type to Single line of text (not Hyperlink), make it required, and add it to the default view.

image

Add the description column.  Name the column “Feed Description,” set the type to Single line of text, make it required, and add it to the default view.

image 

Next, add some feeds to the list:

image 

Add an RSS Viewer Web Part

Add an RSS Viewer web part to a page:

image

Configure the web part to default to your favorite feed:

image

 

Add a Filter Web Part

Add a filter web part to the page to control which feed is displayed in the RSS Viewer.  Add a SharePoint List Filter web part to the page:

image

Configure the filter web part to use the values from the feed list:

image

 

Connect the Filter Web Part to the RSS Viewer Web Part

Next, connect the filter web part to the RSS viewer web part.  Select Connections > Send Filter Values To > RSS Viewer:

image

Set the Connection Type to “Get Feed URL From”:

image

 

Test the Filter

Click the browse button on the filter:

image

Select one of the feeds and the corresponding feed should display in the RSS Viewer:

image 

 

Conclusion

The RSS Viewer web part can be made more dynamic and provide a better user experience by using filter web parts.  In this walkthrough I showed you one type of filter, but I invite you to investigate other approaches using some of the other out of the box filter web parts.

 

References and Additional Reading

Posted by johnwpowell | 2 Comments
Filed under: ,

Subscribe to SharePoint Web Parts using Internet Explorer 8 Web Slices

One of the new features of Internet Explorer 8 is Web Slices.  This feature enables you to subscribe to a section of a web page and notifies you when the content changes.  By design, SharePoint Web Parts are a natural fit for this feature, and in this article I’ll show you how to build a Web Part that does just that.  I’ll also show you how easy it is to develop a web part using the CTP release of Visual Studio 2008 Extensions for Windows SharePoint Services (VSSWse) 1.3.

About Web Slices

Web Slices enable you to have a very narrow control over the content you subscribe to.  When you browse a page that has Web Slice sections, they “light up” when you hover over them:

image

In addition, the new Web Slice icon on the toolbar lights up, and the menu is populated with all the slices on the page:

image   

When you subscribe to a slice, the web page section is added to the toolbar and is periodically refreshed.  The refresh interval is completely customizable and can also be refreshed manually:

image 

Defining a Web Slice Section

Web Slices are defined with HTML tags that have certain CSS classes.  You can read the full specification, but here’s all you need to know to build your first Web Slice:

To define a Web Slice section, use the hslice class name:

<div class="hslice" id="1">…</div>

To define the slice title, use the entry-title class name:

<p class="entry-title">Game System - $66.00</p>

To define the slice content, use the entry-content class name:

<div class="entry-content">This auction closes in 4 hours.</div>

The end result looks like this:

 <div class="hslice" id="1">

    <p class="entry-title">Game System - $66.00</p>

    <div class="entry-content">This auction closes in 4 hours.</div>

</div>

Build a SharePoint Web Part

For this example, I’m using VSSWSE 1.3 which you can read about here.  I would recommend it not only because it’s easy to use, but also because it is sure to become the de-facto standard.  To get started, create a new Web Part project:

image

Choose if you want to deploy to the GAC or bin directory.  We’ll use GAC for this example:

image 

A solution is created with a default web part named WebPart1.  Rather than renaming it, delete the WebPart1 folder and add a new Web Part to the project with a more descriptive name:

image

Next, configure the url of your SharePoint site you want to use to test the Web Part.  On the Debug settings, set the start url:

image

In the Web Part, override the CreateChildControls method:

image 

This code generates the needed HTML and is pretty self-explanatory.  I chose to hide the Web Slice entry-title as it would be redundant beneath the Web Part title.  To test the code, right-click the solution or project and select Deploy.  This will package the Web Part as a feature and deploy and activate it on the site:

image 

Next, add the Web Part to a page in the site and test it.

How I Wished It Worked

Web Slices are powerful, but I don’t really want to develop a bunch of new Web Parts to use that feature.  I wish every Web Part were capable of wrapping its content in a Web Slice div tag.  I’m sure there is a way to inject this functionality (and there might even be a supported method), but I wish it were built in to the Web Part framework.  Every Web Part could then have a Web Slice category with relevant settings:

image

Wouldn’t that be powerful?

Summary

In this article, I demonstrated how to build a Web Part that users can subscribe to using Web Slices.  I also showed you how easy it is to build a Web Part using the CTP release of Visual Studio 2008 Extensions for Windows SharePoint Services version 1.3.  Happy slicing!

References and Additional Reading

How to Copy an Assembly From the GAC to the File System

Sometimes you need a local copy of an assembly from the GAC and here is a quick tip on how to do it.  The GAC can be found in the c:\windows\assembly directory, but if you try to browse it, the following custom shell extension appears:

image

This view does not provide the ability to copy assemblies, but it does provide some very useful information such as the strong name details.  Here are some options to get around that feature to copy an assembly from the GAC.

Option 1: Disable the Shell Extension in the Registry

One possibility, but not the best one, is to disable the shell extension.  I don't like this approach because it involves editing the registry and I like the information provided by the shell extension.  Here's how to disable the extension if you want to.  Open the registry editor and add/set the HKLM\Software\Microsoft\Fusion\DisableCacheViewer DWORD value:

image

Set the value to 1:

image

Once you make that change, you can browse the directory:

image

 

Option 2: Go Command-O

Another option is to copy assemblies from the GAC from the command line.  This approach works well if you prefer working from the command line, but if you like to right-click with a mouse, this might not be the choice for you.

I highly recommend PowerShell, but you can use Windows Command Prompt.  Find your way to the c:\windows\assembly directory and copy the file you need:

image

Option 3: Use the SUBST Command

The SUBST command allows you to create a shortcut to a path on your file system and assigns that shortcut a drive letter.  I really like this approach because you have the option of using Windows Explorer without having to disable the shell.

Suppose you want to create a G Drive (G for GAC), use the following command: SUBST G: C:\WINDOWS\ASSEMBLY

image

Then in Windows Explorer you are free to double-click and right-click to your heart's content and still use the shell extension. 

image

Posted by johnwpowell | 8 Comments
Filed under: , , ,

Consume SharePoint Web Services with WCF using the Repository, Gateway, Mapper, Domain Model and Factory Design Patterns

The SharePoint developer community has produced a wealth of knowledge and code samples that demonstrate how to consume SharePoint web services and leverage them in domain-specific applications.  This information is often task-centric, such as  "How to add an Item to a List using SharePoint Web Services."   In this article, we will take a framework-centric approach and apply sound, fundamental object-oriented (OO) design principles to implement a reusable library for working with the SharePoint web services.

Motivation and Goals

My motivation for this article is to help you create a foundation for a framework that can be used in one, or reused across many SharePoint-integrated applications.  I also want to demonstrate how good software design principles can (and should) be applied to SharePoint development, and confess my own sins for not always following them.  For some reason, I often find myself thinking procedurally when developing a SharePoint customization.  Perhaps it's because the product is a 70% solution and requires a different mindset and approach than a large greenfield software project does.  Maybe customers expect results faster when you build it on SharePoint, and I've taken shortcuts to meet them.  Or maybe I've just been lazy, but in any case, here are some of the issues I've seen or been responsible for:

  • Re-writing the same SharePoint code on each new project
  • Not refactoring within and across applications
  • Tightly coupling application code to the XML input and output from SharePoint services
  • Lack of a domain model; designing procedurally instead of applying domain-driven design (DDD)
  • Not having well-defined layers and strategies
  • Failure to design for testability and not writing unit tests

Here are some of the goals I would like to achieve with this framework:

  • Implement a domain model that is independent and decoupled from the SharePoint services
  • Define a set of patterns and strategies for communicating with the SharePoint services
  • Create a reusable library that can be used in many different domain-specific applications
  • Use DDD and Test-Driven-Design (TDD)
  • Apply good OO principals and patterns
  • Use Windows Communication Foundation (WCF)

Design

In the next section, I'll walk through the implementation and the process I went through to arrive at the design below.  Generally speaking, I used DDD and TDD (where it made sense) and followed the Single Responsibility principle.  If you are not familiar with this, it is often described as "a class should only have one reason to change."  I call it the "and test."  I describe the purpose of the class and if I have to use the word and, I consider refactoring some of the functionality to another class.

In the diagram below, the client application uses the repository and domain classes.  The repository is responsible for retrieving domain objects based on criteria.  The repository uses one or more service gateways to get the information it needs.  The service gateway communicates with the SharePoint web services using a WCF client.  The service calls typically return XML responses, and it is the job of the service response mapper to translate the XML to a domain object.  Finally, the WCF client factory creates clients configured to call the SharePoint services.

Get the code

SPServicePattern

 

Implementation

In this example, the goal is to get all the lists in a given site.  Following DDD, we will start the design in the domain layer and create a list class that has a title property:

image

Create the list repository class to define the method clients will call the get the lists in a given site:

image

Following TDD, we'll create a unit test that will fail until we implement the repository:

image

Return to the repository class and add the implementation to call the lists service gateway which we haven't created yet.  How did I know to create a lists service gateway?  I decided to have a gateway that corresponds to each SharePoint web service and did some research to find the exact service and method that would return the lists for a given site.  The implementation below is very simple, but you can imagine a more complex repository that uses several service gateways and does some additional processing to construct a domain object graph.

image

Create the lists service gateway so the project will compile:

image 

Create a failing unit test for the lists service gateway:

 image

Next we'll add a reference to the SharePoint Lists service.   Note: Visual Studio will add an app.config and a Properties > DataSources folder and you can delete both of them.  We are going to configure WCF programmatically.

image 

 

Return to the lists service gateway and fill in the implementation.  Now I'm pulling a "Julia Childs" on you.  I already went through several iterations of TDD and refactoring to arrive at the implementation below.  Initially this method had all the code to call the web service, and there was no WCF client factory nor a mapper.  After implementing this logic in several gateways, I refactored the commonality to classes.  It made sense to have a factory to construct WCF clients in a consistent way and allow me to make a change in one place if I discovered something new.  The Single Responsibility "and test" led me to refactor the mapping code to a separate class and the end result is a much cleaner implementation:

image

 

Next, we'll implement the WCF client factory.  As you can see, it has been through a few iterations of TDD and refactoring starting with the service url class.  This class contains constants and methods for working with SharePoint service urls.  The other thing you'll notice is the the WCF configuration that is typically done in a configuration file is being done programmatically in the service binding factory class.

image

Here is the implementation of the service binding factory:

image

The gateway uses a mapper to map the XML response from the SharePoint web service to a domain object.  The XML response is in the form of <element attribute1...n /> which, in this case, translates nicely to domain objects and properties.  The code below uses Linq to find all List elements and for each one it calls MapInternal which creates a list object from the element:

image

Now if we return to the repository GetLists unit test, it passes and output each list title in the site:

image

 

Summary

In this article and walkthrough, I demonstrated how DDD, TDD, patterns and good OO principles can be applied to implement a framework that consumes SharePoint web services.  Even though I focused on the web services, the SharePoint API can also be regarded as a service and the same fundamentals can be applied to developing applications that run on the server.  I hope this inspires and helps you to implement your own framework and improve the quality and reuse of your SharePoint code.

Get the code

 

References and Additional Reading

SharePoint Designer Copy List Item Workflow Activity Does Not Copy All Fields

A common SharePoint customization request is to copy or move items and documents between lists.  This can be accomplished without development by creating a workflow using SharePoint Designer (SPD).  With this approach, you may encounter issues with list item fields not copying or copying intermittently.  This article will explain how the copy list item activity works, what can go wrong and how to troubleshoot and resolve the issues.

About SPD Workflows

If you are not familiar with SPD workflows, here is a quick primer on how to create a workflow that copies a list item to another list.  Open the site and select File > New Workflow.  Select the source list and set the start options.  Note in this example, the workflow executes when a new list item is added.

Define your new workflow

The workflow consists of steps which have conditions and actions.  In this example, there are no conditions, only an action to copy the list item:

Copy List Item Step

Configure the action to copy the current item in the source list to the target list and click Finish to associate the workflow with the source list.

Configure Copy List Item Activity 

Add a new item to the source list and you can see the outcome of the workflow:

Copy List Item Source

The item was copied to the target list:

Copy List Item Target

How the Copy List Item Activity Works

The copy list item activity iterates through all the fields in the source list and tries to find a match in the target list.  The fields are considered compatible for copying when the following is true:

  • The source and target field names are the same.  It compares the field name you see in the browser (SPField.Title), not the internal/static name (SPField.InternalName)
  • The source and target field are the same type
  • The source and target fields are compatible.  For example, if the source field allows multiple selections, but the target does not, or the source allows selecting people and groups, but the target only allows selecting people
  • When the source field is a lookup to another list, the target field must use the same list
  • The source field is not read only
  • The source field is not a special field, such as ContentTypeId

After the field matches have been determined, a list item is created in the target list using a system update (does not trigger event receivers / workflows and the created by user is system).  In addition, when the field type is User, the user(s) are created in the target web if they don't already exist.

What Can Go Wrong

There are two common symptoms and the underlying causes of each is human error.  But don't beat yourself up; they are easy mistakes to make and correct.  Use the following to troubleshoot the issue:

  • When a SPD workflow doesn't copy one field to the target list:
    • The field names don't match
    • The field names match, but the field type and the settings do not 
  • In a document library, when none of the fields are copied (except the file name), or when all the fields are copied but intermittently:
    • The issue is that everything is working as designed and the workflow hasn't accounted for it!  There is a difference between New and Upload 

The Difference Between New and Upload in a Document Library

In a document library, users can add a document two ways through the browser.  The first option is to click New.  This opens the client application associated to the list template such as Microsoft Word.  In Word 2007, the list item properties appear on the Document Information Panel (In Word 2003, the list item properties are displayed in a dialog when you save the document):

Document Information Panel

Because the list item properties are captured by the client application, the document and properties are saved at the same time.  The save triggers the workflow created event receiver logic and the copy will succeed.  When the document is added using the Upload (or Upload Mutliple) option, the upload occurs in two stages.  The first stage is adding the file to the document library:

Upload Document Stage 1

The second stage is updating the list item properties.  At this point, the item has been added to the document library, the created event has fired, and the copy list item workflow has completed. The properties haven't been entered, so the workflow copied the document and blank property values to the target list.

Upload Document Stage 2

The following summarizes the differences between New and Upload:

  • When a document is uploaded using New, there is only a create 
  • When a document is uploaded using Upload, there is a create when the file is uploaded and an update when (and if) the properties are updated--but the properties are updated with a system update so the update event does not fire

Troubleshooting and Solutions

  • When only one field doesn't copy
    • Don't trust your eyes, copy the field name from the source list and paste it into the field name of the target list.  For example, how long does it take you to spot the difference between these two fields:  Source:Mutiline Field, Target:Multiline Field?
    • Compare the settings between the source field and target field.  Does one allow multiple values and the other does not?
  • In a document library when all fields don't copy or copy intermittently
    • The workflow needs to account for the difference between New and Upload
      • Configure the workflow to execute when the item is changed
      • Add a condition to the copy list item step to examine the workflow status

To overcome the issue with document libraries, one possible approach is to modify the SPD workflow as follows:

  • Set the workflow to execute only when the item is created
  • In the first step, add a wait activity to wait until a required field is not empty (a field that is entered in the "second  stage" after the document is uploaded)

Conclusion

SPD workflows are a business-user enabling technology and a useful tool in your SharePoint customization toolkit.  I hope this article sheds some light on how to implement a copy list item workflow using SPD and how to troubleshoot and resolve common issues with the approach.

Enterprise Content Management: What it is and Why You Need It

If you were to survey corporate America on how it defines an enterprise content management (ECM) system, the majority response would be a software tool primarily used by large organizations to create and publish Web content. This article challenges that definition and proposes that ECM is a process that may or may not be supplemented with software tools. In addition, the scope of ECM extends to organizations of all sizes and beyond public Web content to any leverageable informational asset that an enterprise creates, publishes, consumes, and archives.

Before delving into ECM, we must answer two fundamental questions: what is enterprise content and why do we need to manage it? In the broadest sense, enterprise content is an informational asset owned by an enterprise. An article on the corporate Web page is but one drop in the enterprise’s content portfolio bucket, which includes content that is not traditionally managed by ECM software such as strategic plans, proposals, lessons learned, marketing slicks, images, whitepapers, and case studies. If you have ever heard (or asked) the question, “Where is the ______ we did for _____,” you are already down the path to understanding why we need to manage enterprise content.

There are many practical reasons for managing content such as controlling what is published and who can see it, but the fundamental reason is to enable an enterprise to leverage what it “knows,” and more importantly to be able to “know what it knows.” Although the topic of Knowledge Management (KM) is outside the scope of this article, the transfer of knowledge to content and content to knowledge is the overarching concept behind why ECM should be a key component of all enterprises.

The term, “ECM,” is associated with software tools and is typically reserved for medium to large companies due to the significant investment required. But if you expand the context of ECM to be a process that is supplemented by tools, it becomes apparent that enterprises of all sizes already have a system for managing content, formalized or not. Running the gamut from the “wild, wild west” of ECM to highly structured processes backed by automation, the information produced and consumed by these systems is the lifeblood of any organization.

Although there is no right or wrong ECM methodology, there are best practices for content management that can be built upon. “Right” is only measured by how well the system meets the needs of its organization. These needs should be understood before investing in a software tool. It is also critical to have a commitment from key decision makers to improve how information is managed and leveraged continuously. Although it may prove challenging to obtain this commitment, there is a strong case for ECM. By leveraging what it knows, an enterprise can use its experience as a foundation, rather than continually paying others to re-do work from scratch. Not leveraging what you know is like sending monthly payments to the bank when the loan is paid in full—that defies common sense.

The first step toward improving the process of managing information assets in your organization is being able to measure the health of your ECM portfolio against a set of goals. No single set of requirements, or one software tool will meet the needs of all enterprises. Each must establish its own goals, gather requirements, analyze alternatives, and choose between buying and building a tool.

An ECM system exists to facilitate the flow of information throughout the organization. Think of the ECM system as plumbing and information as water flowing through it. Do the pipes in your organization have leaks, bottlenecks or valves that make it difficult for authors to publish content and consumers to find it? Do some departments dig their own information wells and isolate themselves from the enterprise information supply? Analogies aside, a healthy ECM system is one that is an integral part of the business and enables the unrestricted flow of information throughout the enterprise.

Bottlenecks typically occur in the Publish phase of the ECM lifecycle. Authors create content and often encounter an information technology (IT) bottleneck when they attempt to share it with the organization, its partners or customers. This bottleneck results in frustration for the author, increased workload and resource requirements on IT, and reduces the motivation to share information. People have a job to do, and if the ECM system hinders that job, they will avoid or go around it. This results in numerous information wells across the enterprise and cripples information sharing and knowledge transfer. Bottlenecks are not just isolated to IT; however, and can be caused by many factors notwithstanding complexity, bureaucracy, and inefficient processes.

We have established that the flow of information is the overarching goal of a healthy ECM system, but we can dissect that goal into key health indicators found in each phase of the ECM lifecycle. Enterprise content moves through five phases during its lifetime: Create, Describe, Publish, Consume and Archive. The following indicators are not meant as requirements for procuring an automated system, but rather are observable points of a healthy ECM system, automated or not. Using these indicators you can establish measurable goals and embark on an ECM improvement process.

Create

Everyone in your organization has information to share, and the ECM system should facilitate, and encourage this. Treating everyone as a contributor enables people to “give back” and results in communities of interest and a continuous information feedback loop. If only a few can contribute, the content could become biased or narrowly focused. With the ECM open to a diverse author population, content creation must be simple. Authors should be able to work in familiar tools. If it is too complex to create content for the ECM system, the organization will not achieve a balanced perspective on given areas and many people simply won’t share information at all.

Key indicators found in the Create phase are:

  • Everyone is a potential contributor and should have the ability to author and submit content

  • Authors should be able to work in familiar tools. The ECM system shouldn’t preclude the author from using their word processor with features such as spelling and grammar checking or force someone to compose a lengthy whitepaper in a text box on a web page

  • Content creation must be simple. If the process is complex or requires technical intervention, people will be reluctant to share information

Describe

Before publishing and sharing content or perhaps even before composing it, the author is aware of certain attributes such as its subject and intended audience. These attributes are referred to as metadata or facets, and enable information consumers to find what they are looking for by browsing or searching. The ECM system must have a strong content typing mechanism that enables information architects to design content types, their facets and associated validation rules. To provide a concrete example, suppose an organization frequently produces case studies and whitepapers about project management. The ECM system would define a case study and a whitepaper content type. The whitepaper type could have facets such as project management lifecycle and target experience level whereas the case study type might have industry and company name. In addition, ECM systems should enforce validation rules to maintain the integrity and consistency of the metadata.

Humans have an innate need to bring order out of chaos. In nature, we categorize by genus and species, and in a brick and mortar library, books are shelved by topic. This organization by category and subcategory is referred to as taxonomy. In traditional library classification schemes such as the Dewey Decimal System, each piece of content has a "correct" place in a hierarchically organized classification system, but there needn’t be a “correct” single-faceted taxonomy. Without the limitations imposed by physical storage, automated ECM systems can dynamically generate multi-faceted taxonomies enabling information to be classified by a series of facets. This enables information consumers to categorize content in a manner that is meaningful to them.

In addition to classifying content by facets, ECM systems should enable authors and content managers to relate content with other content. By creating these relationships, information consumers can be led to other relevant information and take them down paths they may not have anticipated. Automated systems can discover relationships between by analyzing usage, identifying patterns and making recommendations. Most of us are familiar with cross-selling techniques such as “you might also be interested in this,” or “people who purchased this also purchased this.” ECM systems should do with content what retailers do with products. The payoff is in worker productivity and intellectual capital.

Key indicators found in the Describe phase are:

  • The ability to define content types and associated facets and validation rules

  • Support for both static and dynamic information taxonomies

  • Association of relevant content

Publish

Once content has been created and described, the next phase involves sharing it with all or part of the organization. The Publish phase is a checkpoint that controls what is published and who can see it. With everyone in your organization as a potential contributor and sensitive information to protect, the ECM system must enforce the publication process. Without process, the ECM system could become a dumping ground rather than a knowledge repository. We use the term workflow to describe the steps that must be taken to publish content. A basic workflow could be:

  • Author submits content for approval

  • Content manager reviews content and approves or rejects it

  • Content is published

The workflow will be different at each organization, and the ECM system should include a flexible mechanism that supports custom workflows.

In addition to controlling the publication process, determining and specifying security requirements is another major step in the Publish phase. This can be accomplished a number of ways, but automated ECM systems typically use role-based security. Each role has a specific set of rights which could include such permissions as “View,” “Edit,” “Delete,” or “Approve.” The ideal ECM system enables roles to be set at the facet or content section level. With this type of granular security, a team could collaborate on a proposal and each person could only view and modify the sections and metadata they are responsible for. The security mechanism should also be flexible to include employees, customers, partners and even anonymous users.

Versioning is an important aspect of the Publication phase. The ECM system should enable the option to store multiple versions of a given content item, but only the approved published version should be available for consumption. There are two types of content versioning:

  • Publication versioning

  • Historical versioning

Publication versioning is the ability to have a single content item with two versions, the version currently being edited and the published version. This type of versioning is an essential feature of an ECM system and prevents the modification of published content while still enabling it to be developed. Modified content should go through a workflow and upon approval, become the published version. Historical versioning is the ability to keep a historical record of changes to content over time. When enabled, this type of versioning should allow content managers to view or rollback to a previous version.

Key indicators found in the Publish phase are:

  • Customizable workflow to control what is published

  • Security mechanism that controls permissions to information

  • Support for publication versioning and the ability to enable historical versioning

Consume

The Consume phase has both the longest duration and the most importance to the organization. Through this phase, personal knowledge becomes shared knowledge, and the single most important function of the ECM system is to support this. What would be the sense of gathering, describing and publishing content it if information consumers are unable to find it? To understand the role of the ECM system during this phase, let’s examine how consumers go about locating information.

The methods by which consumers locate information can be divided into the following categories:

  • Intentional : ask an expert, searching, browsing

  • Incidental : during an intentional effort, another path to the information presents itself

  • Accidental : the information is found purely by chance in a way that could not have been predicted

Arguably the top way in which people locate information is to find someone who is regarded to them or by the organization as a subject matter expert. This approach works well in small organizations, but in larger or disparate organizations it is simply not effective. The ECM system does not have the answers to all questions and should enable consumers to locate and communicate with experts. Experts can be self-identified or nominated based off some objective criteria such as participation in working groups, prior experience or current position. If someone executes an intentional search for “Project Management,” the ECM system should not only return whitepapers and case studies, but also people with expert knowledge.

Searching is a pervasive method of locating information, and should be one of the largest investment areas in the ECM system. Information consumers should be able to execute a basic keyword search and be presented with results. In addition to keyword searches, the ECM system should have an advanced search mechanism based around the content types and facets described in the Describe phase. In other words, consumers should be able to make a request such as “show me all case studies where the sector is Public and the customer is Department of Homeland Security. ” Finally, the search should include a learning mechanism that analyzes search patterns and improves future search results.

In addition to asking an expert and searching, consumers also browse to locate information. The ECM system should provide features to support this, and one mechanism is a Library View. A library view can be thought of as a virtual bookshelf and presents a single information taxonomy. Examples of a library view can be seen at http://msdn.microsoft.com/library/ and http://www.pmboulevard.com/Library.aspx . Because there is no single correct taxonomy, the ideal ECM should enable consumers to dynamically generate multi-faceted library views such as “show me templates organized by sector then customer then project management lifecycle.”

Incidental information location happens when a user intentionally sets out to locate information and another path to the information presents itself. This type of information discovery is often overlooked, but is an essential function of an ECM system through related content, targeting, personalization and recommendations. When consuming content, users should be presented with related content which can also include files and other supplements to the main body of information. Targeting is the matching of content facets to user facets. One example of targeting might be Virginia-based employees seeing a message on the corporate intranet that North Carolina-based employees do not. Information consumers can also locate information incidentally through recommendations that include such things as “this week’s most popular articles,” and “people who were interested in this were also interested in this.”

Consumers may also find information by accident. This does not mean it was purely by chance, however. The user may not have been expecting to find information there, but though the ECM system, its content managers made a conscious decision in content placement. By highlighting content in a newsletter or a featured location on a web page, users may find themselves reading an article they currently have no interest in, but equips them for a future endeavor. The ECM system should facilitate accidental information location by enabling content managers to feature content and control its placement.

Key indicators found in the Consume phase are:

  • Support for intentional, incidental and accidental location of information

  • Ability to identify and locate subject matter experts

  • Basic and advanced search

  • Library views

  • Related content

  • Targeting and recommendations

  • Ability to feature and control content placement

  • Enable information consumers to provide feedback

Archive

The last phase of the ECM lifecycle is content disposition. All content has a lifespan of relevance, timelessness and usefulness and the ECM should archive content beyond its “sell by date.” During the Publish phase, content managers should be able to specify the publication end date, and in this case archival should be automatic. For other content, both objective and subjective decisions must be made to archive.

The ECM system should assist content managers in determining what to archive or destroy. The system should provide usage analysis to identify unused content. In addition, by enabling information consumers to provide feedback about its usefulness, content can either be updated or archived.

Key indicators found in the Archive phase are:

  • Provide usage statistics to recommend content for archival

  • Enable content managers to make archival decisions based on information consumer feedback

Conclusion

By leveraging ECM, organizations can facilitate the transformation of personal information to shared information and tacit knowledge to explicit knowledge. By “knowing what it knows,” an enterprise can use experience as a foundation, rather than continually re-doing and re-learning on each task. Hopefully this series on ECM has imparted the importance of managing content at your organization and that content and ultimately knowledge management is a continuous improvement process. By understanding the ECM lifecycle and the attributes of a healthy system, you should be inspired to set goals and undertake an ECM improvement process in your organization.

Build PowerShell CmdLets to call SharePoint Web Services using WCF

In this post I'll show you how to build a PowerShell cmdlet that consumes SharePoint web services using WCF.  What I like about this approach is that it provides the ability to use some of the SharePoint API without having to be logged in to the SharePoint server.  Although you can accomplish the same thing using PowerShell scripts and functions, in this post we'll build a SharePoint cmdlet using C#. 

About the Approach

There are several advantages of combining PowerShell and SharePoint web services:

  • Consume the SharePoint API without logging on to a SharePoint server
  • Provide a layer of abstraction and reusable task-oriented building blocks
  • Reduce risk of performance issues caused by PowerShell scripts that don't dispose of unmanaged SharePoint resources

There are also some drawbacks to writing custom cmdlets that consume SharePoint web services:

  • SharePoint web services do not expose the full API; you may need to augment them with business-specific functionality
  • Cmdlets require compilation and additional overhead over script file

Getting Started

In this walkthrough, I'm using Visual Studio 2008 on a SharePoint virtual machine.  Although you could use a Visual Studio PowerShell project template, we'll are going to build everything by hand so there are no mysteries.  So let's get started by creating a new class library project:

image 

Add a reference to PowerShell which is contained in System.Management.Automation assembly.  If you've installed the Windows SDK, you can find it in the following location: C:\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0

image

Since we are creating a class that will both describe and install a custom snap in, we need to reference System.Configuration.Install

image

Create the Snap In and Cmdlet

Add a new class that inherits CustomPSSnapIn.  Add the RunInstaller attribute to the class and describe the snap in by overriding the Name, Vendor and Description properties.  Your code should end up looking like this:

image 

Add a new class that inherits from Cmdlet.  We'll build a cmdlet to get information about a SharePoint site, so name it "GetSPWebCmdlet."  Add the Cmdlet attribute to the class. You'll notice the cmdlet name, "Get-SPWeb" is a combination of the noun string SPWeb and verb enumeration VerbsCommon.Get specified by the Cmdlet attribute:

image

Override the Cmdlets property in the snap in class and add the Get-SPWeb cmdlet to the Cmdlets collection:

image

Configure Visual Studio to Install and Add the Snap In When the Project is Debugged

Before we add any code to the cmdlet, let's configure Visual Studio to automatically deploy the snap in to make debugging easier.  We'll use a post-build event to install the snap in, so in the spirit of learning PowerShell, we'll write the post-build script in PowerShell!  Add a new text file to your project named PostBuildEvent.ps1.  Add the following script to the file:

trap
{
   $errorTxt = $_.Exception.Message
   write-output "$errorTxt"
   break
}

$targetDir = $args[0]
$targetFileName = $args[1]
$snapInName = $args[2]

write-output "Deploying..."
join-path $targetDir $targetFileName
write-output "Snap In Name: $snapInName"

set-location $targetDir
set-alias installutil $env:windir\Microsoft.NET\Framework\v2.0.50727\installutil
installutil $targetFileName

Notice the script file takes some arguments (targetDir, targetFileName and snapInName).  Using that information, it installs the SnapIn (and our SharePoint cmdlet).  Configure Visual Studio to pass the arguments to the post-build event:

image

After a SnapIn is installed, you have to add it to your environment before it can be used.  As you can see, even though our custom snap in has been installed, it is not listed when you call Get-PSSnapin:

image

To add it, call Add-PSSnapin passing in the name of the assembly:

image

We don't want to add the snap in after each code change, so we are going to automate it by adding a console file that will add the snap in into our environment when the project is debugged.  Add a new file named SharePointSamples.Console.psc1 to your project.  Put the following xml in the file:

<?xml version="1.0" encoding="utf-8"?>
<PSConsoleFile ConsoleSchemaVersion="1.0">
  <PSVersion>1.0</PSVersion>
  <PSSnapIns>
    <PSSnapIn Name="SharePointSamples.PowerShell" />
  </PSSnapIns>
</PSConsoleFile>

In the project configuration, Debug settings, configure Visual Studio to start PowerShell when you debug and in the command-line arguments section, use the -PSConsoleFile argument to specify the console file you created:

image

Implement the Cmdlet

Now that deployment and debugging are taken care of, add the following code to the cmdlet:

 image

The cmdlet takes the url of the site as an argument.  Arguments are implemented as class properties decorated with the Parameter attribute.  The work is implemented in the ProcessRecord method.  As you can see, I already did some re-factoring and created a service locator class that will give me an instance of the SharePoint Webs service (using WCF).  The code then invokes the GetWeb method and writes out the resulting xml. 

Here is the code for the service locator.  As you can see it configures the SharePoint Webs proxy:

image

Most of the key WCF configuration settings for integrated authentication are implemented in the BindingFactory class:

image

Test the Cmdlet

To test the cmdlet, run the project.  When the PowerShell command prompt opens, type Get-SPWeb.  You will be prompted for the site url if you don't provide it:

image 

Summary

In this walkthrough, you learned how to leverage PowerShell, WCF and the SharePoint web services to implement a custom cmdlet.  Using this approach, you can expand the administrative capabilities of SharePoint and build reusable, task-oriented building blocks to increase your productivity and capability.

Get the code for this article.

Additional Reading

Walkthrough: Import Data From a Line Of Business System into SharePoint User Profiles

In this walkthrough, I'll show you how to import data from a line of business (LOB) system into SharePoint user profiles and make that information searchable.  Using this approach, you can leverage information that is stored in other LOB systems and create a unified view of user information in SharePoint.

Create an Interface to the LOB System

Suppose your company has an HR system that contains information about employees.  Since it is the system of record for employee data, we don't want users to re-enter this information in SharePoint.  In this example, the HR system is backed by Sql Server and the data we need is stored in a table named "Employee."  Although we are accessing the database directly in this walkthrough, you should consider using a web service to provide a layer of abstraction and a well-defined interface. 

In our HR system, the Employee table contains a column that we can use to look up employees based on information we have in SharePoint.  In this case, we will use the "UserAccountName" column, but in a real-world implementation you need to find a key that is guaranteed to be unique such as the user's security identifier (SID).  The following shows the design of the Employee table:

image 

When SharePoint to imports data from the HR system, it is essentially a crawl operation, so the SharePoint search account will need read access to the Employee table.  Because this database contains sensitive information, we will only allow the account access what it needs and nothing more:

image

Create Application Definition

Ceate an application definition XML file so SharePoint knows what entities and actions are available from the HR system.  We could do this by hand, but it is much easier to use a tool such as BDC Meta Man.  To start, select Connect to data source:

image

Enter database connection information and click Connect:

image 

Once connected, drag the Employee table onto the design surface:

image

The tool generates the Employee entity and three actions: Finder, Specific Finder and IDEnumerator.  If you are not familiar with these terms, here is a simple explanation.  A Finder returns a list of entities.  A Specfic Finders returns a single entity, and an IDEnumerator returns the identifiers of all entities.  By default, the tool uses the primary key as the parameter to the specific finder.  Since we don't have the employee number in SharePoint, we need to modify the Specific Finder action to take an account name.  Select the action and click Modify:

image

Clear the condition from the EmployeeNumber column and add a condition to the UserAccountName.  Click Generate

image

You'll see a preview of the query.  Click OK

image

If prompted to configure parameters, just put something in the fields (like a space) and click OK

image

Edit the HR System details:

image

You can use this screen to modify the system and instance name.  You can also set the authentication mode, which we'll set to RevertToSelf.  This basically means, "whoever you are impersonating, revert to who you were."  So if you were accessing an entity as contoso\johndoe it would revert back to the SharePoint application pool account.  If you used PassThrough, contoso\johndoe would be accessing the database as himself.  After configuring the system, click Save

image

Select Configuration > Settings and specify where the XML file should be saved.  Enter a path and click Save

image

Click the green "play" button to generate the application defintion

image

Import Application Definition

Go to your Shared Services web site and select Import application definition in the Business Data Catalog section.  Browse for the application definition file and click Import.

image

Configure BDC Permissions

Configure the SharePoint permissions for the application and entities.  Click Manage Permissions:

image

Select the search account, check the Execute permission and click Save:

image

The search account needs access to both the application and the employee entity, so from the manage permission screen for the application, click Copy all permissions to descendents:

image

This will copy the same permissions from the application to all of the entities in that application

image

Test the BDC Application

You can test the application by accessing the profile page for the Employee entity.  You can find the url of the profile page by viewing the entity:

image

Copy the url into a browser and enter a parameter for UserAccountName.  You will see a generic error message "Unable to connect to [Instance Name]."  This is because we used RevertToSelf and the account accessing the database is the application pool account (contoso\spfarm in this example).

image

To prove this, grant the application pool account rights to the Employee table and the entity details will display

 image

Give the Search Account Permission to Update User Profiles

Because the import is running as the search account, that user must have the permission to update user profiles.  To configure this, from Shared Services, select Personalization services permissions:

image

Click Add Users/Groups:

image

Enter the search account and check the Manage user profiles permission.  Click Save

image 

Add an Import Connection

Next, we will configure the user profile to import the Location column into the SharePoint user profile.  In the Shared Services web site, select User profiles and properties:

image

To connect to the HR System, you have to create an import connection.  To do this, click View import connections:

image

Click Create New Connection:

image

Select Business Data Catalog and choose the entity.  There should only be one employee for each SharePoint user profile, so we'll select 1:1 mapping.  Next, we must specify the attribute in the UserProfile to pass to the Specific Finder method.  Select AccountName and click OK

image

Add a User Profile Property and Map to a BDC Entity Attribute

Add a property to the SharePoint user profile to contain the Location we are importing from the HR system.  Click Add profile property:

image

Enter a Name, Display Name and configure privacy and editing rights:

image

In the Source Data Connection drop down, select the import connection you created in the previous step.  Select the field to map from the HR System:

image

Perform a Full User Profile Import

Select Start full import:

image

The primary (AD) import will run and then there will be a short pause before the Membership and BDC import starts:

image

After the import completes, check the import log for errors:

image

If you encounter errors, you'll have to comb through the ULS logs to find the specific issue:

image

Verify the Import Worked

On the user profile page, select View user profiles and select a profile.  The property should now contain data from the HR System:

image

Make the Imported Property Searchable

Now we'll do something useful with the information we imported from the HR System by making it searchable.  We'll create a search scope so you can search for people by location.  When we added the location property to the user profile the check box was selected to make the property indexed:

image 

Before the property can be used in search, we must perform a full crawl.  From the Shared Services website, access Search Settings.  Click on Content sources and crawl schedules.  Select Local Office SharePoint Server Sites > Start Full Crawl:

image

Create a Search Scope

Once the crawl completes, return to Search Settings and select Metadata property mappings.  Locate the property you added to the user profile and edit it.  Check the box "Allow this property to be used in scopes."

image

Return to Search Settings and select View Scopes.  Click New Scope.  Enter a name for the scope such as "Hong Kong Employees," and click OK:

image

Next, add a rule to the scope by clicking on Add rules:

image

Select Property Query and select the user profile location property we added.  Enter a value such as "Hong Kong."  Select Require and click OK:

image

Return to Search Settings and in the Scopes section, click Start update now.

image

From the Shared Services website, access Search Settings.  Click on Content sources and crawl schedules.  Select Local Office SharePoint Server Sites > Start Full Crawl:

image

You can confirm the scope contains items by viewing the scopes:

image

Use the Search Scope in a Site Collection

Access the site collection settings and add the scope.  From Site Collection Administration, select Search scopes.

image

Notice the search scope you created is in the Unused Scopes section.  We want it to display in the search dropdown, so click on Search Dropdown:

image

Check the box to include the new scope:

image

The search dropdown will now display the new scope.

image

You can further customize the search scope so it takes you to the people search results page.  You could even add a tab to the Search center for each location (scope) such as "Seattle," "New York" and "Hong Kong" to make a people directory.  Finally, you can add people search web parts in other parts of the site collection to enable users to quickly find people.

Conclusion

In this walkthrough, I showed you how to import data from a line of business (LOB) system into SharePoint user profiles and make that information searchable.  This approach enables you to expose data from LOB systems and display the information in the user's profile and other areas in SharePoint.

References and Additional Reading

Posted by johnwpowell | 3 Comments
Filed under: ,

SharePoint BDC Error: Cannot have more than 30 Parameters for a Method

One thing to be aware of is that the BDC has a hard limit of 30 parameters.  If you use the BDC as designed, you likely will never hit this limit, but if you are trying to implement insert/update functionality you may encounter this little gotcha.  When it happens, you will see the following error message:

image

The BDC Meta Man tool has a write-back feature that adds custom actions for inserting and updating.  The tool will create an insert action and an update action, and add a parameter for each selected column.

image

Beware of the 30 parameter limit when designing BDC solutions.

Posted by johnwpowell | 1 Comments
More Posts Next page »
 
Page view tracker