Marketing campaigns basics

Advanced

A marketing campaign (campaign) is a set of activities unified by a single concept and aimed to achieve a marketing goal. A campaign is conducted within a certain period for a specific target audience. Campaigns are available in the Marketing Creatio product lineup.

Use campaign to execute the following actions:

  • Nurture customer needs.
  • Inform audiences (campaign participants) about campaign activities.
  • Engage participants.
  • Engage with interested contacts.

Learn more: Marketing campaigns (user documentation).

You can set up a campaign diagram in the Campaigns section. Campaign includes the following diagram components:

  • elements
  • flows between elements
  • general settings

General procedure to set up a campaign:

  1. Create a campaign. Instructions: Add a campaign (user documentation).
  2. Set up the diagram. Instructions: Set up campaign diagram (user documentation).
  3. Launch the campaign.

As a result:

  • The schema of the campaign execution flow will be created. The schema of the campaign execution flow may differ significantly from the visual campaign diagram in the designer.

  • The elements will be converted into the schema of the campaign execution flow.

  • Launch time will be calculated for each element.

You can schedule the next campaign launch. Instructions: Schedule the next campaign launch.

Campaign elements 

When you create a campaign, its layout contains predefined elements.

Campaign diagram lets you use the following elements:

The way to call campaign elements differs depending on the settings and determines their position in the campaign execution flow schema. The first elements to be executed are Adding event audience and Exit from campaign. They define the audience content for the current step. Campaign supports the following ways to call elements:

  • Synchronous call. Executed according to the order specified in the flow schema. The flow to the next elements is performed after the element is executed. In this case, the flow between elements is blocked until the operation is completed.
  • Asynchronous call. Wait until the execution of certain external systems, resources, asynchronous services, or user reactions (for example, clicking a link from the Marketing email element of a campaign) is finished.

Campaign element classes 

Creatio implements campaign elements using JavaScript classes and C# classes.

Class
Class description
JavaScript classes
Terrasoft.manager.ProcessFlowElementSchema The base class of the element schema.
Terrasoft.manager.CampaignBaseCommunicationSchema The parent class for all elements in the Communications group.
Terrasoft.manager.CampaignBaseAudienceSchema The parent class for all elements in the Audience group.
Terrasoft.Configuration.BaseCampaignSchemaElementPage The base schema of the element property panel. The element schema corresponds to the property panel schema. Each new element property panel extends the base schema.
Terrasoft.manager.CampaignSchemaManager Manages the schemas of elements available in Creatio. Inherits the functionality of the Terrasoft.manager.BaseSchemaManager class.
C# classes that implement simple campaign elements
Terrasoft.Core.Campaign.CampaignSchemaElement The base class of the campaign schema element. Parent class for campaign schema elements.
Terrasoft.Configuration.AddCampaignParticipantElement The Adding event audience element class.
Terrasoft.Configuration.ExitFromCampaignElement The Exit from campaign element class.
Terrasoft.Configuration.MarketingEmailElement The Marketing email element class.
Terrasoft.Configuration.EventConditionalTransitionElement The Add from event element class.
Terrasoft.Configuration.LandingConditionalTransitionElement The Add from landing element class.
C# classes that implement executable campaign elements
Terrasoft.Core.Campaign.CampaignProcessFlowElement The base class of the executable campaign element.
Terrasoft.Configuration.AddCampaignAudienceElement The audience element class.
Terrasoft.Configuration.ExcludeCampaignAudienceElement The class of the audience exit element.
Terrasoft.Configuration.BulkEmailCampaignElement The Marketing email element class.

Implement a custom campaign element 

  1. Create a campaign element.
  2. Create a property panel for the campaign element.
  3. Add the element to the element area of the Campaign Designer.
  4. Create an executable element for the campaign element.
  5. Implement the business logic of the campaign element.
  6. Implement campaign event handling. Instructions: Event handler mechanism.

Detailed example: Implement a custom campaign element.

Event handler mechanism 

Use event handler mechanism to implement campaign event handling. You can handle saving, copying, deleting, starting and stopping campaign events.

To implement campaign event handling:

  1. Create a public sealed handler class.
  2. Specify the CampaignEventHandlerBase class as the parent class.
  3. Describe event handler signatures in the class. To do this, implement corresponding interfaces.
  4. Implement a default accessible constructor in the class. The class does not need to be generalized.

Use the following interfaces to describe event handler signatures.

Interface
Interface description
IOnCampaignBeforeSave Contains a method that is called before the campaign is saved.
IOnCampaignAfterSave Contains a method that is called after the campaign is saved.
IOnCampaignDelete Contains a method that is called before the campaign is deleted.
IOnCampaignStart Contains a method that is called before the campaign is started.
IOnCampaignStop Contains a method that is called before the campaign is stopped.
IOnCampaignValidate Contains a method that is called when the campaign is validated. When you implement the interface, we recommend saving errors to the campaign schema using the AddValidationInfo(string) method.
IOnCampaignCopy Contains a method that is called after the campaign is copied.

Make sure to update the configuration library after compilation and include the created types in this library. Otherwise, the Parameter 'type' cannot be null error might occur when saving the campaign.

To fix the Parameter 'type' cannot be null error:

  1. Recompile the project.
  2. Clear the storage that contains cached data.
  3. Clear the application pool.
  4. Restart the IIS.

Flows between campaign elements 

Campaign participants are moving from one element to the other via flows. If the flow has certain conditions set up, Creatio filters the participants based on these conditions and determines the execution time of the subsequent element. Learn more: Transitions between campaign elements (user documentation).

Classes of flow between campaign elements 

Class
Class description
Terrasoft.Configuration.SequenceFlowElement Implements an unconditional flow.
Terrasoft.Configuration.ConditionalSequenceFlowElement Implements a conditional flow.
Terrasoft.Configuration.EmailConditionalTransitionElement Implements a response-based flow.

Implement a custom flow for a campaign element 

  1. Create a flow schema for a campaign element.
  2. Create a flow property panel for a campaign element.
  3. Create an executable element for the campaign element flow.
  4. Implement the business logic of the flow for the campaign element.
  5. Implement the flow logic when the source is changed.
  6. Connect the flow logic.

Detailed example: Implement a custom flow for a campaign element.

Schedule the next campaign launch 

  1. Create a new campaign or open an existing campaign. Instructions: Add a campaign (user documentation).
  2. Fill out the properties of the campaign.
    Property
    Property value
    Start mode
    at the specified time
    Scheduled start date
    date and, if necessary, time of the next campaign launch
  3. Save the changes.

As a result:

  1. The time when to launch the next element will be calculated. Performed depending on the settings for the time delay before the next element is run. Creatio uses formulas for calculation.
  2. The time to launch the next campaign will be set. To do this, Creatio executes the following actions:

    1. Compare values of the element launch time.
    2. Select the closest campaign launch time based on the comparison.
    3. Set the selected value as the next campaign start time.
  3. A list of elements will be generated to execute at the next campaign launch. The list includes all elements whose launch time is equal to the next campaign launch time.

Also, you can configure a time delay before the next element is executed. To do this:

  1. Create a new campaign or open an existing campaign. Instructions: Add a campaign (user documentation).
  2. Fill out the properties of the campaign.
    Property
    Property value
    Enable delay before executing an element?
    "Yes" for the specified period
    Delay unit
    "Days" or "Hours"
    Quantity
    required days (hours) of delay
    Exact time
    Select the Timer validity checkbox and specify the required start time
  3. Save the changes.

View the element start options in the table below.

Staring option
Setup fields
Enable delay before executing an element?
Delay unit
Quantity
Without delay
No, execute after the previous one
Field not available
Field not available
During the day
Yes, for specified time period
Hours
Specifies the delay in hours
In a several days
Yes, for specified time period
Days
Specifies the delay in days

View the formulas for calculating the next element start time for different start options in the table below.

Staring option
Formulas for calculating
Date
Time
Without delay
Closest campaign launch date
Closest campaign launch time
During the day
Current date
Current time + N minutes/hours
In a several days
Current date + N days
User-specified time

Where: N is the value of Quantity field you specified.

Add a custom campaign element
Advanced

Use the Campaign designer to set up marketing campaigns. Using this designer, you can create a campaign diagram that consists of interconnected elements. In addition to default campaign elements you can create custom ones.

Algorithm for adding a custom campaign element 

  1. Create a new element for the Campaign designer.
  2. Create the element’s edit page.
  3. Expand the Campaign designer menu with a new element.
  4. Create the element’s server part.
  5. Create executable element for the new campaign element.
  6. Add custom logic for processing campaign events.

Example. Create a new campaign element for sending text messages (SMS) for users.

Example implementation algorithm 

1. Creating a new element for the Campaign designer 

To display the element in the Campaign designer UI, add a new module schema for the campaign element. The procedure for creating a module schema is covered in the “Create a client schema” article. Set the following properties for the created schema:

  • Title – "Test SMS Element Schema".
  • Name – "TestSmsElementSchema".

Attention. The schema names in the case below do not contain the Usr prefix. You can change the default prefix in the Prefix for object name (SchemaNamePrefix) system setting.

Add a localized string to the schema:

  • Name – "Caption".
  • Value – "Test SMS".
scr_add_localizable_string.png

Add images that will represent the campaign element in the Campaign designer. Use the SmallImage, LargeImage and TitleImage properties to add the images.

scr_add_small_image.png

In this example we used a scalable vector graphics (SVG) image available here.

Add following source code on the Source Code section of the schema":

TestSmsElementSchema
define("TestSmsElementSchema", ["TestSmsElementSchemaResources", "CampaignBaseCommunicationSchema"],
    function(resources) {
        Ext.define("Terrasoft.manager.TestSmsElementSchema", {
            // Parent schema.
            extend: "Terrasoft.CampaignBaseCommunicationSchema",
            alternateClassName: "Terrasoft.TestSmsElementSchema",
            // 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.Caption,
            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.
            typeName: "Terrasoft.Configuration.TestSmsElement, Terrasoft.Configuration",
            // 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 elemen's outbound connections.
            getConnectionUserHandles: function() {
                return ["CampaignSequenceFlow", "CampaignConditionalSequenceFlow"];
            },
            // Expnding 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.TestSmsElementSchema;
    });

Specifics:

  • The managerItemUId property value must be unique for the new element and not repeat the value of the other elements.
  • The typeName property contains the name of the C# class that corresponds to the campaign element name. This class will be saving and reading the element’s properties from the schema metadata.

Save the schema to apply changes.

Adding a group of elements 

If a new group of elements, such as Scripts must be created for the campaign element, the schema source code must be supplemented with the following code:

TestSmsElementSchema
// Name of the new group.
group: "Scripts",

constructor: function() {
      if (!Terrasoft.CampaignElementGroups.Items.contains("Scripts")) {
           Terrasoft.CampaignElementGroups.Items.add("Scripts", {
                name: "Scripts",
                caption: resources.localizableStrings.ScriptsElementGroupCaption
           });
      }
      this.callParent(arguments);
}

Also, add a localized string with the following properties:

  • Name – "ScriptsElementGroupCaption".
  • Value – "Scripts".

Save the schema to apply changes.

2. Creating the element’s edit page 

Create the campaign element’s edit page in the custom package to enable the users to view and edit the element’s properties. To do this, create a schema that expands BaseCampaignSchemaElementPage (CampaignDesigner package). The procedure for creating a replacing client schema is covered in the “Create a client schema” article.

Set the following properties for the created schema:

  • Title – "TestSmsElementPropertiesPage".
  • Name – "TestSmsElementPropertiesPage".
  • Parent object – "BaseCampaignSchemaElementPage".

Add localized strings to the created schema with properties given in the table.

Localized string properties

Name Value
PhoneNumberCaption Sender phone number
SmsTextCaption Message
TestSmsText Which text message should be sent? (Which SMS text to send?)

Add following source code on the Source Code section of the schema":

TestSmsElementPropertiesPage
define("TestSmsElementPropertiesPage", [],
    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" }
                    }
                }
            ]
        };
    }
);

Save the schema to apply changes.

3. Expanding the Campaign designer menu with a new element 

To display the new element in the Campaign designer menu, expand the campaign element base schema manager. Add a schema that expands CampaignElementSchemaManagerEx (the CampaignDesigner package) to the custom package. The procedure for creating a replacing client schema is covered in the “Create a client schema” article.

Set the following properties for the created schema:

  • Title – "TestSmsCampaignElementSchemaManagerEx".
  • Name – "CampaignElementSchemaManagerEx".
  • Parent object – "CampaignElementSchemaManagerEx".

Add following source code on the Source Code section of the schema":

CampaignElementSchemaManager
require(["CampaignElementSchemaManager", "TestSmsElementSchema"],
    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.TestSmsElementSchema"
        });
    });

Save the schema to apply changes.

4. Creating server part of the custom campaign element 

To implement saving the campaign element properties, create a class that interacts with the application server part. The class must inherit CampaignSchemaElement and override the ApplyMetaDataValue() and WriteMetaData() methods.

Create a source code schema with the following properties:

  • Title – "TestSmsElement".
  • Name – "TestSmsElement".

For more information on creating source code schemas, please see the Create the Source code schema article.

Add the following source code on the Source Code section of the schema":

TestSmsElement
namespace Terrasoft.Configuration
{
    using System;
    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 TestSmsElement : CampaignSchemaElement
    {
        private const string PhoneNumberPropertyName = "PhoneNumber";
        private const string SmsTextPropertyName = "SmsText";
        // Default constructor.
        public TestSmsElement() {
            ElementType = CampaignSchemaElementType.AsyncTask;
        }
        // Constructor with parameter.
        public TestSmsElement(TestSmsElement source)
                : base(source) {
            ElementType = CampaignSchemaElementType.AsyncTask;
            PhoneNumber = source.PhoneNumber;
            SmsText = source.SmsText;
        }

        // Instance action Id.
        protected override Guid Action {
            get {
                return CampaignConsts.CampaignLogTypeMailing;
            }
        }

        // Phone number.
        [MetaTypeProperty("{A67950E7-FFD7-483D-9E67-3C9A30A733C0}")]
        public string PhoneNumber {
            get;
            set;
        }
        // Text message.
        [MetaTypeProperty("{05F86DF2-B9FB-4487-B7BE-F3955703527C}")]
        public string SmsText {
            get;
            set;
        }
        // Applies metadata values.
        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;
            }
        }

        // Records metadata values.
        public override void WriteMetaData(DataWriter writer) {
            base.WriteMetaData(writer);
            writer.WriteValue(PhoneNumberPropertyName, PhoneNumber, string.Empty);
            writer.WriteValue(SmsTextPropertyName, SmsText, string.Empty);
        }
        // Copies element.
        public override object Clone() {
            return new TestSmsElement(this);
        }

        // Creates a specific ProcessFlowElement instance.
        public override ProcessFlowElement CreateProcessFlowElement(UserConnection userConnection) {
            var executableElement = new TestSmsCampaignProcessElement {
                UserConnection = userConnection,
                SmsText = SmsText,
                PhoneNumber = PhoneNumber
            };
            InitializeCampaignProcessFlowElement(executableElement);
            return executableElement;
        }
    }
}

Publish the source code schema.

5. Creating executable element for the new campaign element 

For the custom campaign element to execute the needed logic, add an executable element. It is a class that inherits the CampaignProcessFlowElement class, where the SafeExecute() method is implemented.

To create an executable element, add a source code schema element with the following properties in the custom package:

  • Title – "TestSmsCampaignProcessElement".
  • Name – "TestSmsCampaignProcessElement".

Add following source code on the Source Code section of the schema":

TestSmsCampaignProcessElement
namespace Terrasoft.Configuration
{
    using System;
    using System.Collections.Generic;
    using Terrasoft.Core.Campaign;
    using Terrasoft.Core.DB;
    using Terrasoft.Core.Process;
    
    public class TestSmsCampaignProcessElement : CampaignProcessFlowElement
    {
        public const string ContactTableName = "Contact";

        public TestSmsCampaignProcessElement(ICampaignAudience campaignAudience) {
            CampaignAudience = campaignAudience;
        }

        public TestSmsCampaignProcessElement() {
        }
        
        // Audiences for whom to send texts on the current step.
        private ICampaignAudience _campaignAudience;
        private ICampaignAudience CampaignAudience {
            get {
                return _campaignAudience ??
                    (_campaignAudience = new CampaignAudience(UserConnection, CampaignId));
            }
            set {
                _campaignAudience = value;
            }
        }

        // SMS-specific properties. Passed from an instance of the TestSmsElement class.
        public string PhoneNumber {
            get;
            set;
        }
        public string SmsText {
            get;
            set;
        }

        // Implementation of the element execution method
        protected override int SafeExecute(ProcessExecutingContext context) {
            // TODO: Implement sending SMS messages.
            //
            // Current step for audiences is set as completed.
            return CampaignAudience.SetItemCompleted(SchemaElementUId);
        }
    }
}

Publish the source code schema.

6. Adding custom logic for processing campaign events 

Use the event handler mechanism to implement custom logic on saving, copying, deleting, running and stopping campaigns. Create a public sealed handler class that inherits CampaignEventHandlerBase. Implement interfaces that describe specific event handler signatures. This class must not be generic. It must have a constructor available by default.

The following interfaces are supported in the current version:

  • IOnCampaignBeforeSave – contains method that will be called before saving the campaign.
  • IOnCampaignAfterSave – contains method that will be called after saving the campaign.
  • IOnCampaignDelete – contains method that will be called before deleting the campaign.
  • IOnCampaignStart – contains method that will be called before running the campaign.
  • IOnCampaignStop – contains method that will be called before stopping the campaign.
  • IOnCampaignValidate – contains method that will be called on validating the campaign.
  • IOnCampaignCopy – contains method that will be called after copying the campaign.

If an exception occurs during the event processing, the call chain is stopped, and campaign status is reverted to the previous one in DB.

Note. When implementing the IOnCampaignValidate interface, save errors in the campaign schema using the AddValidationInfo(string) method.

Additional case conditions 

In order for the new custom campaign element to work, SMS gateway connection is required. The connection, account status and other parameters must be checked during campaign validation. The messages must be sent when campaign starts.

To implement these conditions, add a source code schema element with the following properties in the custom package:

  • Title – "TestSmsEventHandler".
  • Name – "TestSmsEventHandler".

Add following source code on the Source Code section of the schema":

TestSmsEventHandler
namespace Terrasoft.Configuration
{
    using System;
    using Terrasoft.Core.Campaign.EventHandler;
    
    public sealed class TestSmsEventHandler : 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);
            }
        }
    }
}

After making the changes, publish the schema. Compile the application and clear the cache.

As a result, a new TestSMS element will be added in the campaign element menu (1) that the users can add to the campaign diagram (2). When an added element is selected, its edit page will be displayed (3).

scr_result.png

Attention. When saving the campaign, the “Parameter ‘type’ cannot be null” may occur. The error indicates that the configuration library was not updated after the compilation and therefor does not contain the new types.

Recompile the project and clear all possible storages with cached data. You may also need to clear the application pool and restart the website in IIS on the application server.

Configure campaign elements for working with triggers
Advanced

Starting from version 7.12.4, a new Triggered adding campaign element has been added to Creatio. The way the element works with campaign audience has been changed: as soon as the trigger is run, the synchronous campaign element is launched.

Thus, new and existing campaign elements must be configured for working with this element.

To include the custom element into the synchronous fragment and make sure it works correctly both for the scheduled execution and triggers, make the following changes on the server side:

  • Specify the additional CampaignSchemaElementType.Sessioned type for the element.
  • The executed element for the campaign custom element (the inheritor of the CampaignProcessFlowElement class) must work using the CampaignAudience base property. Perform all operations (specifying the audience, selecting the Step complete checkbox) via the object in the CampaignAudience property of the ICampaignAudience type.

Example. Configure the marketing campaign element for sending SMS messages to users. Learn how to create this element in the “Add a custom campaign element” article.

Example implementation algorithm 

1. Modifying the class that interacts with the server side of the application 

Attention. Perform steps 1-6 from the “Add a custom campaign element” article before you implement the case.

Change the TestSmsElement source code schema class (the inheritor of the CampaignSchemaElement class). Specify additional ElementTypeCampaignSchemaElementType.Sessioned in the class constructor.

TestSmsElement
public TestSmsElement() {
    // TestSmsElement is asynchronous and session element that can be triggered.
    ElementType = CampaignSchemaElementType.AsyncTask | CampaignSchemaElementType.Sessioned;
}

2. Modifying the executed element for the new campaign element 

Modify the TestSmsCampaignProcessElement source code schema class (the inheritor of the CampaignProcessFlowElement class). Add the audience reading operation to the SafeExecute() method implementation via the object in the CampaignAudience property of the ICampaignAudience type.

TestSmsCampaignProcessElement
// Method implementation of the element performance.
    protected override int SafeExecute(ProcessExecutingContext context) {
        // TODO: Implement sending SMS-messages.
        //
        //
        // Receive the audience that is available for processing by the element at the moment..
        var audienceSelect = CampaignAudience.GetItemAudienceSelect(CampaignItemId);
        //
        // Specify the current audience step as [Complete].
        return CampaignAudience.SetItemCompleted(SchemaElementUId);
    }
Add a custom flow to a new campaign element
Advanced

Use Campaign designer to set up your marketing campaigns. You can create a visual campaign diagram that would consist of interconnected pre-configured elements. You can also add custom campaign elements.

Algorithm of adding a custom flow (an arrow) 

  1. Create a new schema for the Flow element.
  2. Create the edit page for the Flow element properties.
  3. Create the server part for the Flow element.
  4. Create the executed element for the flow transition.
  5. Create the CampaignConnectorManager replacing module for adding the flow operation logic.
  6. Connect the CampaignConnectorManager replacing module.

Example. Create a custom flow (an arrow) from the new campaign element for sending SMS-messages to a user and add a possibility to select the bulk sms response condition. On the setup page, a user can select an option to either ignore the responses or take them into consideration based on the list of possible bulk sms response types. If no response type is selected, the flow is enabled for any response.

Attention. In this case, the Usr prefix is not used in schema names. You can change the prefix used by default in the Prefix for object name system setting (the SchemaNamePrefix code).

Attention. Perform steps 1-6 from the “Add a custom campaign element” article before you implement the case.

Example implementation algorithm 

1. Creating the TestSmsTarget and TestSmsResponseType objects 

Create schemas for the TestSmsTarget and TestSmsResponseType objects in the development package.

Learn more about creating object schemas in the “Create the entity schema” article.

Add a column with the following properties to the TestSmsResponseType schema:

  • Title – “Name”
  • Name – “Name”
  • Data type – “Text (50 characters)”

Add columns with the following properties to the TestSmsTarget schema:

Primary column properties of the TestSmsTarget object schema

Title Name Data type
Phone number PhoneNumber “Text (50 characters)”
SMS text SmsText “Text (50 characters)”
Contact Contact “Lookup” – “Contact”
Test SMS response TestSmsResponse “Lookup” – “TestSmsResponseType”

Learn more about adding object schema columns in the “Create the entity schema” article.

Columns and properties of the TestSmsTarget object schema
scr_adding_custom_arrow_5.png
Columns and properties of the TestSmsResponseType object schema
scr_adding_custom_arrow_4.png

Save and publish the objects.

Create a lookup for the TestSmsResponseType object and populate it with the “Sms delivered”, “Canceled”, “Error while receiving”, etc. values. The response identifiers (Ids) will be used in the edit page code of the condition flow properties from the bulk SMS.

scr_adding_custom_arrow_8.png

2. Creating a new schema for the flow element 

To display the element in the Campaign designer user interface, create a new module schema in the development package. The procedure for creating a module schema is covered in the “Create a client schema” article. Set the following properties for the created schema:

  • Title – “ProcessTestSmsConditionalTransitionSchema”.
  • Name – “ProcessTestSmsConditionalTransitionSchema”.

Attention. The schema name for the arrow must contain the “Process” prefix

scr_adding_custom_arrow_1.png

Add the following source code to the Source code section of the schema:

ProcessTestSmsConditionalTransitionSchema
define("ProcessTestSmsConditionalTransitionSchema", ["CampaignEnums",
    "ProcessTestSmsConditionalTransitionSchemaResources",
    "ProcessCampaignConditionalSequenceFlowSchema"],
    function(CampaignEnums) {
        Ext.define("Terrasoft.manager.ProcessTestSmsConditionalTransitionSchema", {
            extend: "Terrasoft.ProcessCampaignConditionalSequenceFlowSchema",
            alternateClassName: "Terrasoft.ProcessTestSmsConditionalTransitionSchema",
            managerItemUId: "4b5e70b0-a631-458e-ab22-856ddc913444",
            mixins: {
                parametrizedProcessSchemaElement: "Terrasoft.ParametrizedProcessSchemaElement"
            },
            // The full type name of the connected arrow element.
            typeName: "Terrasoft.Configuration.TestSmsConditionalTransitionElement, Terrasoft.Configuration",
            // The name of the arrow element for connecting to campaign elements.
            connectionUserHandleName: "TestSmsConditionalTransition",
            // The name of the arrow property setup page.
            editPageSchemaName: "TestSmsConditionalTransitionPropertiesPage",
            elementType: CampaignEnums.CampaignSchemaElementTypes.CONDITIONAL_TRANSITION,
            // Bulk sms response collection.
            testSmsResponseId: null,
            // The checkbox that takes into consideration the response condition for contact transition.
            isResponseBasedStart: false,
            getSerializableProperties: function() {
                var baseSerializableProperties = this.callParent(arguments);
                // Properties for serialization and transfer to the server part when saving.
                Ext.Array.push(baseSerializableProperties, ["testSmsResponseId", "isResponseBasedStart"]);
                return baseSerializableProperties;
            }
        });
        return Terrasoft.ProcessTestSmsConditionalTransitionSchema;
    });

Save the created schema.

3. Creating the edit page of the flow element properties 

To display and modify campaign element properties, create its edit page in the development package. Create the schema replacing CampaignConditionalSequenceFlowPropertiesPage (the CampaignDesigner package). The procedure for creating a replacing schema is covered in the “Create a client schema” article.

Set the following properties for the created schema:

  • Title – “TestSmsConditionalTransitionPropertiesPage”.
  • Name – “TestSmsConditionalTransitionPropertiesPage”.
  • Parent object – “CampaignConditionalSequenceFlowPropertiesPage”.
scr_adding_custom_arrow_2.png

Add localizable strings, whose properties are listed in table to the created schema.

Primary properties of the localizable strings

Name Value
ReactionModeCaption What is the result of the {0} step?
ReactionModeDefault Transfer participants regardless of their response
ReactionModeWithCondition Set up responses for transferring participants
IsTestSmsDelivered Test SMS delivered
IsErrorWhileReceiving Error while receiving

Add the following source code to the Source code section of the schema:

TestSmsConditionalTransitionPropertiesPage
define("TestSmsConditionalTransitionPropertiesPage", ["BusinessRuleModule"],
    function(BusinessRuleModule) {
        return {
            messages: {},
            attributes: {
                "ReactionModeEnum": {
                    dataValueType: this.Terrasoft.DataValueType.CUSTOM_OBJECT,
                    type: this.Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
                    value: {
                        Default: {
                            value: "0",
                            captionName: "Resources.Strings.ReactionModeDefault"
                        },
                        WithCondition: {
                            value: "1",
                            captionName: "Resources.Strings.ReactionModeWithCondition"
                        }
                    }
                },
                "ReactionMode": {
                    "dataValueType": this.Terrasoft.DataValueType.LOOKUP,
                    "type": this.Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
                    "isRequired": true
                },
                "IsTestSmsDelivered": {
                    "dataValueType": this.Terrasoft.DataValueType.BOOLEAN,
                    "type": this.Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN
                },
                "IsErrorWhileReceiving": {
                    "dataValueType": this.Terrasoft.DataValueType.BOOLEAN,
                    "type": this.Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN
                }
            },
 
            rules:
            {
                "ReactionConditionDecision": {
                    "BindReactionConditionDecisionRequiredToReactionMode": {
                        "ruleType": BusinessRuleModule.enums.RuleType.BINDPARAMETER,
                        "property": BusinessRuleModule.enums.Property.REQUIRED,
                        "conditions": [{
                            "leftExpression": {
                                "type": BusinessRuleModule.enums.ValueType.ATTRIBUTE,
                                "attribute": "ReactionMode"
                            },
                            "comparisonType": this.Terrasoft.ComparisonType.EQUAL,
                            "rightExpression": {
                                "type": BusinessRuleModule.enums.ValueType.CONSTANT,
                                "value": "1"
                            }
                        }]
                    }
                }
            },
            methods: {
                // Ensures sms-response correspondence (based on the TestSmsResponseType lookup).
                // We assume that Creatio already contains the TestSmsResponseType lookup
                // with the TestSmsDelivered and ErrorWhileReceiving records.
                getResponseConfig: function() {
                    return {
                        "IsTestSmsDelivered": "F2FC75B3-58C3-49A6-B2F2-353262068145",
                        "IsErrorWhileReceiving": "37B9F9D5-E897-4B7B-A65E-3B3799A18D72"
                    };
                },
 
                subscribeEvents: function() {
                    this.callParent(arguments);
                    // Connecting the handler to the event of changing the ReactionMode attribute value
                    this.on("change:ReactionMode", this.onReactionModeLookupChanged, this);
                },
 
                // Event handler-method of changing the ReactionMode attribute.
                onReactionModeLookupChanged: function() {
                    var reactionModeEnum = this.get("ReactionModeEnum");
                    var reactionMode = this.get("ReactionMode");
                    var decisionModeEnabled = (reactionMode && reactionMode.value === reactionModeEnum.WithCondition.value);
                    if (!decisionModeEnabled) {
                        this.set("ReactionConditionDecision", null);
                    }
                },
 
                // Initiates the viewModel properties to display the page when opening.
                initParameters: function(element) {
                    this.callParent(arguments);
                    var isResponseBasedStart = element.isResponseBasedStart;
                    this.initReactionMode(isResponseBasedStart);
                    this.initTestSmsResponses(element.testSmsResponseId);
                },
 
                // Auxiliary method cutting the line to the specified length and adding allipsis at the end.
                cutString: function(strValue, strLength) {
                    var ellipsis = Ext.String.ellipsis(strValue.substring(strLength), 0);
                    return strValue.substring(0, strLength) + ellipsis;
                },
 
                // Sets the status value to "Sms delivered".
                initIsTestSmsDelivered: function(value) {
                    if (value === undefined) {
                        value = this.get("IsTestSmsDelivered");
                    }
                    this.set("IsTestSmsDelivered", value);
                },
 
                // Sets the status value to "Error while receiving".
                initIsErrorWhileReceiving: function(value) {
                    if (value === undefined) {
                        var isErrorWhileReceiving = this.get("IsErrorWhileReceiving");
                        value = isErrorWhileReceiving;
                    }
                    this.set("IsErrorWhileReceiving", value);
                },
 
                // Initiates the selected responses when opening the page.
                initTestSmsResponses: function(responseIdsJson) {
                    if (!responseIdsJson) {
                        return;
                    }
                    var responseIds = JSON.parse(responseIdsJson);
                    var config = this.getResponseConfig();
                    Terrasoft.each(config, function(propValue, propName) {
                        if (responseIds.indexOf(propValue) > -1) {
                            this.set(propName, true);
                        }
                    }, this);
                },
 
                initReactionMode: function(value) {
                    var isDefault = !value;
                    this.setLookupValue(isDefault, "ReactionMode", "WithCondition", this);
                },
 
                // Auxiliary method extracting the identifier array from the incoming JSON parameter.
                getIds: function(idsJson) {
                    if (idsJson) {
                        try {
                            var ids = JSON.parse(idsJson);
                            if (this.Ext.isArray(ids)) {
                                return ids;
                            }
                        } catch (error) {
                            return [];
                        }
                    }
                    return [];
                },
 
                onPrepareReactionModeList: function(filter, list) {
                    this.prepareList("ReactionModeEnum", list, this);
                },
 
                // Saves the response values and the setting of adding the response condition. 
                saveValues: function() {
                    this.callParent(arguments);
                    var element = this.get("ProcessElement");
                    var isResponseBasedStart = this.getIsReactionModeWithConditions();
                    element.isResponseBasedStart = isResponseBasedStart;
                    element.testSmsResponseId = this.getTestSmsResponseId(isResponseBasedStart);
                },
 
                // Receives the serialized Ids of the selected responses.
                getTestSmsResponseId: function(isResponseActive) {
                    var responseIds = [];
                    if (isResponseActive) {
                        var config = this.getResponseConfig();
                        Terrasoft.each(config, function(propValue, propName) {
                            var attrValue = this.get(propName);
                            if (attrValue && propValue) {
                                responseIds.push(propValue);
                            }
                        }, this);
                    }
                    return JSON.stringify(responseIds);
                },
 
                getLookupValue: function(parameterName) {
                    var value = this.get(parameterName);
                    return value ? value.value : null;
                },
 
                getContextHelpCode: function() {
                    return "CampaignConditionalSequenceFlow";
                },
 
                getIsReactionModeWithConditions: function() {
                    return this.isLookupValueEqual("ReactionMode", "1", this);
                },
 
                getSourceElement: function() {
                    var flowElement = this.get("ProcessElement");
                    if (flowElement) {
                        return flowElement.findSourceElement();
                    }
                    return null;
                },
                // Adds the name of the element that the arrow is generated from to the text.
                getQuestionCaption: function() {
                    var caption = this.get("Resources.Strings.ReactionModeCaption");
                    caption = this.Ext.String.format(caption, this.getSourceElement().getCaption());
                    return caption;
                }
            },
            diff: /**SCHEMA_DIFF*/[
                // Container.
                {
                    "operation": "insert",
                    "name": "ReactionContainer",
                    "propertyName": "items",
                    "parentName": "ContentContainer",
                    "className": "Terrasoft.GridLayoutEdit",
                    "values":
                    {
                        "layout":
                        {
                            "column": 0,
                            "row": 2,
                            "colSpan": 24
                        },
                        "itemType": this.Terrasoft.ViewItemType.GRID_LAYOUT,
                        "items": []
                    }
                },
                // Title.
                {
                    "operation": "insert",
                    "name": "ReactionModeLabel",
                    "parentName": "ReactionContainer",
                    "propertyName": "items",
                    "values":
                    {
                        "layout":
                        {
                            "column": 0,
                            "row": 0,
                            "colSpan": 24
                        },
                        "itemType": this.Terrasoft.ViewItemType.LABEL,
                        "caption":
                        {
                            "bindTo": "getQuestionCaption"
                        },
                        "classes":
                        {
                            "labelClass": ["t-title-label-proc"]
                        }
                    }
                },
                // List.
                {
                    "operation": "insert",
                    "name": "ReactionMode",
                    "parentName": "ReactionContainer",
                    "propertyName": "items",
                    "values":
                    {
                        "contentType": this.Terrasoft.ContentType.ENUM,
                        "controlConfig":
                        {
                            "prepareList":
                            {
                                "bindTo": "onPrepareReactionModeList"
                            }
                        },
                        "isRequired": true,
                        "layout":
                        {
                            "column": 0,
                            "row": 1,
                            "colSpan": 24
                        },
                        "labelConfig":
                        {
                            "visible": false
                        },
                        "wrapClass": ["no-caption-control"]
                    }
                },
                // List element.
                {
                    "operation": "insert",
                    "parentName": "ReactionContainer",
                    "propertyName": "items",
                    "name": "IsTestSmsDelivered",
                    "values":
                    {
                        "wrapClass": ["t-checkbox-control"],
                        "visible":
                        {
                            "bindTo": "ReactionMode",
                            "bindConfig":
                            {
                                converter: "getIsReactionModeWithConditions"
                            }
                        },
                        "caption":
                        {
                            "bindTo": "Resources.Strings.IsTestSmsDelivered"
                        },
                        "layout":
                        {
                            "column": 0,
                            "row": 2,
                            "colSpan": 22
                        }
                    }
                },
                // List element.
                {
                    "operation": "insert",
                    "parentName": "ReactionContainer",
                    "propertyName": "items",
                    "name": "IsErrorWhileReceiving",
                    "values":
                    {
                        "wrapClass": ["t-checkbox-control"],
                        "visible":
                        {
                            "bindTo": "ReactionMode",
                            "bindConfig":
                            {
                                converter: "getIsReactionModeWithConditions"
                            }
                        },
                        "caption":
                        {
                            "bindTo": "Resources.Strings.IsErrorWhileReceiving"
                        },
                        "layout":
                        {
                            "column": 0,
                            "row": 3,
                            "colSpan": 22
                        }
                    }
                }
            ]/**SCHEMA_DIFF*/
        };
    }
);

Save the created schema.

4. Creating the server part of the flow element from the Bulk SMS element 

To implement saving the base and user campaign element properties, create a class interacting with the server part of the application. The class should inherit CampaignSchemaElement and override the ApplyMetaDataValue() and WriteMetaData() methods.

Create the source code schema with the following properties:

  • Title – “TestSmsConditionalTransitionElement”;
  • Name – “TestSmsConditionalTransitionElement”.

Creating the source code schema is covered in the “Create the Source code schema” article.

Add the following source code to the Source code section of the schema:

TestSmsConditionalTransitionElement
namespace Terrasoft.Configuration
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Globalization;
    using System.Linq;
    using Newtonsoft.Json;
    using Terrasoft.Common;
    using Terrasoft.Core;
    using Terrasoft.Core.Campaign;
    using Terrasoft.Core.DB;
    using Terrasoft.Core.Process;
    
    [DesignModeProperty(Name = "TestSmsResponseId",
        UsageType = DesignModeUsageType.NotVisible, MetaPropertyName = TestSmsResponseIdPropertyName)]
    [DesignModeProperty(Name = "IsResponseBasedStart",
        UsageType = DesignModeUsageType.Advanced, MetaPropertyName = IsResponseBasedStartPropertyName)]
    public class TestSmsConditionalTransitionElement : ConditionalSequenceFlowElement
    {
 
        private const string TestSmsResponseIdPropertyName = "TestSmsResponseId";
        private const string IsResponseBasedStartPropertyName = "IsResponseBasedStart";
        
        public TestSmsConditionalTransitionElement() {}
        
        public TestSmsConditionalTransitionElement(TestSmsConditionalTransitionElement source)
            : this(source, null, null) {}
 
        public TestSmsConditionalTransitionElement(TestSmsConditionalTransitionElement source,
            Dictionary<Guid, Guid> dictToRebind, Core.Campaign.CampaignSchema parentSchema)
            : base(source, dictToRebind, parentSchema) {
            IsResponseBasedStart = source.IsResponseBasedStart;
            _testSmsResponseIdJson = JsonConvert.SerializeObject(source.TestSmsResponseId);
        }
 
        private string _testSmsResponseIdJson;
        
        private IEnumerable<Guid> Responses {
            get {
                return TestSmsResponseId;
            }
        }
 
        [MetaTypeProperty("{DC597899-B831-458A-A58E-FB43B1E266AC}")]
        public IEnumerable<Guid> TestSmsResponseId {
            get {
                return !string.IsNullOrWhiteSpace(_testSmsResponseIdJson)
                    ? JsonConvert.DeserializeObject<IEnumerable<Guid>>(_testSmsResponseIdJson)
                    : Enumerable.Empty<Guid>();
            }
        }
        
        [MetaTypeProperty("{3FFA4EA0-62CC-49A8-91FF-4096AEC561F6}",
    IsExtraProperty = true, IsUserProperty = true)]
        public virtual bool IsResponseBasedStart {
            get;
            set;
        }
        
        protected override void ApplyMetaDataValue(DataReader reader) {
            base.ApplyMetaDataValue(reader);
            switch (reader.CurrentName) {
                case TestSmsResponseIdPropertyName:
                    _testSmsResponseIdJson = reader.GetValue<string>();
                    break;
                case IsResponseBasedStartPropertyName:
                    IsResponseBasedStart = reader.GetBoolValue();
                    break;
                default:
                    break;
            }
        }
        
        public override void WriteMetaData(DataWriter writer) {
            base.WriteMetaData(writer);
            writer.WriteValue(IsResponseBasedStartPropertyName, IsResponseBasedStart, false);
            writer.WriteValue(TestSmsResponseIdPropertyName, _testSmsResponseIdJson, null);
        }
        
        public override object Clone() {
            return new TestSmsConditionalTransitionElement(this);
        }
        
        public override object Copy(Dictionary<Guid, Guid> dictToRebind, Core.Campaign.CampaignSchema parentSchema) {
            return new TestSmsConditionalTransitionElement(this, dictToRebind, parentSchema);
        }
        
        // Overrides the factory method for creating the executed element 
        // Returns the element with the  TestSmsConditionalTransitionFlowElement type
        public override ProcessFlowElement CreateProcessFlowElement(UserConnection userConnection) {
            var sourceElement = SourceRef as TestSmsElement;
            var executableElement = new TestSmsConditionalTransitionFlowElement {
                UserConnection = userConnection,
                TestSmsResponses = TestSmsResponseId,
                PhoneNumber = sourceElement.PhoneNumber,
                SmsText = sourceElement.SmsText
            };
            InitializeCampaignProcessFlowElement(executableElement);
            InitializeCampaignTransitionFlowElement(executableElement);
            InitializeConditionalTransitionFlowElement(executableElement);
            return executableElement;
        } 
    }  
}

Save and publish the created schema.

5. Creating the executed element for the flow from the Bulk SMS element 

To add a functionality that would take into consideration the response type as per the sent bulk sms, create the executed element. It is a class, the inheritor of the ConditionalTransitionFlowElement class.

To create the executed element, add the source code schema with the following properties in the development package:

  • Title – “TestSmsConditionalTransitionElement”.
  • Name – “TestSmsConditionalTransitionElement”.

Add the following source code to the Source code section of the schema:

TestSmsConditionalTransitionFlowElement
namespace Terrasoft.Configuration
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Terrasoft.Common;
    using Terrasoft.Core.DB;
 
    public class TestSmsConditionalTransitionFlowElement : ConditionalTransitionFlowElement
    {
 
        public string SmsText { get; set; }
 
        public string PhoneNumber { get; set; }
 
        public IEnumerable<Guid> TestSmsResponses { get; set; }
 
        private void ExtendWithResponses() {
            TransitionQuery.CheckArgumentNull("TransitionQuery");
            if (TestSmsResponses.Any()) {
                Query responseSelect = GetSelectByParticipantResponses();
                TransitionQuery.And("ContactId").In(responseSelect);
            }
        }
 
        private Query GetSelectByParticipantResponses() {
            var responseSelect = new Select(UserConnection)
                .Column("ContactId")
                .From("TestSmsTarget")
                .Where("SmsText").IsEqual(Column.Parameter(SmsText))
                    .And("PhoneNumber").IsEqual(Column.Parameter(PhoneNumber))
                    .And("TestSmsResponseId")
                        .In(Column.Parameters(TestSmsResponses)) as Select;
            responseSelect.SpecifyNoLockHints(true);
            return responseSelect;
        }
 
        protected override void CreateQuery() {
            base.CreateQuery();
            ExtendWithResponses();
        }
    }
}

Save and publish the created schema.

6. Creating the CampaignConnectorManager replacing module for adding the flow operation logic 

To add specific logic of the flow operation upon changing the source (i.e. the element that the outgoing arrow is generated from), create a new module schema replacing the CampaignConnectorManager module in the development package. The procedure for creating a module schema is covered in the “Create a client schema” article. Set the following properties for the created schema:

  • Title – “TestSmsCampaignConnectorManager”.
  • Name – “TestSmsCampaignConnectorManager”.

Add the following source code to the Source code section of the schema:

TestSmsCampaignConnectorManager
define("TestSmsCampaignConnectorManager", [], function() {
 
    Ext.define("Terrasoft.TestSmsCampaignConnectorManager", {
        // Specify replacing of the CampaignConnectorManager module
        override: "Terrasoft.CampaignConnectorManager",
 
        // Add mapping of the name of arrow source campaign element – arrow type (full name)
        initMappingCollection: function() {
            this.callParent(arguments);
            this.connectorTypesMappingCollection.addIfNotExists("TestSmsElementSchema",
                "Terrasoft.ProcessTestSmsConditionalTransitionSchema");
        },
 
        // Virtual method for reload 
        // Arrow processing logic before its sudstitute by an arrow with a new type.
        additionalBeforeChange: function(prevTransition, sourceItem, targetItem) {
            // additional logic here
        },
 
        // Virtual method for reload 
        // Populating specific fields of the created arrow based on the previous arrow.
        fillAdditionalProperties: function(prevElement, newElement) {
            if (newElement.getTypeInfo().typeName === "ProcessTestSmsConditionalTransitionSchema") {
                // Copy the configured responses if the previous arrow is of the same type 
                newElement.testSmsResponseId = prevElement.testSmsResponseId ? prevElement.testSmsResponseId : null;
                // Copy the configuration of response setup
                newElement.isResponseBasedStart = prevElement.isResponseBasedStart
                    ? prevElement.isResponseBasedStart
                    : false;
            }
        }
    });
});

Save the created schema.

7. Connecting the CampaignConnectorManager replacing module 

To connect the module created on the previous step, create a replacing client module and specify BootstrapModulesV2 from the NUI package as the parent object. The procedure of creating a replacing client module is covered in the “Create a client schema“ article.

Add the following source code to the Source code section of the schema:

BootstrapModulesV2
// Set the previously created TestSmsCampaignConnectorManager module as a dependency
define("BootstrapModulesV2", ["TestSmsCampaignConnectorManager"], function() {});

Save the created schema.

Note. During an actual implementation of this case, we recommend creating a separate Bulk SMS object schema. The TestSmsElement and TestSmsConditionalTransitionElement objects will contain the Id of this object and not the SmsText, PhoneNumber … fields. The TestSmsCampaignProcessElement executed element in the Execute() method must contain the logic of adding contacts to the bulk sms audience. A separate mechanism (or several mechanisms) must perform sending of the bulk sms and afterwards record the participants’ responses. Based on these responses, the arrow will transfer the campaign audience to the following campaign step.