Click here to Skip to main content
Click here to Skip to main content

Deploy and use SPCopy: Part 2

, 5 May 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
Accessing functionality in SPCopy from Part 1.

Introduction

This article, a follow-up to SPCopy, will show different methods to expose the functionality for use in SharePoint, either directly from an Application Page, or from administrative tools such as stsadm or PowerShell.

Caution: With the exception of ISPStsadmCommand, all examples in this article are created for SharePoint 2010 and use Visual Studio 2010.

ISPStsadmCommand

Anyone who has worked with SharePoint knows, stsadm.exe is a commandline tool that allows administrators to perform actions on a SharePoint server. Just as SharePoint itself is very configurable and extensible to meet demands that couldn't have been anticipated when it was created, stsadm.exe is also configurable to allow commands to be added as necessary. The method to accomplish this is by implementing the ISPStsadmCommand interface, found in the Microsoft.SharePoint.StsAdmin namespace.

Since this interface has been covered in depth by many other resources, some of which I list at the end of this article, I will only show a basic implementation.

public class HelloWorld : ISPStsadmCommand
{
    #region ISPStsadmCommand Members
    public string GetHelpMessage(string command)
    {
        return "Help World";
    }
    public int Run(string command, 
                   System.Collections.Specialized.StringDictionary keyValues, 
                   out string output)
    {
        output = "Hello, World";
        return 0;
    }
    #endregion
}

The compiled assembly is placed in the GAC and an XML file is added to the ...\12\CONFIG folder.

<?xml version="1.0" encoding="utf-8" ?>
<commands>
  <command name="hello"
           class="HelloWorldCommand.HelloWorld, 
           HelloWorldCommand, 
           Version=1.1.1.1, 
           Culture=neutral, 
           PublicKeyToken=a9baec847ddb5ad1"/>
</commands?

Again, this article is not intended to provide an in-depth coverage of the implementation of the ISPStsadmCommand interface. The included download has a full implementation for the SPCopy functionality.

PowerShell Cmdlets

Unfortunately, support for extending stsadm.exe has been depreciated in SharePoint 2010, so the above method will not work. Instead, SharePoint 2010 now makes use of Windows PowerShell and Cmdlet. One of the benefits of this is that they can be deployed as SharePoint packages, WSP, just like other SharePoint items, such as features or site definitions, along with the other benefits of Windows PowerShell, which this article won't cover.

Implementing a SharePoint Cmdlet

The first thing to understand when creating a SharePoint Cmdlet is which class to use. There are five base classes to choose from: SPNewCmdletBase, SPSetCmdletBase, SPGetCmdletBase, SPRemoveCmdletBase, and SPCmdlet. All of these can be found in the Microsoft.SharePoint.PowerShell namespace. The differences are as follows:

  • SPNewCmdletbase<TCmdletObject>: An abstract base class that is used to create new instances of objects and save them to the data store.
  • SPGetCmdletBase<TCmdletObject>: Abstract base class that allows derived classes to find and return a set of objects of type TCmdletObject.
  • SPSetCmdletBase<TCmdletObject>: An abstract base class that derived classes can use to update properties for existing objects in the data store.
  • SPRemoveCmdletBase<TCmdletObjec>: An abstract base class that removes an existing data object of the specified type from the data store.
  • SPCmdlet: Represents an abstract base class for all custom Cmdlets that are written to be used in SharePoint deployments, providing uniform behavior across all SharePoint Cmdlets.

As you can see, the first four base classes are basically CRUD operations, Create = SPNewCmdletBase, Read = SPGetCmdletBase, Update = SPSetCmdletBase, and Delete = SPRemoveCmdletBase. The last is a general base class for deriving custom Cmdlets. Since the SPSiteCopy functionality doesn't involve any CRUD operations so to speak, SPCmdlet is the base class I'll use.

SPCmdlet

To build SPCopyCmd, I'll start by selecting the Empty SharePoint Project from the New Project templates dialog.

Image2.PNG

To create a SPCmdlet, a reference to Microsoft.SharePoint.PowerShell.dll is needed. This presents a challenge though. The only place this assembly appears is in the GAC, not in the 14\ISAPI folder where other SharePoint related assemblies can be found. To locate this assembly, you need to search for it using the command:

dir Microsoft.Sharepoint.Powershell.dll /s

Image1.PNG

Once the assembly has been located, copy it to another location so it can be referenced in your project. I recommend copying it to the 14\ISAPI folder along with the other SharePoint related assemblies; this way, it can be easily found in the future. Another assembly reference that must be added to your project is to System.Management.Automation.dll. Although System.Management shows up in the .NET tab of the Add Reference dialog, this is not the one we need. The one needed doesn't show up in the .NET tab, so you must browse to it at C:\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0.

Now that all of the required assemblies have been referenced, you can start building the custom SPCmdlet.

[SPCmdlet(RequireLocalFarmExist = true, RequireUserFarmAdmin = false)]
public class SPCopyBase : SPCmdlet
{
    protected override void InternalValidate()
    {
        if(string.IsNullOrEmpty(Source))
            throw new ArgumentException("Source must be specified");
        if(string.IsNullOrEmpty(Destination))
            throw new ArgumentException("Destination must be specified");
    }
    protected override void InternalProcessRecord()
    {
        // Implementation goes here       
    }
    #region Properties
    [Parameter(Position = 0, Mandatory = true)]
    public string Source { get; set; }
    [Parameter(Position = 1, Mandatory = true)]
    public string Destination { get; set; }
    protected bool IsCopy { get; set; }
    #endregion
}

As you can see, the class derives from SPCmdlet and implements two overrides, InternalValidate and InternalProcessRecord. If this Cmdlet were implementing a Set or Get function, we would override the RetrieveDataObject or UpdateDataObject, respectively. However, since this Cmdlet is not for CRUD type operations, we only need to implement these two methods. You can also see the SPCmdletAttribute being applied to this class. The three parameters available for this attribute are really self-explanatory, RequireLocalFarmExists, RequireUserFarmAdmin and RequireUserMachineAdmin.

To expose the Cmdlet to PowerShell, an XML file must be created and placed in the 14\CONFIG\POWERSHELL\Registration folder. Using the SharePoint project in VS2010. adding this folder to the project is easy. Right-click on the project in the Solution Explorer and select Add -> SharePoint Mapped Folder. Navigate to the proper folder and click OK.

Image3.PNG

Once the folder has been created, add an XML file. Although you can name it anything you'd like, the convention should be to name it for the project. This XML file is used by SharePoint PowerShell to load any commands into the environment when it starts.

<ps:Config xmlns:ps="urn:Microsoft.SharePoint.PowerShell"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="urn:Microsoft.SharePoint.PowerShell  SPCmdletSchema.xsd">
  <ps:Assembly Name="$SharePoint.Project.AssemblyFullName$">
    <ps:Cmdlet>
      <ps:VerbName>Copy-SPWeb</ps:VerbName>
      <ps:ClassName>MANSoftDev.SharePoint.PowerShell.SPCopyCmd</ps:ClassName>
      <ps:HelpFile>MANSoftDev.SharePoint.PowerShell.dll-help.xml</ps:HelpFile>
    </ps:Cmdlet>
    <ps:Cmdlet>
      <ps:VerbName>Move-SPWeb</ps:VerbName>
      <ps:ClassName>MANSoftDev.SharePoint.PowerShell.SPMoveCmd</ps:ClassName>
      <ps:HelpFile>MANSoftDev.SharePoint.PowerShell.dll-help.xml</ps:HelpFile>
    </ps:Cmdlet>
  </ps:Assembly> 
</ps:Config>

The first thing necessary is to set the Name attribute of the Assembly element to the four part name of the assembly you are building. $SharePoint.Project.AssemblyFullName$ will automatically include this at compile time. Next, you need to add a Cmdlet element for each Cmdlet contained in the assembly. The VerbName element tells PowerShell what Verb-Name to associate with the Cmdlet, and the ClassName element contains the fully qualified name of the class that implements it. The HelpFile element is required; however, the file specified does not actually have to exist. For convention sake, you can use the name of the assembly, which will make it easier if you decide to provide a help file. This article will not cover implementing help files.

Implementing the Cmdlet

Since the assembly contains both the copy and move commands, PowerShell needs a way to distinguish which class is used for which command. That's where the CmdletAttribute comes in. The two required parameters are used to designate the verb and noun, respectively, that are associated with the Cmdlet. The VerbsCommon enumeration contains values for commonly used verbs, such as Set, Get, Find, and many more. If one of these does not fit your needs, any string can be supplied.

[Cmdlet(VerbsCommon.Copy, "SPWeb")]
public class SPCopyCmd : SPCopyBase
{
    public SPCopyCmd()
    {
        base.IsCopy = true;
    }
}
[Cmdlet(VerbsCommon.Move, "SPWeb")]
public class SPMoveCmd : SPCopyBase
{
    public SPMoveCmd()
    {
        base.IsCopy = false;
    }
}

All of the work for copying or moving a SPWeb was completed in the previous article, so implementation of the Cmdlet is very easy. For simplicity, I'll just copy the SPWeb class from the previous project to this project. Please refer to the previous article for details of this implementation.

protected override void InternalProcessRecord()
{
    MANSoftDev.SharePoint.PowerShell.SPWeb copy = 
                new MANSoftDev.SharePoint.PowerShell.SPWeb();
    copy.SourceURL = Source;
    copy.DestinationURL = Destination;
    if(IsCopy)
        copy.Copy();
    else
        copy.Move();
}

After everything is compiled, all that is left to do is deploy the files to the server. In the Solution Explorer, right-click the project and select Deploy. This will copy the XML file to the Registration folder and install the assembly in the GAC. When you open the SharePoint 2010 Management Shell, it will load all Cmdlets using the XML files in the PowerShell\Registration folder.

Of course, this article is not a complete treatise on Cmdlet development, so there are many more aspects that could be implemented, but for now, it covers the topic.

Application Page

Although creating a ISPStsadminCommand or SPCmdlet to expose the functionality for copying or moving a web is relatively easy, they are not very user friendly or accessible. To use them, we must be on the SharePoint machine itself. To expose the functionality in a more user friendly manner, an application page can be created and made accessible via the Site Action menu or the Site Settings page.

For those who have worked with SharePoint 2007, you will be very pleased with the tools available in Visual Studio 2010 for creating and deploying solution packages. I'll touch on some basics, but this article is not intended to cover the Visual Studio 2010 SharePoint tools.

To create the Application Page and add it to the necessary menus, I'll start once again by creating an Empty SharePoint Project. After it has been created, right-click on the project in the Solution Explorer and select Add -> SharePoint "Layouts" Mapped Folder. As with SharePoint 2007, the best practice is to create a folder under layouts to contain your custom implementations. Now, right-click on this folder and select Add -> New Item.

Image4.PNG

After selecting Application Page and naming it appropriately, SPCopy.aspx, in this case, click Add. The project structure will look like this now:

Image5.PNG

Now, it's just a matter of adding the appropriate controls to the ASPX and the event handlers to the code-behind.

Image6.PNG

protected void OnMove(object sender, EventArgs e)
{
    if(Destination.Text.Length != 0)
    {
        web.SourceURL = SPContext.Current.Web.Url;
        web.DestinationURL = Destination.Text;
        web.Move();
        Response.Redirect(Destination.Text);
    }    
}
protected void OnCopy(object sender, EventArgs e)
{
    if(Destination.Text.Length != 0)
    {
        web.SourceURL = SPContext.Current.Web.Url;
        web.DestinationURL = Destination.Text;
        web.Move();
        Response.Redirect(Destination.Text); 
    }
}

To get access to the page, I'll add an item to the Site Settings page and Site Actions menu. This is accomplished by adding an Element to the project. Once again, Visual Studio 2010 makes this very easy. Right-click the project in the Solution Explorer and select Add -> New Item. Select Empty Element from the Add New item dialog. Once it has been added to the project, add the XML as below. Although the Site Actions group would be the logical place for this item, there does not appear to be a GroupId for it, so I'll add it to the Site Administration group.

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint">
  <CustomAction Id="SPCopy"
                 RequireSiteAdministrator="True"
                 Location="Microsoft.SharePoint.SiteSettings"
                 GroupId="SiteAdministration"
                 Title="Copy/Move Web"
                 Description="Copy or move web"
                 Sequence="1001">
    <UrlAction Url="_layouts/mansoftdev/spcopy.aspx"/>
  </CustomAction>\>

Once again, this article isn't about SharePoint CustomActions, Elements, or Features, so I won't delve into the details. I have provided some links in the Rreferences section.

Points of interest

The improvements to Visual Studio 2010 in relation to SharePoint development are amazing and certainly makes development for this platform much easier and even fun.

IStsadmCommand references

SPCmdlet references

CustomAction references

History

  • 4/27/10: Initial release.

License

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

Share

About the Author


Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web01 | 2.8.141015.1 | Last Updated 5 May 2010
Article Copyright 2010 by Not Active
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid