Example 2 (Advanced)

Integrate a static data type that references another static data type into the CMS Console

In this advanced example, we create two IData types, one of which (IMovie) will reference the other (IGenre). The IGenre interface will have two properties: Id (GUID) and Name (string). The IMovie will have these properties: Id (GUID), Genre (GUID), Title (String), Description (String), Country (String).

The IMovie data item form will keep data on two tabs. The "Main" tab will have all fields except the "Description". The "Description" tab will be presented as a full-fledged XHTML editor on its own tab. The "Genre" field will be populated with items of the IGenre type. The "Country" field will be populated with a set of values via built-in CMS Functions.

In the console, the data items will appear grouped by genre.

Note: You should create a class library for your IData types (with the references to Composite.dll and any other required ones), build it and place the DLL in /Bin on your website.

Static Data Types

  1. Create a class library project (for example, "Movies")
  2. In the project, create an IGenre interface that inherits from IData, add two properties - Id, Name - and add required attributes to both the IGenre interface and the properties.
  3. Also create an IMovie interface that inherits from IData, add five properties - Id, Title, Genre, Description, Country - and add required attributes to both the IGenre interface and the properties.

    IGenre.cs:

    using System;
    
    using Composite.Data;
    using Composite.Data.Hierarchy;
    using Composite.Data.Hierarchy.DataAncestorProviders;
    
    namespace Demo
    {
        [AutoUpdateble]
        [DataAncestorProvider(typeof(NoAncestorDataAncestorProvider))]
        [KeyPropertyName("Id")]
        [LabelPropertyName("Name")]
        [DataScope(DataScopeIdentifier.PublicName)]
        [ImmutableTypeId("{92C1409F-0480-4970-9B3C-1290B00F49EB}")]
        public interface IGenre : IData
        {
            [StoreFieldType(PhysicalStoreFieldType.Guid)]
            [ImmutableFieldId("{2BE405E5-DB52-4FC2-A6AD-790C837FCA40}")]
            Guid Id { get; set; }
    
            [StoreFieldType(PhysicalStoreFieldType.String, 32)]
            [ImmutableFieldId("{55F69B8F-7F53-4BF7-BDB0-34B7AA6C3187}")]
            string Name { get; set; }
        }
    
    }
    
    Download IGenre.cs

    IMovie.cs:

    using System;
    
    using Composite.Data;
    using Composite.Data.Hierarchy;
    using Composite.Data.Hierarchy.DataAncestorProviders;
    using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
    using Composite.Data.Validation.Validators;
    
    namespace Demo
    {
        [AutoUpdateble]
        [DataAncestorProvider(typeof(NoAncestorDataAncestorProvider))]
        [KeyPropertyName("Id")]
        [LabelPropertyName("Title")]
        [DataScope(DataScopeIdentifier.PublicName)]
        [ImmutableTypeId("{B51EE692-8360-4F6A-B0B9-E16A7E74EEBE}")]
        public interface IMovie : IData
        {
            [StoreFieldType(PhysicalStoreFieldType.Guid)]
            [ImmutableFieldId("{F3E6F73F-B2A4-48FA-9D7B-D0D2A578C2EB}")]
            Guid Id { get; set; }
    
            [NotNullValidator()]
            [StringSizeValidator(1, 64)]
            [StoreFieldType(PhysicalStoreFieldType.String, 64)]
            [ImmutableFieldId("{31F38EC0-EA21-4E7F-82E8-6BAD22AADBA6}")]
            string Title { get; set; }
    
            [ForeignKey(typeof(Demo.IGenre), "Id", AllowCascadeDeletes = true)]
            [StoreFieldType(PhysicalStoreFieldType.Guid)]
            [ImmutableFieldId("{5F1A6735-6BC1-435E-8E91-1D3ACE690697}")]
            Guid Genre { get; set; }
    
            [StoreFieldType(PhysicalStoreFieldType.String, 1042)]
            [ImmutableFieldId("{D51790D7-1B04-4B0E-B19F-9F23E73DE645}")]
            string Description { get; set; }
    
            [StoreFieldType(PhysicalStoreFieldType.String, 32)]
            [ImmutableFieldId("{3D480D76-CD0E-473E-BABF-9B8CCFBC4455}")]
            string Country { get; set; }
        }
    
    }
    

    Download IMovie.cs

    Please note that by using the corresponding attributes on the IMovie’s properties we set:
    • the Genre property as a foreign key
    • validators on the Title property (“not null” and “size between 1 and 64”)
    For more information on creating IData types, please see Data types using C#.
  4. Build the project.
  5. Copy the DLL ("Movies.dll") to /Bin of your website.

Dynamic Forms

In /App_Data/Composite/DynamicTypeForms, create form definitions for items of the IGenre and IMovie types.

GenreForm.xml:

<cms:formdefinition xmlns:cms="http://www.composite.net/ns/management/bindingforms/1.0" xmlns="http://www.composite.net/ns/management/bindingforms/std.ui.controls.lib/1.0" xmlns:f="http://www.composite.net/ns/management/bindingforms/std.function.lib/1.0">
  <cms:bindings>
    <cms:binding name="Id" type="System.Guid" optional="true" />
    <cms:binding name="Name" type="System.String" optional="true" />

  </cms:bindings>
  <cms:layout>
    <cms:layout.label>
      <cms:read source="Name" />
    </cms:layout.label>
    <FieldGroup>
      <TextBox Label="Name" Help="The name of the genre">
        <TextBox.Text>
          <cms:bind source="Name" />
        </TextBox.Text>
      </TextBox>
    </FieldGroup>
  </cms:layout>
</cms:formdefinition>

Download GenreForm.xml

MovieForm.xml:

<cms:formdefinition xmlns:cms="http://www.composite.net/ns/management/bindingforms/1.0" xmlns="http://www.composite.net/ns/management/bindingforms/std.ui.controls.lib/1.0" xmlns:f="http://www.composite.net/ns/management/bindingforms/std.function.lib/1.0">
  <cms:bindings>
    <cms:binding name="Id" type="System.Guid" optional="true" />
    <cms:binding name="Title" type="System.String" optional="true" />
    <cms:binding name="Description" type="System.String" optional="true" />
    <cms:binding name="Country" type="System.String" optional="true" />
    <cms:binding name="Genre" type="System.Guid" optional="true" />
  </cms:bindings>
  <cms:layout>
    <cms:layout.label>
      <cms:read source="Title" />
    </cms:layout.label>
    <TabPanels>
      <PlaceHolder Label="Main">
        <FieldGroup>
          <TextBox Label="Title" Help="The title of the movie">
            <TextBox.Text>
              <cms:bind source="Title" />
            </TextBox.Text>
          </TextBox>

          <KeySelector Label="Country" Help="The country the movie was produced in" OptionsKeyField="Key" OptionsLabelField="Label" Required="true">
            <KeySelector.Selected>
              <cms:bind source="Country" />
            </KeySelector.Selected>
            <KeySelector.Options>
              <f:StaticMethodCall Type="Composite.Plugins.Functions.WidgetFunctionProviders.StandardWidgetFunctionProvider.String.SelectorWidgetFunction,Composite" Method="GetOptions" Parameters="&lt;SelectorOptionsSource KeyFieldName=&quot;&quot; LabelFieldName=&quot;&quot;&gt;&#xD;&#xA;  &lt;TreeNode&gt;&#xD;&#xA;    &lt;f:param xmlns:f=&quot;http://www.composite.net/ns/function/1.0&quot; name=&quot;Options&quot;&gt;&#xD;&#xA;      &lt;f:function xmlns:f=&quot;http://www.composite.net/ns/function/1.0&quot; name=&quot;Composite.Utils.String.Split&quot;&gt;&#xD;&#xA;        &lt;f:param name=&quot;String&quot; value=&quot;US,UK,France,Germany,Denmark,Spain,Italy&quot; /&gt;&#xD;&#xA;      &lt;/f:function&gt;&#xD;&#xA;    &lt;/f:param&gt;&#xD;&#xA;  &lt;/TreeNode&gt;&#xD;&#xA;&lt;/SelectorOptionsSource&gt;" />
            </KeySelector.Options>
          </KeySelector>
          <KeySelector Label="Genre" Help="The genre of the movie" OptionsKeyField="Key" OptionsLabelField="Label" Required="true">
            <KeySelector.Selected>
              <cms:bind source="Genre" />
            </KeySelector.Selected>
            <KeySelector.Options>
              <f:StaticMethodCall Type="&lt;t n=&quot;Composite.Plugins.Functions.WidgetFunctionProviders.StandardWidgetFunctionProvider.DataReference.DataReferenceSelectorWidgetFunction`1, Composite, Version=2.1.4197.27061, Culture=neutral, PublicKeyToken=null&quot;&gt;&#xD;&#xA;  &lt;t n=&quot;Demo.IGenre&quot; /&gt;&#xD;&#xA;&lt;/t&gt;" Method="GetOptions" Parameters="Demo.IGenre" />
            </KeySelector.Options>
          </KeySelector>
        </FieldGroup>
      </PlaceHolder>

      <XhtmlEditor Label="Description" Help="The description of the movie" ClassConfigurationName="common">
        <XhtmlEditor.Xhtml>
          <cms:bind source="Description" />
        </XhtmlEditor.Xhtml>

      </XhtmlEditor>

    </TabPanels>
  </cms:layout>
</cms:formdefinition>

Download MovieForm.xml

As you can see in "MovieForm.xml":

  • It uses the TabPanel element to create two tabs: "Main" and "Description". "Main" is introduced with the Placeholder element, while the "Description" tab is the entire XhtmlEditor.
  • Also here, functions are used for two of the properties. The widget for the "Country" field is set via the StaticMethodCall to "String.SelectorWidgetFunction" which, in turn, uses the "Composite.Utils.String.Split" function that supplies hard-coded options for the dropdown. The widget for the "Genre" field is set via "DataReference.DataReferenceSelectorWidgetFunction" that gets its values from the IGenre type.
  • Please note that starting from C1 CMS v 3.0 you can create a CMS Function that will populate the KeySelector widget with predefined values. For example, you can create an inline C# function that will return a set of values as Dictionary<string, string>.

using System.Collections.Generic;

namespace Demo
{
  public static class InlineMethodFunction
  {
    public static Dictionary<string,string> GetCountries()
    {
      Dictionary<string,string> countries = new Dictionary<string,string>();
      values.Add("US", "US");
      values.Add("UK", "UK");
      values.Add("Denmark", "Denmark");
      // some other countries
      return countries;
    }
  }
}
Download GetCountries.cs

You can then use this function for <KeySelector.Options> instead of StaticMethodCall like this:

<KeySelector Label="Country" Help="The country the movie was produced in" OptionsKeyField="Key" OptionsLabelField="Value" Required="true">
	<!-- skipped -->
	<KeySelector.Options>
		<func:function name="Demo.GetCountries" xmlns:func="http://www.composite.net/ns/function/1.0"/>
	</KeySelector.Options>
</KeySelector>

For more information on creating form definitions, please see Editing Form Markup.

Tree Definition

In /App_Data/Composite/TreeDefinitions, create an XML-based tree definition to present the items of both types and enable the user to manage them.
<ElementStructure xmlns="http://www.composite.net/ns/management/trees/treemarkup/1.0" xmlns:f="http://www.composite.net/ns/function/1.0">

  <ElementStructure.AutoAttachments>
    <NamedParent Name="Data" Position="Top"/>
  </ElementStructure.AutoAttachments>

  <ElementRoot>
    <Children>
      <Element Label="Movies" Id="MoviesRoot">
        <Children>
          <DataElements Type="Demo.IGenre, Movies" Label="${C1:Data:Demo.IGenre:Name}" Icon="folder-open">
            <Actions>
              <EditDataAction Label="Edit Genre" CustomFormMarkupPath="~/App_Data/Composite/DynamicTypeForms/GenreForm.xml" />
              <DeleteDataAction Label="Delete Genre"/>
              <ConfirmAction ConfirmMessage="Are you sure you want to delete all movies of this genre?" ConfirmTitle="Delete Movies" Label="Delete Movies Here">
                <f:function name="Demo.DeleteMovies">
                  <f:param name="Guid" value="${C1:Data:Demo.IGenre:Id}"/>
                </f:function>
              </ConfirmAction>
              <AddDataAction Label="Add Movie" Type="Demo.IMovie" CustomFormMarkupPath="~/App_Data/Composite/DynamicTypeForms/MovieForm.xml"/>
            
            </Actions>
            <Children>
              <DataElements Type="Demo.IMovie, Movies" Label="${C1:Data:Demo.IMovie:Title}" Icon="page-publication">
                <Actions>
                  <EditDataAction Label="Edit Movie" CustomFormMarkupPath="~/App_Data/Composite/DynamicTypeForms/MovieForm.xml" />
                  <DeleteDataAction Label="Delete Movie"/>
                  <ReportFunctionAction Label="Show As Cover">
                    <f:function name="Demo.ShowFormatted">
                      <f:param name="Guid" value="${C1:Data:Demo.IMovie:Id}"/>
                    </f:function>
                  </ReportFunctionAction>
                </Actions>
                <Filters>
                  <ParentIdFilter ParentType="Demo.IGenre, Movies" ReferenceFieldName="Genre"/>
                </Filters>
              </DataElements>

            </Children>
          </DataElements>
        </Children>
        <Actions>
          <AddDataAction Label="Add Genre" Type="Demo.IGenre" CustomFormMarkupPath="~/App_Data/Composite/DynamicTypeForms/GenreForm.xml"/>
        </Actions>
      </Element>

    </Children>

  </ElementRoot>
</ElementStructure>

Download MoviesView.xml

For more information on creating tree definitions, please see Guide to Applications.

In the above sample tree definition, the IMovie data items are grouped by IGenre with the ParentIdFilter. The user can add, edit and delete both IMovie and IGenre data items.

Besides, two more actions are added:

  • With the ReportAction, the fictitious CMS Function “Demo.ShowFormatted” is called on an IMovie item. The function takes in one parameter, which is the IMovie data item GUID passed as a dynamic field value. (Read more on the ReportAction.)
  • With the ConfirmAction, the fictitious CMS Function “Demo.DeleteMovies” will be called with a confirmation message (“Are you sure you want to delete..?”) on an IGenre item. The IGenre data item GUID is passed as the function’s parameter. (Read more on the ConfirmAction .)

Note: This tree definition file is provided for reference. If you place this tree definition in the TreeDefintions folder, the tree will not appear in the Console because the "Demo.ShowFormatted" and "Demo.DeleteMovies" functions do not exist on your website. You can create stub functions with the same names, replace these functions with your own "actual" functions, or comment out those two actions (ReportAction and ConfirmAction) – to get the tree definition working.