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
- Create a class library project (for example, "Movies")
- In the project, create an
IGenre
interface that inherits fromIData
, add two properties -Id
,Name
- and add required attributes to both theIGenre
interface and the properties. - Also create an
IMovie
interface that inherits fromIData
, add five properties -Id
,Title
,Genre
,Description
,Country
- and add required attributes to both theIGenre
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 theIMovie
’s properties we set:- the
Genre
property as a foreign key - validators on the Title property (“not null” and “size between 1 and 64”)
IData
types, please see Data types using C#. - the
- Build the project.
- 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>
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="<SelectorOptionsSource KeyFieldName="" LabelFieldName="">
 <TreeNode>
 <f:param xmlns:f="http://www.composite.net/ns/function/1.0" name="Options">
 <f:function xmlns:f="http://www.composite.net/ns/function/1.0" name="Composite.Utils.String.Split">
 <f:param name="String" value="US,UK,France,Germany,Denmark,Spain,Italy" />
 </f:function>
 </f:param>
 </TreeNode>
</SelectorOptionsSource>" /> </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="<t n="Composite.Plugins.Functions.WidgetFunctionProviders.StandardWidgetFunctionProvider.DataReference.DataReferenceSelectorWidgetFunction`1, Composite, Version=2.1.4197.27061, Culture=neutral, PublicKeyToken=null">
 <t n="Demo.IGenre" />
</t>" 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>As you can see in "MovieForm.xml":
- It uses the
TabPanel
element to create two tabs: "Main" and "Description". "Main" is introduced with thePlaceholder
element, while the "Description" tab is the entireXhtmlEditor
. - 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 asDictionary<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>
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 anIGenre
item. TheIGenre
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.