Skip to main content
Version: 8.2

Implement a custom campaign element

Level: advanced
note

The functionality is relevant to Classic UI.

To implement the example:

  1. Create a marketing campaign element. Read more >>>
  2. Create a property panel for the campaign element. Read more >>>
  3. Add the element to the element area of the Campaign Designer. Read more >>>
  4. Create an executable element for the campaign element. Read more >>>
  5. Implement the back-end part of the campaign element. Read more >>>
Example

Create a custom Test SMS element of a marketing campaign to send SMS to specific users. Add the custom element to the Campaign Designer. Use the icon provided below.

1. Create a marketing campaign element

  1. Create a package to implement the example. Instructions: Create a user-made package.

    For this example, create the sdkCreateCampaignElementPackage package.

  2. Create a client module schema. Instructions: Implement a non-visual module.

    For this example, use the client module properties as follows.

    Property

    Property value

    Code

    UsrTestSmsElementSchema

    Title

    SMS element schema

  3. Add a localizable string that contains the campaign element name. Instructions: Add a localizable string.

    For this example, add a localizable string that contains the Test SMS campaign element name to the UsrTestSmsElementSchema client module schema. Use the localizable string properties as follows.

    Property

    Property value

    Code

    SmsElementCaption

    Value

    Test SMS

  4. Add icons that represent the campaign element in different Campaign Designer modes.

    For this example, use an image available via the link.

    1. Click in the context menu of the Images node.

    2. Fill out the icon properties.

      Property

      Property value

      Code

      TitleImage

      Image

      Select the image

    3. Upload the icon.

    4. Repeat step 1-3 for SmallImage and LargeImage resources.

  5. Add source code that implements the appearance of the campaign element to the schema. To do this, link the uploaded image to the titleImage, largeImage and smallImage properties.

    UsrTestSmsElementSchema
    define("UsrTestSmsElementSchema", ["UsrTestSmsElementSchemaResources", "CampaignBaseCommunicationSchema"], function(resources) {
    Ext.define("Terrasoft.manager.UsrTestSmsElementSchema", {
    /* Parent schema. */
    extend: "Terrasoft.CampaignBaseCommunicationSchema",
    alternateClassName: "Terrasoft.UsrTestSmsElementSchema",
    /* Manager Id. Must be unique. */
    managerItemUId: "a1226f93-f3e3-4baa-89a6-11f2a9ab2d71",
    /* Plugged mixins. */
    mixins: {
    campaignElementMixin: "Terrasoft.CampaignElementMixin"
    },
    /* Element name. */
    name: "TestSms",
    /* Resource binding. */
    caption: resources.localizableStrings.SmsElementCaption,
    titleImage: resources.localizableImages.TitleImage,
    largeImage: resources.localizableImages.LargeImage,
    smallImage: resources.localizableImages.SmallImage,
    /* Schema name of the edit page. */
    editPageSchemaName: "TestSmsElementPropertiesPage",
    /* Element type. */
    elementType: "TestSms",
    /* Full name of the class that corresponds to the current schema.
    Use following typeName if the class is in the simple package. */
    typeName: "Terrasoft.Configuration.UsrTestSmsElement, Terrasoft.Configuration",
    /* If the class is in the assembly package use
    typeName: "Terrasoft.Configuration.UsrTestSmsElement, SomeAssemblyPackageName" */
    /* Overriding the properties of visual styles. */
    color: "rgba(249, 160, 27, 1)",
    width: 69,
    height: 55,
    /* Setting up element-specific properties. */
    smsText: null,
    phoneNumber: null,
    /* Determining the types of the element's outbound connections. */
    getConnectionUserHandles: function() {
    return ["CampaignSequenceFlow", "CampaignConditionalSequenceFlow"];
    },
    /* Expanding the properties for serialization. */
    getSerializableProperties: function() {
    var baseSerializableProperties = this.callParent(arguments);
    return Ext.Array.push(baseSerializableProperties, ["smsText", "phoneNumber"]);
    },
    /* Setting up the icons that are displayed on the campaign diagram. */
    getSmallImage: function() {
    return this.mixins.campaignElementMixin.getImage(this.smallImage);
    },
    getLargeImage: function() {
    return this.mixins.campaignElementMixin.getImage(this.largeImage);
    },
    getTitleImage: function() {
    return this.mixins.campaignElementMixin.getImage(this.titleImage);
    }
    });
    return Terrasoft.UsrTestSmsElementSchema;
    });

    Where:

    • managerItemUId is the unique value for the new element. It must differ from the value of the other elements.

    • typeName contains the name of the C# class that corresponds to the campaign element name. This class saves and reads the element properties from the schema properties.

      note

      If you use the class in a simple package, specify typeName as follows:: "Terrasoft.Configuration.UsrTestSmsElement, Terrasoft.Configuration". If you use the class in a assembly package, specify typeName as follows: "Terrasoft.Configuration.UsrTestSmsElement, SomeAssemblyPackageName".

  6. Save the schema.

2. Create a property panel for the campaign element

  1. Create a page view model. Instructions: View model schema.

    For this example, use page view model properties as follows.

    Property

    Property value

    Code

    UsrTestSmsElementPropertiesPage

    Title

    Properties panel of the Test SMS element

    Parent object

    BaseCampaignSchemaElementPage

  2. Add three localizable strings that include the field names. Instructions: Add a localizable string.

    Code

    Value

    PhoneNumberCaption

    Sender phone number

    SmsTextCaption

    Message

    TestSmsText

    Which SMS text to send?

  3. Set up the properties panel of the campaign element.

    Schema property

    Code

    Description

    attributes

    PhoneNumber

    Stores phone number information

    SmsText

    Stores information on the message text

    methods

    init()

    Initializes the module

    getContextHelpCode()

    Returns the code of the element to generate a link to the context help

    initParameters()

    Initializes the attributes using the current schema property values

    saveValues()

    Preserves schema properties

    getPhoneNumber()

    Reads the current value from the PhoneNumber attribute

    getSmsText()

    Reads the current value from the SmsText attribute

    diff

    ContentContainer

    Container on page

    TestSmsLabel

    Main caption for the element

    PhoneNumberLabel

    Caption of the text field to enter the sender number

    PhoneNumber

    Text box to input the phone number

    SmsTextLabel

    Caption of the text field to enter the message text

    SmsText

    Text field to enter the message text

    UsrTestSmsElementPropertiesPage
    define("UsrTestSmsElementPropertiesPage", [], function() {
    return {
    attributes: {
    /* Attributes that correspond to specific properties of element schema. */
    "PhoneNumber": {
    "dataValueType": this.Terrasoft.DataValueType.TEXT,
    "type": this.Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN
    },
    "SmsText": {
    "dataValueType": this.Terrasoft.DataValueType.TEXT,
    "type": this.Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN
    }
    },
    methods: {
    init: function() {
    this.callParent(arguments);
    this.initAcademyUrl(this.onAcademyUrlInitialized, this);
    },
    /* Element code for generating a contextual help link. */
    getContextHelpCode: function() {
    return "CampaignTestSmsElement";
    },
    /* Initialization of attributes with the current schema property values. */
    initParameters: function(element) {
    this.callParent(arguments);
    this.set("SmsText", element.smsText);
    this.set("PhoneNumber", element.phoneNumber);
    },
    /* Saving schema properties. */
    saveValues: function() {
    this.callParent(arguments);
    var element = this.get("ProcessElement");
    element.smsText = this.getSmsText();
    element.phoneNumber = this.getPhoneNumber();

    },
    /* Reading current attribute values. */
    getPhoneNumber: function() {
    var number = this.get("PhoneNumber");
    return number ? number : "";
    },
    getSmsText: function() {
    var smsText = this.get("SmsText");
    return smsText ? smsText : "";
    }
    },
    diff: [
    /* UI container. */
    {
    "operation": "insert",
    "name": "ContentContainer",
    "propertyName": "items",
    "parentName": "EditorsContainer",
    "className": "Terrasoft.GridLayoutEdit",
    "values": {
    "itemType": Terrasoft.ViewItemType.GRID_LAYOUT,
    "items": []
    }
    },
    /* Element primary signature. */
    {
    "operation": "insert",
    "name": "TestSmsLabel",
    "parentName": "ContentContainer",
    "propertyName": "items",
    "values": {
    "layout": {
    "column": 0,
    "row": 0,
    "colSpan": 24
    },
    "itemType": this.Terrasoft.ViewItemType.LABEL,
    "caption": {
    "bindTo": "Resources.Strings.TestSmsText"
    },
    "classes": {
    "labelClass": ["t-title-label-proc"]
    }
    }
    },
    /* Caption for the text field where sender name is entered. */
    {
    "operation": "insert",
    "name": "PhoneNumberLabel",
    "parentName": "ContentContainer",
    "propertyName": "items",
    "values": {
    "layout": {
    "column": 0,
    "row": 1,
    "colSpan": 24
    },
    "itemType": this.Terrasoft.ViewItemType.LABEL,
    "caption": {
    "bindTo": "Resources.Strings.PhoneNumberCaption"
    },
    "classes": {
    "labelClass": ["label-small"]
    }
    }
    },
    /* Text field for entering phone number. */
    {
    "operation": "insert",
    "name": "PhoneNumber",
    "parentName": "ContentContainer",
    "propertyName": "items",
    "values": {
    "labelConfig": {
    "visible": false
    },
    "layout": {
    "column": 0,
    "row": 2,
    "colSpan": 24
    },
    "itemType": this.Terrasoft.ViewItemType.TEXT,
    "classes": {
    "labelClass": ["feature-item-label"]
    },
    "controlConfig": { "tag": "PhoneNumber" }
    }
    },
    /* Caption for text field for entering message text. */
    {
    "operation": "insert",
    "name": "SmsTextLabel",
    "parentName": "ContentContainer",
    "propertyName": "items",
    "values": {
    "layout": {
    "column": 0,
    "row": 3,
    "colSpan": 24
    },
    "classes": {
    "labelClass": ["label-small"]
    },
    "itemType": this.Terrasoft.ViewItemType.LABEL,
    "caption": {
    "bindTo": "Resources.Strings.SmsTextCaption"
    }
    }
    },
    /* Text field for entering message text. */
    {
    "operation": "insert",
    "name": "SmsText",
    "parentName": "ContentContainer",
    "propertyName": "items",
    "values": {
    "labelConfig": {
    "visible": false
    },
    "layout": {
    "column": 0,
    "row": 4,
    "colSpan": 24
    },
    "itemType": this.Terrasoft.ViewItemType.TEXT,
    "classes": {
    "labelClass": ["feature-item-label"]
    },
    "controlConfig": { "tag": "SmsText" }
    }
    }
    ]
    };
    });
  4. Save the schema.

3. Add the element to the element area of the Campaign Designer

  1. Create a replacing view model. Instructions: Implement a replacing module.

For this example, use replacing view model schema properties as follows.

Property

Property value

Code

CampaignElementSchemaManagerEx

Title

Campaign element manager

Parent object

CampaignElementSchemaManagerEx

  1. Add the element to the element area of the Campaign Designer. To do this, add source code to the JS schema section.

    UsrTestSmsElementSchemaManagerEx
    require(["CampaignElementSchemaManager", "UsrTestSmsElementSchema"], function() {
    /* Adding a new schema to the list of available element schemas in the Campaign Designer. */
    var coreElementClassNames = Terrasoft.CampaignElementSchemaManager.coreElementClassNames;
    coreElementClassNames.push({
    itemType: "Terrasoft.UsrTestSmsElementSchema"
    });
    });
  2. Save the schema.

4. Create an executable element for the campaign element

  1. Create a source code schema. Instructions: Implement the source code.

    For this example, use source code schema properties as follows.

    Property

    Property value

    Code

    UsrTestSmsCampaignProcessElement

    Title

    Test SMS process element

  2. Implement an executable element that executes the business logic of the created element.

    1. Specify the CampaignFlowElement class as a parent class.

    2. Overload the following methods:

      Method name

      Method description

      SingleExecute()

      Enables the campaign execution engine to invoke the necessary logic for the element's behavior

      GetAudienceQuery()

      Defines the audience for the element

    UsrTestSmsCampaignProcessElement
    namespace Terrasoft.Configuration
    {

    using Terrasoft.Core.Campaign;
    using Terrasoft.Core.DB;

    public class UsrTestSmsCampaignProcessElement : CampaignFlowElement
    {
    public UsrTestSmsCampaignProcessElement() {
    }
    public string PhoneNumber {
    get;
    set;
    }
    public string SmsText {
    get;
    set;
    }
    protected override Query GetAudienceQuery() =>
    new Select(UserConnection)
    .Column("Id")
    .From(CampaignParticipantTable)
    .Where("CampaignItemId").IsEqual(Column.Parameter(CampaignItemId))
    .And("StatusId").IsEqual(Column.Parameter(CampaignConstants.CampaignParticipantParticipatingStatusId))
    .And("StepCompleted").IsEqual(Column.Parameter(0));

    protected override int SingleExecute(Query audienceQuery) {
    return SetItemCompleted(audienceQuery as Select);
    }
    }
    }
  3. Publish the schema.

5. Implement the back-end part of the campaign element

  1. Create a source code schema. Instructions: Implement the source code.

    For this example, use source code schema properties as follows.

    Property

    Property value

    Code

    UsrTestSmsElement

    Title

    Test SMS element

  2. Implement the back-end business logic of a campaign element. It lets you save base and custom properties of the campaign element.

    1. Specify the CampaignSchemaElement class as a parent class.

    2. Overload the following methods:

      Method name

      Method description

      ApplyMetaDataValue()

      Adds assigned values to created properties when reading

      WriteMetaData()

      Writes the current values of the new properties when writing data

    UsrTestSmsElement
    namespace Terrasoft.Configuration
    {
    using System;
    using System.Collections.Generic;
    using Terrasoft.Common;
    using Terrasoft.Core;
    using Terrasoft.Core.Campaign;
    using Terrasoft.Core.Process;

    [DesignModeProperty(Name = "PhoneNumber", UsageType = DesignModeUsageType.NotVisible, MetaPropertyName = PhoneNumberPropertyName)]
    [DesignModeProperty(Name = "SmsText", UsageType = DesignModeUsageType.NotVisible, MetaPropertyName = SmsTextPropertyName)]
    public class UsrTestSmsElement : CampaignSchemaElement
    {
    private const string PhoneNumberPropertyName = "PhoneNumber";
    private const string SmsTextPropertyName = "SmsText";
    public UsrTestSmsElement() {
    ElementType = CampaignSchemaElementType.AsyncTask;
    }

    public UsrTestSmsElement(UsrTestSmsElement source) : this(source, null, null) {
    }

    public UsrTestSmsElement(UsrTestSmsElement source, Dictionary<Guid, Guid> dictToRebind, Core.Campaign.CampaignSchema parentSchema) : base(source, dictToRebind, parentSchema) {
    ElementType = source.ElementType;
    PhoneNumber = source.PhoneNumber;
    SmsText = source.SmsText;
    }

    protected override Guid Action {
    get {
    return CampaignConsts.CampaignLogTypeMailing;
    }
    }

    [MetaTypeProperty("{A67950E7-FFD7-483D-9E67-3C9A30A733C0}")]
    public string PhoneNumber {
    get;
    set;
    }
    [MetaTypeProperty("{05F86DF2-B9FB-4487-B7BE-F3955703527C}")]
    public string SmsText {
    get;
    set;
    }
    protected override void ApplyMetaDataValue(DataReader reader) {
    base.ApplyMetaDataValue(reader);
    switch (reader.CurrentName) {
    case PhoneNumberPropertyName:
    PhoneNumber = reader.GetValue<string>();
    break;
    case SmsTextPropertyName:
    SmsText = reader.GetValue<string>();
    break;
    }
    }

    public override void WriteMetaData(DataWriter writer) {
    base.WriteMetaData(writer);
    writer.WriteValue(PhoneNumberPropertyName, PhoneNumber, string.Empty);
    writer.WriteValue(SmsTextPropertyName, SmsText, string.Empty);
    }

    public override object Clone() {
    return new UsrTestSmsElement(this);
    }
    public override object Copy(Dictionary<Guid, Guid> dictToRebind, Core.Campaign.CampaignSchema parentSchema) {
    return new UsrTestSmsElement(this, dictToRebind, parentSchema);
    }

    public override ProcessFlowElement CreateProcessFlowElement(UserConnection userConnection) {
    var executableElement = new UsrTestSmsCampaignProcessElement {
    UserConnection = userConnection,
    SmsText = SmsText,
    PhoneNumber = PhoneNumber
    };
    InitializeCampaignProcessFlowElement(executableElement);
    return executableElement;
    }
    }
    }
  3. Publish the schema.

View the result

  1. Set up the connection to the SMS gateway.

    For this example, check the connection, account status, and other parameters when validating the campaign, and send a test SMS when starting the campaign.

    1. Create a source code schema. Instructions: Implement the source code.

      For this example, use source code schema properties as follows.

      Property

      Property value

      Code

      UsrTestSmsEventHandler

      Title

      Test SMS handler

    2. Set up a connection to the SMS gateway. To do this, add the following source code to the Source Code schema section.

      UsrTestSmsEventHandler
      namespace Terrasoft.Configuration
      {
      using System;
      using Terrasoft.Core.Campaign.EventHandler;

      public sealed class UsrTestSmsEventHandler : CampaignEventHandlerBase, IOnCampaignValidate, IOnCampaignStart
      {
      /* Implementing handler for the campaign start event.*/
      public void OnStart() {
      /* TODO: Text SMS message sending logic...*/
      }
      /* Implementing event handler for campaign validation.*/
      public void OnValidate() {
      try {
      /* TODO: SMS gateway connection validation logic...*/
      } catch (Exception ex) {
      /* If errors are found, add information to the campaign schema.*/
      CampaignSchema.AddValidationInfo(ex.Message);
      }
      }
      }
      }
    3. Publish the schema.

  2. Compile the app. Instructions: Compile the configuration.

  3. Clear browser cache.

  4. Open the Marketing workspace.

  5. Create a campaign. Instructions: Add a campaign (user documentation).

As a result, Creatio displays a custom Test SMS element of a marketing campaign in the Campaign Designer. You can use the element in the campaign diagram. When the element is selected, Creatio will display its edit page. View the result >>>


Source code

define("UsrTestSmsElementSchema", ["UsrTestSmsElementSchemaResources", "CampaignBaseCommunicationSchema"], function(resources) {
Ext.define("Terrasoft.manager.UsrTestSmsElementSchema", {
/* Parent schema. */
extend: "Terrasoft.CampaignBaseCommunicationSchema",
alternateClassName: "Terrasoft.UsrTestSmsElementSchema",
/* Manager Id. Must be unique. */
managerItemUId: "a1226f93-f3e3-4baa-89a6-11f2a9ab2d71",
/* Plugged mixins. */
mixins: {
campaignElementMixin: "Terrasoft.CampaignElementMixin"
},
/* Element name. */
name: "TestSms",
/* Resource binding. */
caption: resources.localizableStrings.SmsElementCaption,
titleImage: resources.localizableImages.TitleImage,
largeImage: resources.localizableImages.LargeImage,
smallImage: resources.localizableImages.SmallImage,
/* Schema name of the edit page. */
editPageSchemaName: "TestSmsElementPropertiesPage",
/* Element type. */
elementType: "TestSms",
/* Full name of the class that corresponds to the current schema.
Use following typeName if the class is in the simple package. */
typeName: "Terrasoft.Configuration.UsrTestSmsElement, Terrasoft.Configuration",
/* If the class is in the assembly package use
typeName: "Terrasoft.Configuration.UsrTestSmsElement, SomeAssemblyPackageName" */
/* Overriding the properties of visual styles. */
color: "rgba(249, 160, 27, 1)",
width: 69,
height: 55,
/* Setting up element-specific properties. */
smsText: null,
phoneNumber: null,
/* Determining the types of the element's outbound connections. */
getConnectionUserHandles: function() {
return ["CampaignSequenceFlow", "CampaignConditionalSequenceFlow"];
},
/* Expanding the properties for serialization. */
getSerializableProperties: function() {
var baseSerializableProperties = this.callParent(arguments);
return Ext.Array.push(baseSerializableProperties, ["smsText", "phoneNumber"]);
},
/* Setting up the icons that are displayed on the campaign diagram. */
getSmallImage: function() {
return this.mixins.campaignElementMixin.getImage(this.smallImage);
},
getLargeImage: function() {
return this.mixins.campaignElementMixin.getImage(this.largeImage);
},
getTitleImage: function() {
return this.mixins.campaignElementMixin.getImage(this.titleImage);
}
});
return Terrasoft.UsrTestSmsElementSchema;
});

Resources

Package with example implementation

Link for custom element image