responsiv / campaign-plugin

[PREMIUM] Send professional campaign messages to your subscribers.
http://octobercms.com/plugin/responsiv-campaign
5 stars 2 forks source link

Campaign plugin

This plugin allows you to create and design fully custom email campaigns and send them to end users. Campaigns can be sent to users who can sign up to mailing lists using a front-end component, or as part of a predefined collection of recipients, for example, all front-end users.

This table describes the moving parts found in this plugin:

Object Description
Campaign A copy of the message sent to the subscriber.
Campaign Template The HTML scaffolding template used by a Campaign.
Subscriber An individual person, their email address and name.
Subscriber List A mailing list that subscribers can opt-in to.
Recipient Group Like a List but emails are generated by the system, for example All registered users.

Launching a campaign

When you create a campaign for the first time, it will ask if you want to generate a default campaign template in the current theme. This will provide some sample content you can use to get started.

After naming your campaign, you are free to populate the message content as many times are you like. You can also preview the message by clicking the Send test message button.

Once you are ready to send the message click the Launch campaign button where you can select which subscribers should receive the campaign message. There are also several launch options available:

Campaign status meanings

A campaign will have many statuses through its life time, they are described in detail below:

System requirements

This plugin relies on the system schedule process for running its automated tasks. You should ensure that your cron table is configured correctly for this plugin to work. Alternatively you can manually process the campaign logic by calling php artisan campaign:run and run it every 5-10 minutes.

Cron without command line

Some hosting providers will not allow direct access to the cron table, instead they will allow you to call a HTTP endpoint every few minutes. To solve the problem, you can create a custom plugin and call the command using the Artisan Facade. Create a routes.php file in the root folder of the plugin with the route:

use Route;
use Artisan;

Route::get('/campaign_run', function () {
    return Artisan::call('campaign:run');
});

Implementing front-end subscription form

Use the campaignSignup component to display a subscription form on a page. The component has the following properties:

Here is some sample markup:

title = "My page"
url = "/mypage"

[campaignSignup]
list = "followers"
confirm = 0
==
<div id="container">
    <form
        data-request="campaignSignup::onSignup"
        data-request-update="'campaignSignup::result': '#container'">

        <!-- Optional first name -->
        <input name="first_name" type="text" placeholder="First name" />

        <!-- Optional last name -->
        <input name="last_name" type="text"  placeholder="Last name" />

        <!-- Required email address -->
        <input name="email" type="text"  placeholder="john.smith@example.com" />

        <button class="btn btn-default" type="submit">Subscribe</button>

    </form>
</div>

Campaign Templates

Campaign Templates are managed in the CMS area and should be designed by a developer. Each template should use the campaignTemplate CMS Component and like any CMS page, they can support using other components for dynamically generating content.

title = "Default template"
url = "/campaign/message/:code"
description = "Campaign with basic fields"

[campaignTemplate]
==
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">

    <html lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>Newsletter</title>
    </head>

    <body>
        {richeditor name="body" size="huge"}{/richeditor}
    </body>
</html>

There is a template build syntax, this lets you add form fields inside the template where the content can be added via the Campaign creation process. These tags are supported:

Text

Renders a single line editor field for smaller blocks of text.

{text name="websiteName" label="Website Name"}Our wonderful website{/text}

Textarea

Renders a multiple line editor field for larger blocks of text.

{textarea name="websiteDescription" label="Website Description"}This is our vision for things to come{/textarea}

Rich editor

Renders a WYSIWYG content editor.

{richeditor name="content" label="Main content"}Default text{/checkbox}

Markdown

Renders a Markdown content editor.

{markdown name="content" label="Markdown content"}Default text{/markdown}

File Upload

Renders a file upload editor field, the output value is the full path to the file.

{fileupload name="logo" label="Logo"}http://placehold.it/100x100{/fileupload}

Repeater

Renders a repeating section with other fields inside.

{repeater name="content_sections" prompt="Add another content section"}
    <h2>{text name="title" label="Title"}Title{/text}</h2>
    <p>{textarea name="content" label="Content"}Content{/textarea}</p>
{/repeater}

For more details on syntax fields, see the Parser section of the October documentation.

Creating new recipient groups

Plugins can extend the Campaign plugin with new recipient groups. These are like predefined subscriber lists that list their subscribers dynamically by the system. New groups are registered with the API events triggered by the Campaign plugin. The event handlers should be defined in the boot() method of the plugin registration file. There are three events that should be handled in the plugin.

The next example shows an event handler registration code for a plugin. The plugin registers two recipient groups. As you can see, the plugin uses the Customer class to handle the events. That's a recommended approach.

public function boot()
{
    Event::listen('responsiv.campaign.listRecipientGroups', function() {
        return [
            'my-plugin-all-customers' => 'All customers',
            'my-plugin-paid-customers' => 'Customers with paid orders',
        ];
    });

    Event::listen('responsiv.campaign.getRecipientsData', function($type) {
        if ($type == 'my-plugin-all-customers')
            return Customer::getAllCustomers();

        if ($type == 'my-plugin-paid-customers')
            return Customer::getPaidCustomers();
    });
}
Registering new recipient groups

New recipient groups can be registered with the responsiv.campaign.listRecipientGroups event handler. The handler should return an associative array with the type codes in indexes and the names in values. It is highly recommended to use the plugin name for the type code, to avoid conflicts with other plugins. For example:

[
    'my-plugin-recipient-type' => 'My plugin recipient type',
]
Returning information about an recipient collection

Plugins should provide detailed information about the supported recipient types with the responsiv.campaign.getRecipientsData event handlers. The handler gets a single parameter - the recipient type code (one of the codes you registered with the responsiv.campaign.listRecipientGroups handler). The handler code must check whether the requested item type code belongs to the plugin. The handler should return an associative array in the following format:

[
    'recipients' => [
        'greatguy@domain.tld' => ['first_name' => 'Some', 'last_name' => 'Person'],
        'coolgal@domain.tld' => ['first_name' => 'Another', 'last_name' => 'Person'],
        // ...
    ]
]
Extending the unsubscribe event

You may hook the responsiv.campaign.beforeUnsubscribe event if you want to use your own logic called when a user unsubscribes. This event can override the default behavior by returning a response, which is then passed to the user's browser.

Event::listen('responsiv.campaign.beforeUnsubscribe', function(
    $component,
    $subscriber,
    $campaign
) {
    // ...
});

Capturing Extra Data / Meta Data

To capture meta data make sure the metaData property is enabled in the Signup component.

[campaignSignup]
metaData = 1

Then simply customize the component markup and pass custom data to meta[] HTML input array.

<form data-request="onSignup">
    <!-- ... -->

    <label>
        <input type="radio" name="meta[interest]" value="Sport" />
        Sport
    </label>
    <label>
        <input type="radio" name="meta[interest]" value="Games" />
        Games
    </label>
    <label>
        <input type="radio" name="meta[interest]" value="Dancing" />
        Dancing
    </label>

    <label>
        <input type="checkbox" name="meta[tell_me_more]" value="Yes" />
        Tell me more
    </label>

    <!-- ... -->
    <button type="submit">Subscribe</button>
</form>

The captured meta data will then be shown next to each subscriber in the back-end. Note: The column is invisible in the list view by default.

Extend tags available in the content editor

Plugins may extend the list of available tags that can be used in the editor, by registering the tag with the TagManager. Create a function called registerMailCampaignTags() in your Plugin.php and return an array of tags to use in the content editor.

The following attributes are available:

Example:

public function registerMailCampaignTags()
{
    return [
        'occupation' => [
            'value' => function ($tagData) {
                return $tagData->subscriber->meta_data['occupation']
            },
            'preview' => 'Web Developer',
            'description' => 'The subscribers occupation.',
        ],
        'city' => [
            'value' => function ($tagData) {
                return $tagData->subscriber->meta_data['city']
            },
            'preview' => 'Milan',
            'description' => 'The city where the subscriber lives.',
        ],
    ];
}

If you need to fetch the same data in the closures, you may also listen to the responsiv.campaign.extendTagdata event and extend the available tag data. This helps you keep the code a bit cleaner, if you have many different tags that access the same data.

When the event is fired, it provides a reference to the $tagData to which you may add your data objects. Dont forget the ampersand (&) in front of the $tagData parameter to get it by reference and add your data to it.

public function boot()
{
    Event::listen('responsiv.campaign.extendTagdata', function(&$tagData) {
        if ($tagData->subscriber && $tagData->subscriber->email) {
            $user = User::getByEmail($tagData->subscriber->email);
            if ($user) {
                $tagData->ordersQuery = $user->orders();
                $tagData->orders = $user->orders;
            }
        }
    });
}

public function registerMailCampaignTags()
{
    return [
        'latest_order_total' => [
            'value' => function ($tagData) {
                if (!$tagData->ordersQuery) {
                    return null;
                }
                return $tagData->ordersQuery->lastByOrderingDate()->presenter()->renderMoneyTotal();
            },
            'preview' => '[ Total ]',
            'description' => 'The total from the last order',
        ],
        'latest_products_ordered' => [
            'value' => function ($tagData) {
                if (!$tagData->orders) {
                    return null;
                }
                return count($tagData->orders) > 0
                    ? $tagData->ordersQuery->lastByOrderingDate()->presenter()->renderForCampaign()
                    : $tagData->orders->presenter()->renderEmptyOrderListForCampaign();
            },
            'preview' => '[ Products ]',
            'description' => 'List of products form the last order',
        ],
    ];
}