Subscribe


Tags

  • Commerce Server Documentation (0),
  • Architectural and Design (0),
  • Marketing System (0),
  • Orders System (0),
  • Catalog System (0),
  • CS2007 BizTalk Adapters (0),
  • Partner SDK (0),
  • Commerce Server Operations (0),
  • Profile System (0),
  • Migration (0),
  • Commerce Server Staging (0),
  • DWA and Reporting (0),
  • Deployment (0),

March 2010 (2)
February 2010 (9)
July 2010 (8)
August 2010 (8)
June 2010 (1)

Posted Date Published 8/22/2009 2:04:00 PM

Hacking Commerce Server Staging

Way back when I was a program manger on the Commerce Server Product Group one of my features was Commerce Server Staging (CSS). While going over CSS with the dev team and PMs I wanted to make CSS extensible so customers could create their own CSS projects. But as is with everything else this decision was met with “We don’t have time and resources” and it was dropped.

The dev manager thought it was good idea and stated that he would keep the APIs public that way in the next version of Commerce Server they could revisit the subject. Here we are with the new version and no extensibility. Now as you know the CS product has gone through much turn over and that dev manager is with a different team with MS.

In this post I am going to show you how you could hack CSS and build your own custom projects.

CSS UI Basics

CSS UI is only visible using the MMC console. CSS is a windows service but there are APIs that talk to the service. The API support COM as well as .NET (COM wrappers) that allow you to pretty much do everything that you can using MMC.  Having COM means you can write VBScript code for automation (note the Java Script is not supported due to Java script not supporting parameter passing values by ref).There is also a command line tool and once again it almost does everything MMC can.

But this section is about the UI so lets talk about that. The Business Data UIs of CSS is written in .NET. So there is a whole framework that handles this for you. Which means the MMC loads .NET controls when you open a project or create one using the wizard. How does CSS know which tabs to load on the project properties? CSS when installed creates a folder in the Root of Commerce Sever “%COMMERCE_SERVER_ROOT%” called staging. In the staging folder there a sub folder called bin. In the bin folder there is an xml file BusinessDataTypes.xml that holds the types of business data that CSS support.

If you look into the content of the BusinessDataTypes.xml file (see table below) notice that you have Handlers and UIProgIDs. The UIProgIDs are used to load controls onto project tabs. The Handlers are used to export and import data when the project is executed.

<?xml version="1.0" ?>
<BusinessDataTypes>
  <BusinessDataType Name ="Orders"  HandlerAssemblyQualifiedName = "Microsoft.CommerceServer.Staging.OrdersHandler, Microsoft.CommerceServer.Staging.BusinessDataStagingFramework, Version=6.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"  UIProgID = "Microsoft.CommerceServer.Staging.UI.OrdersConfigurationPropertyPageControl" />
  <BusinessDataType Name ="SiteTerms"  HandlerAssemblyQualifiedName = "Microsoft.CommerceServer.Staging.SiteTermHandler, Microsoft.CommerceServer.Staging.BusinessDataStagingFramework, Version=6.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"  UIProgID = "Microsoft.CommerceServer.Staging.UI.SiteTermsPropertyPageControl" />
  <BusinessDataType Name ="Marketing"  HandlerAssemblyQualifiedName = "Microsoft.CommerceServer.Staging.MarketingHandler, Microsoft.CommerceServer.Staging.BusinessDataStagingFramework, Version=6.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"  UIProgID = "Microsoft.CommerceServer.Staging.UI.MarketingPropertyPageControl" />
  <BusinessDataType Name ="Catalog" HandlerAssemblyQualifiedName = "Microsoft.CommerceServer.Staging.CatalogHandler, Microsoft.CommerceServer.Staging.BusinessDataStagingFramework, Version=6.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" UIProgID = "Microsoft.CommerceServer.Staging.UI.CatalogControl" />
</BusinessDataTypes>

So can you create your own UI? Sure why not. I will show you a quick sample which I will outline in this post.

Create CSS Custom UI

  1. The first thing you want to do is create a project in Visual Studio and select Class Library.

  2. Step two add references by implementing the IBusinessDataUserInterface which exists in the “C:\Program Files\Microsoft Commerce Server 2007\Assemblies\CSStagingUserInterfaces.dll” assembly if commerce server was installed in it’s default location. Please note that this is also GACed. The next reference is Microsoft.CommerceServer.Staging.BusinessDataStagingFramework.dll also located in the same directory and GACed.

  3. Now that you have the two references you will need to add a Control to your project and have it inherit IBusinessDataUserInterface and implement all the interface. Your class should look like the image below:
    ClassImage 
    Notice the class attribute make sure that you have the same with your own unique Guid.

  4. Before we can actually write code to our UI we need to add registration code:


    #region Component Registration

    [ComRegisterFunction]
    private static void ComRegister(Type t)
    {
        string name = @"CLSID\" + t.GUID.ToString("B");
        using (RegistryKey key = Registry.ClassesRoot.OpenSubKey(name, true))
        {
            key.CreateSubKey("Control").Close();
            using (RegistryKey key2 = key.CreateSubKey("MiscStatus"))
            {
                key2.SetValue("", "131457");
            }
            using (RegistryKey key3 = key.CreateSubKey("TypeLib"))
            {
                key3.SetValue("", Marshal.GetTypeLibGuidForAssembly(t.Assembly).ToString("B"));
            }
            using (RegistryKey key4 = key.CreateSubKey("Version"))
            {
                Version version = t.Assembly.GetName().Version;
                string str2 = string.Format("{0}.{1}", version.Major, version.Minor);
                if (str2 == "0.0")
                {
                    str2 = "1.0";
                }
                key4.SetValue("", str2);
            }
        }
    }

    [ComUnregisterFunction]
    private static void ComUnregister(Type t)
    {
        string subkey = @"CLSID\" + t.GUID.ToString("B");
        Registry.ClassesRoot.DeleteSubKeyTree(subkey);
    }

    #endregion Component Registration

  5. Ok now we are ready to populate our interface implementations. For the ModuleName replace the text to with your own custom name. The wizardMode variable is used when creating projects using the wizard. When creating a project using the wizard mode you will not see your custom control as the UI for the process is hard coded to only display the four Commerce Sever Business Types. But you can view your control using non wizard mode. The modeReadOnly is used to distinguish between Operator and Administrator. Remember an Operator can not modify project properties.
    Private variables:


    internal const string ModuleName = "Max's Test";
    private BusinessDataCollection businessDataCollection;
    private bool modeReadOnly;
    private bool wizardMode;
    private BusinessData businessData;


    Flush Method:


    public void Flush()
    {
        for (int i = this.businessDataCollection.Count - 1; i >= 0; i--)
        {
            BusinessData businessData = this.businessDataCollection[i];
            if (!(businessData.BusinessDataTypeName != ModuleName))
            {
                this.businessDataCollection.Remove(businessData);
            }
        }
    }

    InitializeSate Method:

    public void InitializeSate(string siteName, Microsoft.CommerceServer.Staging.BusinessDataCollection collection, bool modeReadOnly)
    {
        if (siteName == null)
        {
            throw new ArgumentNullException("siteName");
        }
        if (collection == null)
        {
            throw new ArgumentNullException("collection");
        }
        this.modeReadOnly = modeReadOnly;
        this.businessDataCollection = collection;

        this.businessData = GetFirstBusinessData(collection, ModuleName);
        if (this.businessData == null)
        {
            this.businessData = new BusinessData();
            this.businessData.BusinessDataTypeName = ModuleName;
            this.businessData.BusinessDataId = Guid.NewGuid();

            BusinessDataOption opt = new BusinessDataOption("checkBox1", checkBox1.Checked.ToString());
            this.businessData.ExportOptions.Add(opt);

            BusinessDataOption opt2 = new BusinessDataOption("textBox1", textBox1.Text);
            this.businessData.ExportOptions.Add(opt2);

            this.businessDataCollection.Add(this.businessData);
        }
        else
        {
            string opt = Get(this.businessData.ExportOptions, "checkBox1");
            string opt2 = Get(this.businessData.ExportOptions, "textBox1");

            checkBox1.Checked = Convert.ToBoolean(opt);
            textBox1.Text = opt2;
        }
    }


    SaveState:

    public void SaveState()
    {
        if (!this.modeReadOnly)
        {
            // put code for read only
        }
        // We will assume administrator
        BusinessDataOption opt = GetOption(this.businessData.ExportOptions, "checkBox1");
        opt.Value = checkBox1.Checked.ToString();

        BusinessDataOption opt2 = GetOption(this.businessData.ExportOptions, "textBox1");
        opt2.Value = textBox1.Text;
    }


    WizardMode:

    public bool WizardMode
    {
        set
        {
            this.wizardMode = value;
        }
    }


    Helper Methods:

    internal static BusinessData GetFirstBusinessData(BusinessDataCollection collection, string type)
    {
        foreach (BusinessData data in collection)
        {
            if (data.BusinessDataTypeName == type)
            {
                return data;
            }
        }
        return null;
    }

    internal static BusinessDataOption GetOption(BusinessDataOptionCollection options, string optionName)
    {
        foreach (BusinessDataOption option in options)
        {
            if (option.Name == optionName)
            {
                return option;
            }
        }
        return null;
    }

    internal static string Get(BusinessDataOptionCollection options, string optionName)
    {
        if (options == null)
        {
            throw new ArgumentNullException("options");
        }
        if (optionName == null)
        {
            throw new ArgumentNullException("optionName");
        }
        foreach (BusinessDataOption option in options)
        {
            if (option.Name == null)
            {
                throw new ArgumentNullException("option");
            }
            if (option.Name.Trim().ToUpper(CultureInfo.InvariantCulture) == optionName.ToUpper(CultureInfo.InvariantCulture))
            {
                if (option.Value == null)
                {
                    throw new ArgumentNullException(optionName);
                }
                return option.Value.Trim().ToUpper(CultureInfo.InvariantCulture);
            }
        }
        return null;
    }

     

  6. Ok now that we have our code lets add a new control as we referenced a checkbox and textbox.
    ProjectImage

  7. Compile your project and make sure to sign the assembly as will add it to the GAC.

  8. Modify the BusinessDataTypes.xml file in “C:\Program Files\Microsoft Commerce Server 2007\Staging\Bin” to reflect the GACed assembly.
    xmlfile

  9. GAC the assembly and stop and restart the CSS service.

  10. Now open CSS MMC and create a project not using the wizard.
    CustomProject
    Notice that we have our control in the project properties. I have not figured out how to publish the custom control name to the tab, but all works. Add some values in your custom control tab and other project properties such as site name and destination. Select OK and save your project. Now examine the xml file that is created for your project.
    projectxml 

As you modify the project properties the settings will be saved in the project xml file. Notice that the options are ExportOption name\value. You can also have ImportOptions. In the SaveState method I added code to save the control options in ExportOptions collection you can if you have values that need to be saved for import in the ImportOptions collection.
Ok that’s it for now in the next post I will show you how to create your own handler that will get fired for your custom control.

by Max Akbar | Comments



Comments disabled.