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

Import List VS 2010 SharePoint Extension

, 27 Sep 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
A Visual Studio 2010 SharePoint 2010 extension to import existing list definition and schema to SharePoint 2010 project

Introduction

SharePoint 2010 is a very nice evolution from SharePoint 2007, and integrated with Visual Studio 2010 developer, productivity in SharePoint related projects has been greatly enhanced. Visual Studio 2010 contains a number of built-in templates and features that allow the developer to do with a few clicks of the mouse what had taken painful hand-coding to accomplish before. However, with all of the advances, there are still some areas that could use improvements.

One such area that could use refinement is the ability to import an existing list into a Site Definition project.

Given a scenario of creating a Site Definition, there may be a time when you have an existing list, possibly with data already in it, that can be imported into the project. With the methods currently available, this can become a long and tedious process. In this article, I'll explore the current methods and present an alternative using a Visual Studio SharePoint Project Extension.

Create Site Template

Visual Studio 2010 now includes a SharePoint 2010 project, Import SharePoint Solution Package, that allows you to import an existing site and all its items such as content types, site columns and lists into a Site Definition project.

The first step for this process is to create a template from an existing site via Site Actions -> Save site as template. A drawback here is that it cannot be used for publishing sites.

After clicking this link, a form is presented requesting a name for the template and whether to include content, such as existing data in a list. Once saved, the template is placed in the Solutions Gallery.

The template file can be downloaded from the Solution Gallery by clicking on the template name, in this example: MySiteDef.

Import SharePoint Solution Package

From within Visual Studio 2010, you can create a new project in the standard way and select Import SharePoint Solution Package from the New Project dialog.

After selecting the project, a series of wizard dialogs is displayed allowing you to choose the site and if the project is for a farm or sandboxed solution. The next dialog allows you to choose the location of the template file that was saved in the early step.

After clicking the Next button, Visual Studio will read the solution package specified and extracts information about the site and its contents.

As you can see, this list includes everything in the site. Although you can deselect the items you don't want to import, if it is a large site it can be a tedious process to find the items necessary. As a note, if you deselect a content type or field that a list relies on you will be warned to include it or ignore the warning. After the project has been created, you will need to have the site definition project you want to import the list into opened in the same solution. From there, you can copy the list definition folder from the imported project to the site definition project. This does not automatically copy any related objects such as fields or content types; they must be copied individually in the same manner.

As you can see, this is quite cumbersome for importing a single list definition.

Doing It By Hand

Another possibility is to do it all by hand. You can add a new List Definition item to the project and add the necessary elements to the schema.xml and elements.xml for the list. Of course, you must also do the same for any fields and content types necessary. If you are accustomed to SharePoint 2007 development this method should be familiar, and time consuming. For a small list with standard fields and no existing instance, this can be a viable option; however, the tools are available to do better.

Visual Studio Extension Package

In previous versions of Visual Studio, the way to add additional functionality was to create an Addin. Although this method is still supported, with Visual Studio 2010 the preferred method is to create a Visual Studio Extension package (VSIX) since it is built using the Managed Extensibility Framework. The first thing necessary to create an extension for SharePoint is to download and install the Visual Studio 2010 SDK. Once the SDK is installed, you will find an additional item in the New Project dialog under Other Project Types -> Extensibility.

Creating a Visual Studio Extension package can be a book in itself, so I will concentrate on discussing aspects related to our purpose, creating a Visual Studio 2010 SharePoint project extension.

Extension Manifest

After selecting this project type, you will be presented with a series of dialogs asking for information about the project, like a name and description, icon to use, what language and whether it supports menu commands. After these questions have been answered, the project will be created with several files already populated, most of them are not relevant to this discussion and are not strictly necessary to create an extension. They do have a remarkable amount of inline documentation explaining their purpose though. The one file we will concentrate on however is source.extension.vsixmanifest. This XML file contains all the information necessary to install and configure the extension in Visual Studio as well as display information about it. A custom viewer is used to display it in a nice UI.

One section of interest is the Supported VS Editions. Clicking the Select Editions button will display this dialog to allow you to specify which edition of Visual Studio the extension will be supported. For the purpose of this article, the defaults are fine.

The other section of interest to us is the Content section. This allows you to add or remove assemblies from the package.

Clicking the Add Content button will display a dialog allowing you to select the type of content and where it is located. For this article, we are adding a MEF Component from another project in the solution.

Now, let's get to the implementation.

Import List Extension Implementation

The implementation assembly is a simple class library project. However, to have any classes in this assembly recognized by the MEF and Visual Studio 2010, you need to apply the System.ComponentModel.Composition.ExportAttribute. Before you can do this, however, you need to add two references to the project, Microsoft.VisualStudio.SharePoint.dll and System.ComponentModel.Composition.dll. The latter contains the ExportAttribute and the former contains the interfaces you will need to implement.

[Export(typeof(ISharePointProjectExtension))]
public class ImportListExtention : ISharePointProjectExtension
{
    public void Initialize(ISharePointProjectService projectService)
    {
            
    }
}

As you can see from this snippet, there is only one method that needs to be implemented from the ISharePointProjectExtention interface: Initialize. In this method, you can subscribe to a number of events on the ISharePointProjectService object that is passed to it. For this purpose, I'll subscribe to the ProjectMenuItemsRequested event. This event occurs when the context menu is about to be displayed when right-clicking a SharePoint project in the Solution Explorer window. In the handler for this event, you can either add a menu item to the Actions area of the context menu, using the ActionMenuItems collection, or in this case, to the AddMenuItems collection, which will add the item to the Add submenu.

void OnProjectMenuItemsRequested
	(object sender, SharePointProjectMenuItemsRequestedEventArgs e)
{
    IMenuItem menu = e.AddMenuItems.Add("Import List");
    menu.Click += new EventHandler<menuitemeventargs />(OnMenuClick);
}

void OnMenuClick(object sender, MenuItemEventArgs e)
{
    ImportList import = new ImportList(e.Owner as ISharePointProject);
    import.Import();
}

Import List

After you click the Import List menu item, a dialog is displayed to gather more information about the list to import. In order to get the lists available from the specified site, you need use the SharePoint server object model via SharePoint Commands.

SharePoint Commands

SharePoint cannot be accessed directly from within Visual Studio, so you must create commands to execute through the ISharePointConnection implementation available from the SharePointProject object connected to the SharePoint project in Visual Studio. The purpose of the ISharePointConnection interface is to allow access to SharePoint via the 64-bit Visual Studio process, vssphost.exe.

The commands are built in a separate assembly which must be compiled against .NET Framework 3.5. This point eluded me initially and caused a few hours of head scratching. The assembly will compile and run using the .NET Framework 4.0; however, an exception will be generated when calling ExecuteCommand, seemingly indicating that the command string has not been defined.

Commands are defined by decorating a method with the SharePointCommandAttribute and supplying a name for the command. When using the ExecuteCommand method of the ISharePointConnection object, this same string must be supplied as the first argument.

 Project.SharePointConnection.ExecuteCommand<string>
	(SharePointCommands.Commands.GetLists, SiteURL); 

Some restrictions on ISharePointConnection.ExecuteCommand are that, other than the command name, only one other parameter can be passed to it and both input and return objects must be serializable. ExecuteCommand is a templated method which allows you to specify the type of the input and return value, string and List<string> respectively in the above case. We are passing a string, the URL of the site to retrieve lists from and returning with a collection of strings representing the list names.

[SharePointCommand("MANSoftDev.Commands.GetLists")]
public static List<string> GetLists(ISharePointCommandContext context, string siteUrl)
{
    List<string> lists = new List<string>();
    using(SPSite site = new SPSite(siteUrl))
    {
        using(SPWeb web = site.OpenWeb())
        {
            foreach(SPList item in web.Lists)
            {
                if(item.Hidden == false)
                {
                    lists.Add(item.Title);
                }
            }
        }
    }

    return lists;
}

The implementation of the SharePointCommand method receives an ISharePointCommandContext object from the ISharePointConnection instance that is making the call. This object provides access to the SharePoint context the command will be executed against and contains properties for the Site and Web as well as a Logger allowing messages to be written to the output window in Visual Studio.

The implementation of the GetLists command is very straight forward. We create a SPSite from the URL that is passed in and iterate through the visible SPLists in the Web, then return a collection of the names of each list. Creating the files for the list definition and instance becomes more complicated though.

Create List Definition and Instance

To get information necessary to construct the files, you must supply the site URL and list name. However, since only one parameter can be passed to the ExecuteCommand method, you need to use a wrapper object.

[Serializable]
public struct GetListData
{
    public string SiteUrl { get; set; }
    public string ListName { get; set; }
    public bool IncludeContent { get; set; }
}

project.SharePointConnection.ExecuteCommand<sharepointcommands.getlistdata>
                (SharePointCommands.Commands.GetListData, data);

Although it would be nice to return a SPList from a SharePointCommand, it is not serializable. This means everything necessary to create the definition and instance files must be created in the command implementation method. The good thing is most of the information is readily available from the SPList object.

Schema, List Definition and List Instance Files

The list schema file describes the list's content type, fields, view and more. The basic structure of this file is as follows:

<List>
    <MetaData>
        <ContentTypes />
        <Fields />
        <Views>
            <View>
                <Query />
            </View>
        </Views>
    </MetaData>
</List>

The View and Fields elements are easy to obtain from the SPList.

string views = list.Views.SchemaXml;
string fields = list.Fields.SchemaXml;

The ContentType needs just a bit more to extract the required XML without any extra bits:

string contentTypes = "<contenttypes>";

foreach(SPContentType item in list.ContentTypes)
{
    contentTypes += item.Parent.SchemaXmlWithResourceTokens;
}
contentTypes += "</contenttypes>";

From here, it's just a matter of constructing the XML file:

XElement schema = new XElement(ns + "List",
    new XAttribute("Title", title),
    new XAttribute("Direction", "none"),
    new XAttribute("Url", "Lists/" + url),
    new XAttribute("BaseType", baseType),
    new XAttribute("Type", templateType),
    new XAttribute("BrowserFileHandling", "Permissive"),
    new XAttribute("FolderCreation", "FALSE"),
    new XAttribute("Catalog", "FALSE"),
    new XAttribute("SendToLocation", "|"),
    new XAttribute("ImageUrl", "/_layouts/images/itgen.png"),
    new XAttribute(XNamespace.Xmlns + "ows", "Microsoft SharePoint"),
    new XAttribute(XNamespace.Xmlns + "spctf", 
	"http://schemas.microsoft.com/sharepoint/v3/contenttype/forms"),

    new XElement("MetaData",
        contentTypesXML,
        fieldsXML,
        CreateFormsElement(),
        CleanViews(viewsXML)
        )
    );

and cleaning up the View element slightly:

private static XElement CleanViews(XElement views)
{
    views.Elements("View").Attributes("Name").Remove();

    foreach(XElement view in views.Elements("View"))
    {
        // Need to remove pathing info from this attribute
        view.SetAttributeValue("Url", "AllItems.aspx");
        // Although these attributes are optional, they must be
        // set for Visual Studio to deploy
        view.SetAttributeValue("SetupPath", @"pages\viewpage.aspx");
        view.SetAttributeValue("WebPartZoneID", "Main");
    }

    return views;
}

The list definition and instance are very easy to create:

private static XElement CreateListTemplate
  (string name, int templateType, int baseType, string displayName, string description)
{
    XNamespace ns = "http://schemas.microsoft.com/sharepoint/";

    XElement xml = new XElement(ns + "Elements",
        new XElement("ListTemplate",
            new XAttribute("Name", name), // Name of project item also
            new XAttribute("Type", templateType),  // Matches TemplateType 
                                                   // in ListInstance
            new XAttribute("BaseType", baseType),
            new XAttribute("OnQuickLaunch", "TRUE"),
            new XAttribute("SecurityBits", "11"),
            new XAttribute("Sequence", "410"),
            new XAttribute("DisplayName", displayName),
            new XAttribute("Description", description),
            new XAttribute("Image", "/_layouts/images/itgen.png"))
        );

    // .NET wants to add an empty xmlns element
    // to the child elements when a namespace is 
    // added to the parent. They only way to remove it
    // is from the string representation of the element
    return XElement.Parse(xml.ToString().Replace("xmlns=\"\"", ""));
}

private static XElement CreateListInstance
    (string title, int templateType, string url, string description)
{
    XNamespace ns = "http://schemas.microsoft.com/sharepoint/";

    XElement xml = new XElement(ns + "Elements",
        new XElement("ListInstance",
            new XAttribute("Title", title + " Instance"),
            new XAttribute("OnQuickLaunch", "TRUE"),
            new XAttribute("TemplateType", templateType), // Matches type 
                                                          // in ListTemplate
            new XAttribute("Url", "Lists/" + url),
            new XAttribute("Description", description))
        );

    // .NET wants to add an empty xmlns element
    // to the child elements when a namespace is 
    // added to the parent. They only way to remove it
    // is from the string representation of the element
    return XElement.Parse(xml.ToString().Replace("xmlns=\"\"", ""));
}

Again, since the SPList object is not serializable, you need to use a wrapper object to return this information back to the caller.

[Serializable]
public struct ListData
{
    public string ListName { get; set; }
    public XElement Schema { get; set; }
    public XElement ListInstance { get; set; }
    public XElement ListTemplate { get; set; }

    public bool IsError { get; set; }
    public string ErrorMessage { get; set; }
}

Adding SharePointCommand to Package

Now that the SharePoint command assembly has been constructed, it needs to be added to the Visual Studio Extension package. Just as you added the MEF component to the package, the SharePointCommand needs to be added so it will be deployed. As you can see, the content type for this assembly is Custom Extension Type and the type is set to SharePoint.Commands.v4. This key for the assembly to be recognized as containing commands and be accessible.

Adding to Visual Studio

Now that all of the necessary files have been created, you need to add everything to the Visual Studio project. This is relatively straight forward using the ISharePointProject reference that represents the current project in Visual Studio. Just like a non-SharePoint project, a collection of all files and folders is maintained by the project and can be referenced, in this case, using the ISharePointProjectItemsCollection. Although you could just add the files directly to project tree, this won't work for a SharePoint project. SharePoint projects use a file named SharePointProjectItem.spdata to maintain meta data about the files in a folder and how to process them. This file is not included in the Solution Explorer tree unless you select View All Files.

<ProjectItem Type="Microsoft.VisualStudio.SharePoint.ListDefinition" 
             DefaultFile="Elements.xml" 
             SupportedTrustLevels="All" 
             SupportedDeploymentScopes="Web, Site"
xmlns="http://schemas.microsoft.com/VisualStudio/2010/
SharePointTools/SharePointProjectItemModel">
  <Files>
    <ProjectItemFile Source="Elements.xml" Target="CustomList\" Type="ElementManifest" />
    <ProjectItemFile Source="Schema.xml" Target="CustomList\" Type="ElementFile" />
  </Files>
</ProjectItem>

The key elements in this file are the type attributes. For the ProjectItem element, it is set to Microsoft.VisualStudio.SharePoint.ListDefinition telling Visual Studio what type of items are included in the folder. For the ProjectItemFiles elements, it is set to ElementManifest and ElementFile respectively, indicating to Visual Studio how to process the files when deploying the solution.

Using the ISharePointProjectItemsCollection we first add the folder, specifying the type as Microsoft.VisualStudio.SharePoint.ListDefinition, to the collection which returns a ISharePointProjectItem object. Since the project items are physical files within the project tree, you must write the XML that was returned from the SharePointCommand call to file before it can be added to the project.

ISharePointProjectItem item = project.ProjectItems.Add(listData.ListName, LIST, false);
                    
string folder = Path.GetDirectoryName(item.FullPath);

// Now to add the files for list definition and schema
string fileName = Path.Combine(folder, "Schema.xml");
WriteFile(fileName, listData.Schema);
ISharePointProjectItemFile file = item.Files.AddFromFile(fileName);
// Must set type to update spdata file
file.DeploymentType = DeploymentType.ElementFile;

private void WriteFile(string fileName, XElement element)
{
    XmlWriterSettings xws = new XmlWriterSettings();
    xws.OmitXmlDeclaration = false;
    xws.Indent = true;

    using(XmlWriter writer = XmlWriter.Create(fileName, xws))
    {
        element.WriteTo(writer);  
    }
}

Completion

All of the files have now been added to the SharePoint project with the appropriate metadata set so that Visual Studio will know how to process each during deployment.

Obviously there are settings that may need to be adjusted manually; however, you must admit it is much easier to make these adjustments rather than to create all the files manually.

Room for Improvement

This solution could be made better by adding any custom site columns and/or content types to the project when the list is being imported. This is a more complicated procedure that requires checking each site column against the builtin sites columns to determine if it needs to be added, then checking to ensure it has not already been included in the project. The same applies for content types. With the information that has been provided, you should be able to understand the steps necessary to add this functionality.

Resources

History

  • 9/24/10: Initial upload

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

 
QuestionDon't see the project type after plugin installation Pinmembersenglory6-Mar-13 1:02 
GeneralMy vote of 5 PinmemberMohamed Sadek Rady5-Feb-12 7:25 
QuestionI really like this as a solution PinmemberDavidJacobus8-Oct-11 14:58 
AnswerRe: I really like this as a solution PinmvpMark Nischalke8-Oct-11 17:24 
QuestionList Definitions? Pinmemberkilkfoe13-Jun-11 4:26 
Hi, thanks for the article. I cannot see the list definitions anywhere when I import everything in to the Visual Studio project. I see the list instances, but I am trying to grab a copy of the list definition to make some customizations. Am I missing something, am I blind, are they actually not available?
 
Thanks
AnswerRe: List Definitions? Pinmemberkilkfoe13-Jun-11 4:40 
GeneralRe: List Definitions? PinmvpMark Nischalke3-Jun-11 5:58 
GeneralMy vote of 5 PinmemberMahmoud Fayed24-Sep-10 20:35 
GeneralRe: My vote of 1 PinmvpMark Nischalke24-Sep-10 18:54 
GeneralRe: My vote of 1 PinmemberMahmoud Fayed25-Sep-10 12:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.1411022.1 | Last Updated 27 Sep 2010
Article Copyright 2010 by Not Active
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid