Subscribe for expert
tips and access news

Integrating Drupal With dotMailer Part 4: Exporting User Data With Views

Phil Norton   —   6 March 2013   —   Code & Drupal

In previous posts in this series we looked at how to the send the details of one user at a time to dotMailer. It is also possible to send CSV and Excel files to dotMailer via the AddContactsToAddressBookWithProgress method, which can be used to process multiple contacts at once into a single address book. This method returns a progress ID that can be used to progress of the import via the GetContactImportProgress method.

We had the idea that our Drupal site administrators would be able to view and filter a list of users and then push this list over to a specific address book in dotMailer. The obvious solution here was to create a View to allow the filtering functionality and then send this list over to dotMailer via a View display attachment. The advantage here was that any filters added to the parent View would be passed down to the attached Views. It also meant that the same code could be moved from one project to another with only a View needing to be created with the correct fields.

What we did was to integrate into the excellent Views Data Export module, which provides us with the ability to create CSV files. This module was used to generate the potentially large CSV files in a batch process and once finished send this over to dotMailer, rather than sending it to the user.

The Views API is large and complex, so going into it in detail here will detract from the main focus of this post, mainly the dotMailer integration. I will therefore not be posting large blocks of code examples, but if we get enough interest in this module we will look at posting the code somewhere, perhaps even as a module on drupal.org. Also, there is a large amount of interplay between this module and the Views Data Export module, which this module uses for a lot of the CSV generation code.

To integrate a module into Views you need to use a HOOK_views_api() hook to let Views know you want to integrate the module. We called the module we created Views dotMailer Export and so the function names in this example will replicate that.

1
2
3
function views_dotmailer_export_views_api() {
  return array("version" => "3.0");
}

Now that we have told Views about out module we need to tell it that this module is a plugin. This requires the creation of a HOOK_views_plugins() hook function in a file called views_dotmailer_export.views.inc. This function returns an array of options that tells Views what to do with out module and where to find any files that might be included. What we are doing creating a display and a style. This loads in a class called views_dotmailer_export_plugin_display_export, which is involved in controlling what dotMailer address book to use and how to start the CSV creation.

The main method involved in this class is execute(), which is first called when this View display is initiated, but will be run multiple times because we are running the View via a batch process. The first thing we do is make sure that we have a dotMailer address book and that an active batch execution state exists (these both won't exist the first time the method is run). When these checks fail we then display a form that allows the user to select which address book they are wanting to push contacts to. After the address book has been set the batch is run and the CSV file is generated. Here is the execute() method from the views_dotmailer_export_plugin_display_export class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
class views_dotmailer_export_plugin_display_export extends views_plugin_display_feed {
 
  // -- snip --
 
  function execute() {
 
    if (!$this->is_batched()) {
      return parent::execute();
    }
 
    if (isset($_GET['address_book_id'])) {
      $this->address_book_id = $_GET['address_book_id'];
    }
 
    // Try and get a batch context if possible.
    $eid = !empty($_GET['eid']) ? $_GET['eid'] :
            (!empty($this->batched_execution_state->eid) ? $this->batched_execution_state->eid : FALSE);
    if ($eid) {
      $this->batched_execution_state = views_data_export_get($eid);
    } elseif (!isset($_GET['address_book_id'])) {
      module_load_include('php', 'views_export_dotmailer', 'includes/DotMailer');
 
      $display_path = $this->view->display[$this->view->current_display]->handler->get_path();
 
      $form = drupal_get_form('views_export_dotmailer_address_books_selection', $display_path);
 
      drupal_set_title('Please Select An Address Book');
 
      if (count(form_get_errors()) > 0) {
        return $form;
      }
 
      if (!isset($_GET['address_book_id'])) {
        // Allow the user to select from a list of dotmailer address books.
        return $form;
      }
    }
 
    // First time through
    if (empty($this->batched_execution_state)) {
      $output = $this->execute_initial();
    }
 
    // Last time through
    if ($this->batched_execution_state->batch_state == VIEWS_DATA_EXPORT_FINISHED) {
      $output = $this->execute_final();
    }
    // In the middle of processing
    else {
      $output = $this->execute_normal();
    }
 
    //Ensure any changes we made to the database sandbox are saved
    views_data_export_update($this->batched_execution_state);
 
    return $output;
  }
 
  // -- snip --
 
}

The views_export_dotmailer_address_books_selection form that we told Drupal to render in the code above is pretty simple. All we do is grab a list of address books from dotMailer using the ListAddressBooks API method and put them in a radio option form element. We also allow users to create their own address books via a text field.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
function views_export_dotmailer_address_books_selection($form, &$form_state, $display_path) {
  if (!class_exists('DotMailer')) {
    // load the dotMailer class if it hasn't been already
    module_load_include('php', 'views_export_dotmailer', 'includes/DotMailer');
  }
 
  $dotmailer = new DotMailer('apiusername', 'password');
 
  // Get the list of address books
  $address_book_objects = $dotmailer->ListAddressBooks();
 
  $address_books = array();
  foreach ($address_book_objects as $address_book) {
    if ($address_book->Name != 'Test') {
      $address_books[$address_book->ID] = $address_book->Name;
    }
  }
 
  $form['#validate'][] = 'views_export_dotmailer_address_books_selection_validate';
  $form['#submit'][] = 'views_export_dotmailer_address_books_selection_validate_submit';
 
  $markup_text = <<<MARKUP
Select an address book from the current list taken from dotMailer to push
the user details to. You can alternatively enter a new address book name
to create an address book.
MARKUP;
 
  $form['address_book_help'] = array(
    '#markup' => $markup_text
  );
 
  $form['address_book_id'] = array(
    '#type' => 'radios',
    '#title' => t('Pick a current address book:'),
    '#options' => $address_books,
    '#required' => FALSE
  );
 
  $form['new_address_book'] = array(
    '#type' => 'textfield',
    '#title' => t('Alternatively enter a new address book title:'),
    '#required' => FALSE,
    '#maxlength' => 95,
    '#size' => 50
  );
 
  $form['views_display_path'] = array(
    '#type' => 'value',
    '#value' => $display_path
  );
 
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => 'Select'
  );
 
  return $form;
}

Note that the one of the limitations of the dotMailer API is that you can't send data to the 'Test' account. This is the reason why we are checking to each address book name and rejecting any that are called 'Test'. The 'Test' account is a special account used by dotMailer to allow user to test campaigns, but for some reason isn't fully open to the API and so if you try to do anything then an error will occur.

This creates a form that looks like the following.

dotMailer form for Address books

The validation function for this form just checks to see that either an address book ID has been selected or a new address book name has been entered.

1
2
3
4
5
6
7
8
function views_export_dotmailer_address_books_selection_validate($form, &$form_state) {
  if ($form_state['values']['address_book_id'] == '') {
    // if address_book_id hasn't been entered in then make sure new_address_book has.
    if (trim($form_state['values']['new_address_book']) == '') {
      form_set_error('new_address_book', 'Please select an existing address book or enter a new address book title');
    }
  }
}

The submit function for this form will either create a new address book and get the new ID using the CreateAddressBook method or grab the existing address book ID from the radio list. We then rebuild the page arguments along with the new address book ID and then reload the page with the correct address book ID loaded. This allows us to keep any arguments that might have been passed to the view previously.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function views_export_dotmailer_address_books_selection_validate_submit($form, &$form_state) {
  if (isset($form_state['values']['new_address_book']) && trim($form_state['values']['new_address_book'] != '')) {
    $new_address_book_name = $form_state['values']['new_address_book'];
    $dotmailer = new DotMailer('apiusername', 'password');
    $new_address_book = $dotmailer->CreateAddressBook($new_address_book_name);
    $address_book_id = $new_address_book->ID;
  }
  else {
    $address_book_id = $form_state['values']['address_book_id'];
  }
  $get_values = $_GET;
  if (isset($get_values['q'])) {
    unset($get_values['q']);
  }
 
  $arguments = array(
    'query' => array(
      'address_book_id' => $address_book_id
    )
  );
 
  foreach ($get_values as $key => $value) {
    $arguments['query'][$key] = $value;
  }
 
  drupal_goto($form_state['values']['views_display_path'], $arguments);
}

The final step in all of this is to send the newly created CSV file to dotMailer by using the AddContactsToAddressBookWithProgress method. This method returns a progress ID that can be passed to the GetContactImportProgress method to see what the status of the import it. We don't do this here as once the contacts have been sent we assume that they are being processed. We do check to see that the address book we have selected actually exists and error gracefully if it isn't, telling the user what the issue is. There is also a possibility that the request we made to dotMailer failed. In which case the returned progress ID from the AddContactsToAddressBookWithProgress method would be false. If this is the case we print the error message to screen and save it in the site watchdog log. Here is the code of the execute_final() method in full.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function execute_final() {
    // Should we download the file.
    if (!empty($_GET['address_book_id'])) {
      module_load_include('php', 'views_export_dotmailer', 'includes/DotMailer');
 
      $dotmailer = new DotMailer('apiusername', 'password');
 
      $address_book_id = (int) $_GET['address_book_id'];
 
      // Set up the return path.
      $return_path = $this->view->display['default']->handler->get_path();
 
      // Make sure we have a good address book ID.
      $address_book_count = $dotmailer->GetAddressBookContactCount($address_book_id);
 
      if ($address_book_count === FALSE) {
        drupal_set_title('There was a problem with your request');
        return theme('views_dotmailer_export_complete_page', array(
                    'errors' => array('Address book ID not found, please try again.'),
                    'return_url' => $return_path));
      }
 
      $data = file_get_contents($this->outputfile_path());
 
      // now we have rendered the view we need to submit it to dotmailer.
      $dataType = 'CSV';
      $progress_id = $dotmailer->AddContactsToAddressBookWithProgress($address_book_id, $data, $dataType);
 
      if ($progress_id === FALSE) {
        drupal_set_message('DotMailer request failed with the following error message: ' . $dotmailer->getLastFault());
        watchdog('dotmailer', 'DotMailer request failed with the following error message: ' . $dotmailer->getLastFault());
      }
 
      drupal_set_title('Address Book Processed');
 
      return theme('views_dotmailer_export_complete_page', array(
                  'errors' => array(),
                  'return_url' => $return_path));
    }
  }

This is the last post in our Drupal dotMailer integration series. The dotMailer API is pretty extensive and can be used in multiple situations within Drupal and I have only touched on a few examples. If you want to know more about dotMailer and Drupal then leave a comment below and we'll look at writing more posts on the subject. You can also get involved by contributing to the PHP dotMailer class on github.


This blog is part of a series:



Our Partners / Accreditations / Networks

0161 872 3455

Sign up to our newsletter