Skip to main content
Version: 8.2

Implement a custom campaign element

Level: advanced

To implement the example:

  1. Create a campaign element. Read more >>>
  2. Create a property panel for the campaign element. Read more >>>
  3. Add the campaign element to the Campaign Designer. Read more >>>
  4. Create an executable element for the campaign element. Read more >>>
  5. Implement the business logic of the campaign element. Read more >>>
Example

Create a custom SMS campaign element to send SMS to specific users. Add the element to the Campaign Designer. Use the icon provided via the link.

1. Create a campaign element

  1. Open the Configuration section. Instructions: Open the Configuration section.

  2. Create a sdkCustomCampaignElement package. Instructions: Create a user-made package using Configuration section.

  3. Change the current package to sdkCustomCampaignElement. Instructions: Change the current package.

  4. Click AddModule.

  5. Fill out the schema properties.

    For this example, use the following schema properties.

    Property

    Property value

    Code

    UsrSmsElement

    Title

    SMS element

  6. Add a localizable string that stores the campaign element name.

    1. Create a localizable string. Instructions: Add a localizable string.

    2. Fill out the localizable string parameters.

      Parameter

      Parameter value

      Code

      SmsElementCaption

      Value

      SMS

    3. Click Add.

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

    For this example, use the icon provided via the link.

    1. Go to the properties area → Images → click scr_add_button.png.

    2. Fill out the image properties.

      Property

      Property value

      Code

      TitleImage

      Image

      Click icn_upload_image.png → select the icon provided via the link.

    3. Click Add.

    4. Repeat steps i–iii for both SmallImage and LargeImage resources.

  8. Add source code that implements the appearance of the campaign element. To do this, link the uploaded image to the TitleImage, LargeImage and SmallImage properties.

    UsrSmsElement
    define("UsrSmsElement", ["UsrSmsElementResources",
    "CampaignBaseCommunicationSchema"], function(resources) {
    Ext.define("Terrasoft.manager.UsrSmsElement", {
    /* Parent schema. */
    extend: "Terrasoft.CampaignBaseCommunicationSchema",
    /* Alternative class name. */
    alternateClassName: "Terrasoft.UsrSmsElement",
    /* ID of the new campaign element. */
    managerItemUId: "a1226f93-f3e3-4baa-89a6-11f2a9ab2d71",
    /* Mixins that extend functionality. */
    mixins: {
    campaignElementMixin: "Terrasoft.CampaignElementMixin"
    },
    /* Element code. */
    name: "Sms",
    /* Localizable strings. */
    caption: resources.localizableStrings.SmsElementCaption,
    titleImage: resources.localizableImages.TitleImage,
    largeImage: resources.localizableImages.LargeImage,
    smallImage: resources.localizableImages.SmallImage,
    /* Schema code of the record page. */
    editPageSchemaName: "UsrSmsElementPropertiesPanel",
    /* Element type. */
    elementType: "Sms",
    /* Full name of the class that implements the campaign element logic.
    This class saves and reads the element properties from the schema
    properties. Since the class is placed in the regular package, use the
    "Terrasoft.Configuration.UsrSmsElement, Terrasoft.Configuration"
    typeName. If the class is placed in the assembly package, use
    typeName: "Terrasoft.Configuration.UsrSmsElement,
    SomeAssemblyPackageName" */
    typeName: "Terrasoft.Configuration.UsrSmsElement, " +
    "Terrasoft.Configuration",
    /* CSS styles. */
    color: "rgba(249, 160, 27, 1)",
    width: 69,
    height: 55,
    /* Custom element properties. */
    smsText: null,
    phoneNumber: null,
    /* Define the types of outbound connections supported by this element. */
    getConnectionUserHandles: function() {
    return ["CampaignSequenceFlow", "CampaignConditionalSequenceFlow"];
    },
    /* Include custom properties in serialization. */
    getSerializableProperties: function() {
    var baseSerializableProperties = this.callParent(arguments);
    return Ext.Array.push(baseSerializableProperties, ["smsText",
    "phoneNumber"]);
    },
    /* Configure the element 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.UsrSmsElement;
    });
  9. Click Save.

2. Create a property panel for the campaign element

  1. Click AddPage view model.

  2. Fill out the schema properties.

    For this example, use the following schema properties.

    Property

    Property value

    Code

    UsrSmsElementPropertiesPanel

    Title

    Properties panel for the SMS element

    Parent object

    BaseCampaignSchemaElementPage

  3. Add the localizable strings that include the field names.

    1. Create a localizable string. Instructions: Add a localizable string.

    2. Fill out the localizable string parameters.

      Code

      Value

      PhoneNumberCaption

      Sender phone

      SmsTextCaption

      Message

      SmsText

      Which SMS text to send?

  4. Implement the business logic of the campaign element.

    UsrSmsElementPropertiesPanel
    define("UsrSmsElementPropertiesPanel", [], function() {
    return {
    /* Schema attributes. */
    attributes: {
    /* Attribute that stores data about the phone number. */
    "PhoneNumber": {
    "dataValueType": this.Terrasoft.DataValueType.TEXT,
    "type": this.Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN
    },
    /* Attribute that stores data about the message text. */
    "SmsText": {
    "dataValueType": this.Terrasoft.DataValueType.TEXT,
    "type": this.Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN
    }
    },
    /* Schema methods. */
    methods: {
    /* Initialize the module. */
    init: function() {
    this.callParent(arguments);
    this.initAcademyUrl(this.onAcademyUrlInitialized, this);
    },
    /* Return the context help code for linking to help documentation. */
    getContextHelpCode: function() {
    return "CampaignSmsElement";
    },
    /* Initialize the attributes using values from the campaign element schema. */
    initParameters: function(element) {
    this.callParent(arguments);
    this.set("SmsText", element.smsText);
    this.set("PhoneNumber", element.phoneNumber);
    },
    /* Save attribute values back to the campaign element schema. */
    saveValues: function() {
    this.callParent(arguments);
    var element = this.get("ProcessElement");
    element.smsText = this.getSmsText();
    element.phoneNumber = this.getPhoneNumber();
    },
    /* Receive the "PhoneNumber" attribute value. */
    getPhoneNumber: function() {
    var number = this.get("PhoneNumber");
    return number ? number : "";
    },
    /* Receive the "SmsText" attribute value. */
    getSmsText: function() {
    var smsText = this.get("SmsText");
    return smsText ? smsText : "";
    }
    },
    /* The schema view description. */
    diff: [
    /* The page container. */
    {
    "operation": "insert",
    "name": "ContentContainer",
    "propertyName": "items",
    "parentName": "EditorsContainer",
    "className": "Terrasoft.GridLayoutEdit",
    "values": {
    "itemType": Terrasoft.ViewItemType.GRID_LAYOUT,
    "items": []
    }
    },
    /* The element caption. */
    {
    "operation": "insert",
    "name": "SmsLabel",
    "parentName": "ContentContainer",
    "propertyName": "items",
    "values": {
    "layout": {
    "column": 0,
    "row": 0,
    "colSpan": 24
    },
    "itemType": this.Terrasoft.ViewItemType.LABEL,
    "caption": {
    "bindTo": "Resources.Strings.SmsText"
    },
    "classes": {
    "labelClass": ["t-title-label-proc"]
    }
    }
    },
    /* The caption of the "Text" type field to enter the sender number. */
    {
    "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"]
    }
    }
    },
    /* The "Text" type field to enter the 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" }
    }
    },
    /* The caption of the "Text" type field to enter the 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"
    }
    }
    },
    /* The "Text" type field to enter the 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" }
    }
    }
    ]
    };
    });
  5. Click Save.

3. Add the campaign element to the Campaign Designer

  1. Click AddReplacing view model.

  2. Fill out the schema properties.

    For this example, use the following schema properties.

    Property

    Property value

    Parent object

    CampaignElementSchemaManagerEx

  3. Add the source code.

    CampaignElementSchemaManagerEx
    require(["CampaignElementSchemaManager", "UsrSmsElement"],
    function() {
    /* Register the "UsrSmsElement" in the Campaign Designer. */
    var coreElementClassNames = Terrasoft.CampaignElementSchemaManager.coreElementClassNames;

    /* Add the custom schema to the list of available schemas. */
    coreElementClassNames.push({
    itemType: "Terrasoft.UsrSmsElement"
    });
    }
    );
  4. Click Save.

4. Create an executable element for the campaign element

  1. Click AddSource code.

  2. Fill out the schema properties.

    For this example, use the following schema properties.

    Property

    Property value

    Code

    UsrSmsCampaignProcessElement

    Title

    SMS process element

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

    1. Specify the CampaignFlowElement class as a parent class.

    2. Override the methods.

      • Query GetAudienceQuery()
      • SingleExecute(Query audienceQuery)
    UsrSmsCampaignProcessElement
    namespace Terrasoft.Configuration
    {

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

    /* Define a custom campaign element. */
    public class UsrSmsCampaignProcessElement : CampaignFlowElement
    {
    public UsrSmsCampaignProcessElement() {
    }

    /* Assign the "PhoneNumber" attribute value in the class. */
    public string PhoneNumber {
    get;
    set;
    }

    /* Assign the "SmsText" attribute value in the class. */
    public string SmsText {
    get;
    set;
    }

    /* Define the campaign audience for the element. */
    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));

    /* Execute the campaign step logic for the selected audience. */
    protected override int SingleExecute(Query audienceQuery) {
    return SetItemCompleted(audienceQuery as Select);
    }
    }
    }
  4. Click Save and publish.

5. Implement the business logic of the campaign element

  1. Click AddSource code.

  2. Fill out the schema properties.

    For this example, use the following schema properties.

    Property

    Property value

    Code

    UsrSmsElement

    Title

    SMS element

  3. Specify the CampaignSchemaElement class as a parent class.

  4. Override the methods.

    • Guid Action
    • ApplyMetaDataValue(DataReader reader)
    • WriteMetaData(DataWriter writer)
    • Clone()
    • Copy(Dictionary<Guid, Guid> dictToRebind, Core.Campaign.CampaignSchema parentSchema)
    • ProcessFlowElement CreateProcessFlowElement(UserConnection userConnection)
    UsrSmsElement
    namespace Terrasoft.Configuration
    {
    using System;
    using System.Collections.Generic;
    using Terrasoft.Common;
    using Terrasoft.Core;
    using Terrasoft.Core.Campaign;
    using Terrasoft.Core.Process;

    /* Define the campaign schema element (metadata) for the custom campaign element.
    This class describes the element in the campaign schema and handles metadata persistence. */
    [DesignModeProperty(
    Name = "PhoneNumber",
    UsageType = DesignModeUsageType.NotVisible,
    MetaPropertyName = PhoneNumberPropertyName
    )]
    [DesignModeProperty(
    Name = "SmsText",
    UsageType = DesignModeUsageType.NotVisible,
    MetaPropertyName = SmsTextPropertyName
    )]

    public class UsrSmsElement : CampaignSchemaElement
    {
    /* Constants for property names used in metadata serialization. */
    private const string PhoneNumberPropertyName = "PhoneNumber";
    private const string SmsTextPropertyName = "SmsText";
    public UsrSmsElement() {
    ElementType = CampaignSchemaElementType.AsyncTask;
    }

    /* Default constructor: defines the element type as an async task. */
    public UsrSmsElement(UsrSmsElement source) : this(source, null, null) {
    }

    /* Copy constructors for cloning the element in schema operations. */
    public UsrSmsElement(
    UsrSmsElement source,
    Dictionary<Guid, Guid> dictToRebind,
    Core.Campaign.CampaignSchema parentSchema
    ) : base(source, dictToRebind, parentSchema) {
    ElementType = source.ElementType;
    PhoneNumber = source.PhoneNumber;
    SmsText = source.SmsText;
    }

    /* Define the campaign log action type associated with this element. */
    protected override Guid Action {
    get {
    return CampaignConsts.CampaignLogTypeMailing;
    }
    }

    [MetaTypeProperty("{A67950E7-FFD7-483D-9E67-3C9A30A733C0}")]
    /* Assign the "PhoneNumber" attribute value in the class. */
    public string PhoneNumber {
    get;
    set;
    }

    [MetaTypeProperty("{05F86DF2-B9FB-4487-B7BE-F3955703527C}")]
    /* Assign the "SmsText" attribute value in the class. */
    public string SmsText {
    get;
    set;
    }

    /* Read element metadata from the schema definition during deserialization. */
    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;
    }
    }

    /* Write element metadata to the schema definition during serialization. */
    public override void WriteMetaData(DataWriter writer) {
    base.WriteMetaData(writer);
    writer.WriteValue(PhoneNumberPropertyName, PhoneNumber, string.Empty);
    writer.WriteValue(SmsTextPropertyName, SmsText, string.Empty);
    }

    /* Create a clone of the schema element. */
    public override object Clone() {
    return new UsrSmsElement(this);
    }

    /* Create a copy of the element using rebind support for schema references. */
    public override object Copy(
    Dictionary<Guid, Guid> dictToRebind,
    Core.Campaign.CampaignSchema parentSchema
    ) {
    return new UsrSmsElement(this, dictToRebind, parentSchema);
    }

    /* Create the runtime process flow element that will be executed in the campaign.
    Pass metadata values to the executable process element. */
    public override ProcessFlowElement CreateProcessFlowElement(
    UserConnection userConnection
    ) {
    var executableElement = new UsrSmsCampaignProcessElement {
    UserConnection = userConnection,
    SmsText = SmsText,
    PhoneNumber = PhoneNumber
    };
    InitializeCampaignProcessFlowElement(executableElement);
    return executableElement;
    }
    }
    }
  5. Click Save and publish.

View the result

  1. Click AddSource code.

  2. Fill out the schema properties.

    For this example, use the following schema properties.

    Property

    Property value

    Code

    UsrSmsEventHandler

    Title

    SMS handler

  3. 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.

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

    /* Define a custom event handler for the campaign element.
    Implement the logic for campaign start and validation events. */
    public sealed class UsrSmsEventHandler :
    CampaignEventHandlerBase,
    IOnCampaignValidate,
    IOnCampaignStart
    {
    /* Handler for the campaign start event. This method is executed
    when the campaign starts. */
    public void OnStart() {
    /* Implement the SMS sending logic.*/
    }
    /* Handler for the campaign validation event. This method is executed
    when the campaign is validated before starting. Use it to check the
    SMS gateway connection, account status, and other parameters. */
    public void OnValidate() {
    try {
    /* Implement the SMS gateway connection validation logic. */
    } catch (Exception ex) {
    /* If validation fails, add the error information to the
    campaign schema. */
    CampaignSchema.AddValidationInfo(ex.Message);
    }
    }
    }
    }
  4. Click Save and publish.

  5. Compile the app. Instructions: Compile the configuration.

  6. Clear browser cache.

  7. Open the Campaigns section.

  8. Create a campaign that has arbitrary parameters. Instructions: Add a campaign (user documentation).

  9. Click Edit campaign flow.

  10. Add the SMS element to the canvas.

As a result, Creatio will display a custom SMS element of a marketing campaign in the Campaign Designer. You will be able to use the element in the campaign diagram. When the element is selected, Creatio will display its record page. View the result >>>

Source code

define("UsrSmsElement", ["UsrSmsElementResources",
"CampaignBaseCommunicationSchema"], function(resources) {
Ext.define("Terrasoft.manager.UsrSmsElement", {
/* Parent schema. */
extend: "Terrasoft.CampaignBaseCommunicationSchema",
/* Alternative class name. */
alternateClassName: "Terrasoft.UsrSmsElement",
/* ID of the new campaign element. */
managerItemUId: "a1226f93-f3e3-4baa-89a6-11f2a9ab2d71",
/* Mixins that extend functionality. */
mixins: {
campaignElementMixin: "Terrasoft.CampaignElementMixin"
},
/* Element code. */
name: "Sms",
/* Localizable strings. */
caption: resources.localizableStrings.SmsElementCaption,
titleImage: resources.localizableImages.TitleImage,
largeImage: resources.localizableImages.LargeImage,
smallImage: resources.localizableImages.SmallImage,
/* Schema code of the record page. */
editPageSchemaName: "UsrSmsElementPropertiesPanel",
/* Element type. */
elementType: "Sms",
/* Full name of the class that implements the campaign element logic.
This class saves and reads the element properties from the schema
properties. Since the class is placed in the regular package, use the
"Terrasoft.Configuration.UsrSmsElement, Terrasoft.Configuration"
typeName. If the class is placed in the assembly package, use
typeName: "Terrasoft.Configuration.UsrSmsElement,
SomeAssemblyPackageName" */
typeName: "Terrasoft.Configuration.UsrSmsElement, " +
"Terrasoft.Configuration",
/* CSS styles. */
color: "rgba(249, 160, 27, 1)",
width: 69,
height: 55,
/* Custom element properties. */
smsText: null,
phoneNumber: null,
/* Define the types of outbound connections supported by this element. */
getConnectionUserHandles: function() {
return ["CampaignSequenceFlow", "CampaignConditionalSequenceFlow"];
},
/* Include custom properties in serialization. */
getSerializableProperties: function() {
var baseSerializableProperties = this.callParent(arguments);
return Ext.Array.push(baseSerializableProperties, ["smsText",
"phoneNumber"]);
},
/* Configure the element 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.UsrSmsElement;
});

Resources

Package with example implementation