Skip to content

responsiv/campaign-plugin

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 

Repository files navigation

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:

  • Delayed launch - If this is checked, a date picker will appear and you can select an exact date and time to send the message. This is good for perfectly timing your campaign.

  • Staggered launch - If this is checked, the campaign subscribers will be divided in to smaller groups depending on the selected time period or by a fixed amount of messages. Each group will be contacted every hour until all subscribers have been sent the message.

  • Repeating campaign - This option is particularly useful if you are generating the message content dynamically, for example, a message that contains the latest blog posts on your site. If this is checked, the campaign will duplicate itself after it has launched and automatically apply the Delayed launch option to match the repeating frequency. This will continue perpetually until you manually cancel the most recent iteration.

Campaign status meanings

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

  • Draft - the campaign is still having content added and the design may be changing. This is the only status where content can be modified.

  • Pending - for campaigns that use a delayed launch time, it will stay in this status until the specified launch date has passed. Campaigns in this status are checked every hour.

  • Processing - a list of subscribers is still being built, the campaign will become active when this process completes. For large subscriber lists, this process may take a few minutes.

  • Active - campaign is in progress, there are messages still queued to be sent to the subscribers. Some subscribers may have received the message. Staggered campaigns may stay in this status for some time.

  • Sent - the campaign is complete, all subscribers should have received the message. Information about the subscriber is recorded, such as whether they opened the email or whether they unsubscribed because of this email.

  • Cancelled - the campaign has been cancelled while it was active or before it reached the launch date.

  • Archived - the campaign is now old and doesn't need to be seen any more. All recorded about subscriber activity has been deleted.

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:

  • list - a campaign list code to subscribe the person to. Eg: followers
  • confirm - if this is checked, subscribers must confirm their email address before they are added to the list.

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="[email protected]" />

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

  • responsiv.campaign.listRecipientGroups event handler should return a list of new recipient groups supported by the plugin.
  • responsiv.campaign.getRecipientsData event handler "resolves" a recipient group information and returns the actual email and name of each subscriber.

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' => [
        '[email protected]' => ['first_name' => 'Some', 'last_name' => 'Person'],
        '[email protected]' => ['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:

  • tag: The name of the tag, used in the content editor like {tag_name}.
  • value: The value for this tag. Must be a string, but may be a closure that returns a string. The closure gets a parameter $tagData which includes the subscriber and message object and may be extended (see below).
  • preview: The value that is used for generating the message preview. Must be a string, but may be a closure, just like value.
  • description: A description for the tag, which is display as a tooltip on the tag legend in the content editor.

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',
        ],
    ];
}

About

[PREMIUM] Send professional campaign messages to your subscribers.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published