Activity and Operation Modules

This document provides an overview of concepts that developers should know when developing Activity and Operation modules.


Every Activity must include at least one Operation and at least one Job.

Naming Conventions (Activity)


  • The name parameter should be of the form [application name or vendor name]/activity-[bundle purpose].

    Example: campaignchain/activity-twitter

  • The type parameter should be ‘campaignchain-activity’.


  • The name of an Activity module should follow the convention [application name or vendor name]-[channel name]-[bundle purpose].

    Example: campaignchain-twitter-update-status

Naming Conventions (Operation)


  • The name parameter should be of the form [application name or vendor name]/operation-[bundle purpose].

    Example: campaignchain/operation-twitter

  • The type parameter should be ‘campaignchain-operation’.


  • The name of an Operation module should follow the convention [application name or vendor name]-[channel name]-[operation descriptor].

    Example: campaignchain-twitter-update-status

Linking Activities and Channels

The Activity bundle’s campaignchain.yml file should contain a channels parameter, which specifies the link between the Channel and the Activity. The channels parameter should be of the form [channel bundle name]/[channel module name]

Example: ‘acme/channel-linkedin/acme-linkedin’ refers to the Channel bundle ‘acme/channel-linkedin’ and the Channel module within it named ‘acme-linkedin’.

Wizards and Routes

CampaignChain provides a set of “wizards” that ease integration of your module into the CampaignChain GUI. The Activity Wizard takes care of presenting the user with a form that lists available campaigns, channels and operations. Based on the user’s selection in the form, the Activity Wizard is able to retrieve and display the available Operations for the selected Channel and link it to the selected Campaign.

The Activity Wizard can be invoked from within a controller using the service identifier ‘campaignchain.core.activity.wizard’ as shown below.

// invoke and use activity wizard
$wizard = $this->get('campaignchain.core.activity.wizard');
$campaign = $wizard->getCampaign();
$activity = $wizard->getActivity();

The routes and display name used by the Activity Wizard are obtained from the module configuration in the Activity bundle’s campaignchain.yml file:

      display_name: 'Share News'
          - campaignchain/channel-linkedin/campaignchain-linkedin
          new: campaignchain_activity_linkedin_share_news_item_new
          edit: campaignchain_activity_linkedin_share_news_item_edit
          edit_modal: campaignchain_activity_linkedin_share_news_item_edit_modal
          edit_api: campaignchain_activity_linkedin_share_news_item_edit_api
          campaignchain-due: true

Activities with a Single Operation

If an Activity has only one Operation, this should be made explicit by calling the Activity object’s setEqualsOperation() method, as shown below:

$wizard = $this->get('campaignchain.core.activity.wizard');
$activity = $wizard->getActivity();

Operation Form

When a user defines a new operation, CampaignChain renders a form with fields appropriate to that operation. This form must be included within the Operation module. The easiest way to create this form is by using Symfony’s Form component and FormBuilder interface to define a Form object, as shown below:

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class ShareNewsItemOperationType extends AbstractType

    public function buildForm(FormBuilderInterface $builder, array $options)
            ->add('message', 'text', array(
                'property_path' => 'message',
                'label' => 'Message',
                'attr' => array(
                    'placeholder' => 'Add message...',
                    'max_length' => 200

            ->add('submitUrl', 'text', array(
                'property_path' => 'linkUrl',
                'label' => 'URL of page being shared',
                'attr' => array(
                    'placeholder' => 'Add URL...',
                    'max_length' => 255

        // ... and so on //


This Form object can then be used within controller action methods to create or edit a new operation, as shown below:

$activityType = $this->get('campaignchain.core.form.type.activity');
$shareNewsItemOperation = new ShareNewsItemOperationType(
   $this->getDoctrine()->getManager(), $this->get('service_container')
$operationForms[] = array(
   'identifier' => self::OPERATION_IDENTIFIER,
   'form' => $shareNewsItemOperation,
   'label' => 'LinkedIn Message',

$form = $this->createForm($activityType, $activity);

if ($form->isValid()) {
      // process input

Operation Module and Service

The Activity object’s addOperation() method should be passed an Operation object representing the operation to be added to the activity. CampaignChain’s Operation service can be used to retrieve the correct Operation module, using the Operation bundle name and module identifier. The Operation service is available using the identifier ‘campaignchain.core.operation’.

$operationService = $this->get('campaignchain.core.operation');
$operationModule = $operationService->getOperationModule(
  'campaignchain/operation-linkedin', 'campaignchain-linkedin-share-news-item'

$operation = new Operation();

In this example, the Operation service finds the Operation module named ‘campaignchain-linkedin-share-news-item’ in the bundle named ‘campaignchain/operation-linkedin’.


Every Operation module should include a Job, which actually executes the operation. This Job should implement the JobServiceInterface, which mandates an execute() method that is called when the job is executed. The Job is invoked by the CampaignChain scheduler when an Operation becomes due; it can also be invoked manually to execute an operation immediately.


To process the hooks associated with an Activity, CampaignChain makes a Hook service available, via the service name ‘campaignchain.core.hook’. Call this service’s processHooks() method to process the hooks for an Activity, as shown below:

$hookService = $this->get('campaignchain.core.hook');
$activity = $hookService->processHooks(
  self::BUNDLE_NAME, self::MODULE_IDENTIFIER, $activity, $data

Operations and Locations

An Operation can create a new Location as its end result and/or it can include CTAs that point to Locations. For example, a LinkedIn news sharing Operation would create a new Location - a Linkedin status message that is directly accessible via a unique URL.

When you create a new Activity within CampaignChain, your Operation should also create a Location entry. At the time the Activity is created, the Location entry will necessarily be incomplete as the URL to the Location will not be known.

Once the Operation is executed, the Job that executes it must update the Location with the URL. It must also change the Location’s status from ‘STATUS_UNPUBLISHED’ to ‘STATUS_ACTIVE’.

It’s important to define the owns_location parameter in the Operation module’s campaignchain.yml file as shown below:

      display_name: 'Share News'
      owns_location: true
          job: campaignchain.operation.linkedin.job.share_news_item