Skip to main content

Sitecore 9 - Creating Custom Marketing Automation Activity Part-2

I am going to build user interface part of custom activity. As we saw in previous post, we have a parameter called "ProcessLimit" in activity implementation. So, we should provide a UI to marketing managers that can set it from marketing automation panel.

I don't want to repeat the official documentation, I will only show some key parts.

I started with creating package.json file. When you follow official documentation, you see that you need those packages as required.

  • @angular/core: 5.2.11
  • @angular/http: 5.2.11
  • @ngx-translate/core: 9.0.2
  • rxjs: 5.5.11
You will get warning in terminal if there is any package missing, I added step by step and my final package.json file was like this:

{
  "name": "package",
  "version": "1.0.0",
  "description": "Playground Demo",
  "private": true,
  "angular-cli": {},
  "scripts": {
    "dev": "ngc -p ./src/tsconfig.aot.json && npm run build",
    "build": "webpack --display-error-details"
  },
  "dependencies": {
    "@angular/common": "5.2.11",
    "@angular/compiler": "7.2.4",
    "@angular/compiler-cli": "^7.2.4",
    "@angular/core": "5.2.11",
    "@angular/forms": "5.2.11",
    "@angular/http": "5.2.11",
    "@angular/platform-browser": "5.2.11",
    "@ngx-translate/core": "^9.0.2",
    "@sitecore/ma-core": "file:C:/Playground/Playground.Angular/MarketingAutomation/packages/ma-core",
    "rxjs": "^5.5.11",
    "typescript": "3.2.4",
    "zone.js": "^0.8.4"
  },
  "devDependencies": {
    "ts-loader": "^5.3.3",
    "webpack": "^4.29.3",
    "webpack-cli": "^3.2.3"
  },
  "main": "index.js",
  "author": "",
  "license": "ISC"
}

An important dependency is "@sitecore/ma-core". Sitecore provides a package to use as base and build our UI. We need to give the ma-core path which inside MarketingAutomation folder.

The folder structure is below, you can see an overview what kind of files you will have at the end.


Another important thing is id in plugin.ts file. The Sitecore id of created activity type must be lower case and without curly brackets.

import { Plugin } from '@sitecore/ma-core';
import { SendMoviesToExpressActivity }  from './send-movies-to-express.activity';
import { EditorComponent } from '../codegen/editor/editor.component';
import { SendMoviesToExpressModuleNgFactory } from '../codegen/send-movies-to-express-module.ngfactory'

@Plugin({
    activityDefinitions: [ 
        {
            // The ID must match the ID of the activity type description definition item in the CMS.
            // Must be lowercase and without curly brackets
        id: 'c61c5489-b11b-40f1-ab36-23eb044c2e69',
            activity: SendMoviesToExpressActivity,
            editorComponenet: EditorComponent,
            editorModuleFactory: SendMoviesToExpressModuleNgFactory
        }
    ]
})
export default class SendMoviesToExpressPlugin {}

You will see the activity component implementation below. Remember that we defined parameter name as ProcessLimit in code. Here, it must start with lowercase "processLimit".

import { SingleItem } from '@sitecore/ma-core';

export class SendMoviesToExpressActivity extends SingleItem {

    getVisual(): string {
        const subTitle = this.isDefined ? 'Process Count: ' + this.editorParams.processLimit : '';
        const cssClass = this.isDefined ? '' : 'undefined';

        //CSS class can either be makreting-action or other-element
        return `
            <div class="viewport-readonly-editor marketing-action ${cssClass}">
                <span class="icon">
                    <img src="/~/icon/OfficeWhite/32x32/arrow_right.png" />
                </span>
                <p class="text with-subtitle" title="Send movies">
                    Send movies
                    <small class="subtitle" title="${subTitle}">${subTitle}</small>
                </p>
            </div>
        `;
    }

    get isDefined(): boolean {
        /*
        The editorParams propery value is the object that is serialized from the activity editor.
        If the activity is undefined the editorParams propery will evaluate to {}
        */
        return Boolean(this.editorParams.processLimit);
    }
}

Editor component has an input field to set our parameter.

import { Component, OnInit, Injector } from '@angular/core';
import { EditorBase } from '@sitecore/ma-core';
 
@Component({
    selector: 'readonly-editor',
            template: `
        <section class="content">
            <div class="form-group">
                <div class="row readonly-editor">
                    <label class="col-6 title">Process Limit</label>
                    <div class="col-6">
                        <input type="number" class="form-control" [(ngModel)]="processLimit"/>
                   </div>
                </div>
            </div>
        </section>
    `,
    //CSS Styles are ommitted for brevity
    styles: ['']
})
 
export class EditorComponent extends EditorBase implements OnInit {

    processLimit: number;

    constructor(private injector: Injector) {
        super();
    }
 
    ngOnInit(): void { 
        this.processLimit = this.model ? this.model.processLimit || 0 : 0;
    }

    serialize(): any {
        return {  
            processLimit: this.processLimit
        };
    }
}

Once you are done with components. you can build your app with "npm run dev" command. If there is no error, you will get an bundled js file which include all necessary things.

There are two things left to make it work at this moment. First, I copied this js file and pasted under plugins folder (C:\inetpub\wwwroot\yourwebsite\sitecore\shell\client\Applications\MarketingAutomation\plugins). Then, added a configuration file under App_Config/Include folder to register js file.

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
  <sitecore role:require="Standalone or ContentManagement or DedicatedDispatch">
    <marketingAutomation>
    <pluginDescriptorsRepository>
      <plugins>
        <!-- Assuming that the plugin bundle file name is sample.plugin.js and is deployed directly under the plugins folder -->
        <plugin path="./plugins/send-movies.plugin.js" />
      </plugins>
    </pluginDescriptorsRepository>
    </marketingAutomation>
  </sitecore>
</configuration>

At the end my custom action looked like this:


That's it! You can test and you can debug your code as I showed in previous posts. 

Comments

Popular posts from this blog

Sitecore 9 - Custom Page Events & Filtering with XConnect

This is the first article of a series. I am going to start with creating a custom page event and will show how we can fetch event data using xconnect api. Let's start with reminding demo scenario: Imagine that you have a website displaying movies. Visitors are able to see movie details and take some actions like save movie or share it.  You want to follow the visitors' activities and you want to take some marketing actions based on those activities. For example, if a contact visits a movie more than X time or she/he saves a movie, you want to send those movies to an external system. In addition, there is going to be a limit to send same movie. Such as, it will not be possible to send same movie more than 2 times.  You want to configure this as a marketing automation plan to give flexibility to your marketing managers. They should be able to add configurable rules and activities.  My first focus is movie detail page. I want to track visitors when they visit the

A React Example in Sitecore

That's true, Sitecore components are useful and provide lots of facilities. They allow to build fully flexible website. And there is not much problem using post-backs to display your contents. However, if your website has user interactions, then it could be a headache. Too many post-backs would occur a poor user experience. That's why client-side technologies are really powerful and popular for a long time. There are plenty of javascript libraries and frameworks which all try to provide better user experience in different ways. It is not a topic of this article why I chose React. There are some good articles about it (e.g. Jonne Kats' great article ). However, one of the reasons is React only focus on view part. It is not a fully framework like Angular, it is rich featured library instead. And that's what I was looking for! The idea was to benefit Sitecore server-side and React client-side power. It seems they are very good match! Let's dive into code... I&#

Sitecore 9 - Creating Custom Segmentation Rule

Predicates are important to create rules for marketing automation and segmented lists. Before going through the details, I am going to remind what I am implementing again. Imagine that you have a website displaying movies. Visitors are able to see movie details and take some actions like save movie or share it.  You want to follow the visitors' activities and you want to take some marketing actions based on those activities. For example, if a contact visits a movie more than X time or she/he saves a movie, you want to send those movies to an external system. In addition, there is going to be a limit to send same movie. Such as, it will not be possible to send same movie more than 2 times.  According to my scenario, I need a rule to filter contacts who visits a movie more than X time. Maybe, we can add one more condition there like in Y days. To creating rule will give flexibility to marketing managers that then they can easily add or remove multiple rules and proceed the