You are here

mailchimp_ecommerce_commerce.module in Mailchimp E-Commerce 7

Integrates Drupal Commerce with Mailchimp eCommerce.

File

modules/mailchimp_ecommerce_commerce/mailchimp_ecommerce_commerce.module
View source
<?php

/**
 * @file
 * Integrates Drupal Commerce with Mailchimp eCommerce.
 */

/**
 * Implements hook_form_FORM_ID_alter().
 */
function mailchimp_ecommerce_commerce_form_mailchimp_ecommerce_admin_settings_alter(&$form, &$form_state) {

  // Set default currently from Commerce.
  $form['mailchimp_ecommerce_currency']['#default_value'] = commerce_default_currency();
  $form['product'] = [
    '#type' => 'fieldset',
    '#title' => t('Products'),
    '#description' => t("This information is used to determine the URLs of products on this website. if a product is referenced by a node, that node's URL will be sent to Mailchimp as part of the product information."),
    '#collapsible' => FALSE,
  ];
  $node_type_options = $node_field_options = [
    '' => '-- Select --',
  ];
  $image_field_options = $image_styles = [
    '' => '-- Select --',
  ];
  $description_field_options = [
    '' => '-- Select --',
  ];
  $node_types = node_type_get_names();
  foreach ($node_types as $node_type => $node_name) {
    $node_type_options[$node_type] = $node_name;
  }
  $product_node_type = variable_get('mailchimp_ecommerce_product_node_type', '');
  $form['product']['mailchimp_ecommerce_product_node_type'] = [
    '#type' => 'select',
    '#options' => $node_type_options,
    '#title' => t('Product content type'),
    '#description' => t('Select the content type used to display products on your site.'),
    '#default_value' => $product_node_type,
    '#ajax' => [
      'callback' => 'mailchimp_ecommerce_commerce_admin_settings_form_ajax_callback',
      'wrapper' => 'node-field-wrapper',
    ],
  ];
  if (empty($product_node_type)) {
    $product_node_type = !empty($form_state['values']['mailchimp_ecommerce_product_node_type']) ? $form_state['values']['mailchimp_ecommerce_product_node_type'] : NULL;
  }
  $product_image_types = [];

  // List product reference fields.
  if (!empty($product_node_type)) {
    $field_info = field_info_instances('node', $product_node_type);
    if (!empty($field_info)) {
      foreach ($field_info as $field_name => $properties) {

        // Only list product reference fields.
        if (isset($properties['settings']['referenceable_types'])) {
          $node_field_options[$field_name] = $properties['label'];

          // Track referenceable product variation types for this field.
          $product_image_types += array_filter($properties['settings']['referenceable_types']);
        }
        if (isset($properties['settings']['text_processing'])) {
          $description_field_options[$field_name] = $properties['label'];
        }

        // Include image fields from product display types.
        $field = field_info_field($field_name);
        if ($field['type'] == 'image' || $field['type'] == 'file') {
          $image_field_options[$field['field_name']] = t('@label (product display)', array(
            '@label' => $properties['label'],
          ));
        }
      }
    }
  }
  $form['product']['mailchimp_ecommerce_product_node_field'] = [
    '#type' => 'select',
    '#options' => $node_field_options,
    '#title' => t('Product reference field'),
    '#description' => t('Select the product reference field used on the above content type.'),
    '#default_value' => variable_get('mailchimp_ecommerce_product_node_field', ''),
    '#prefix' => '<div id="node-field-wrapper">',
    '#suffix' => '</div>',
  ];

  // Since each product variant can have its own image, inform Mailchimp
  // about any image fields that exist on our enabled product types.
  foreach ($product_image_types as $product_image_type) {
    foreach (field_info_instances('commerce_product', $product_image_type) as $field_name => $properties) {
      $field = field_info_field($field_name);
      if ($field['type'] == 'image' || $field['type'] == 'file') {
        $image_field_options[$field['field_name']] = $properties['label'];
      }
    }
  }
  $form['product']['mailchimp_ecommerce_commerce_image_field'] = [
    '#type' => 'select',
    '#options' => $image_field_options,
    '#title' => t('Product image field'),
    '#description' => t('Select the image field used on the above content type.'),
    '#default_value' => variable_get('mailchimp_ecommerce_commerce_image_field', ''),
    '#prefix' => '<div id="node-field-wrapper">',
    '#suffix' => '</div>',
  ];
  $image_styles = image_style_options(FALSE);
  $form['product']['mailchimp_ecommerce_commerce_image_style'] = [
    '#type' => 'select',
    '#empty_option' => t('None (original image)'),
    '#multiple' => FALSE,
    '#options' => $image_styles,
    '#title' => t('Image style'),
    '#description' => t('The image style to use when sending product images to Mailchimp.'),
    '#default_value' => variable_get('mailchimp_ecommerce_commerce_image_style', ''),
  ];
  $form['product']['mailchimp_ecommerce_description_field'] = [
    '#type' => 'select',
    '#options' => $description_field_options,
    '#title' => t('Description textfield'),
    '#description' => t('Select the description text field used on the above content type.'),
    '#default_value' => variable_get('mailchimp_ecommerce_description_field', ''),
    '#prefix' => '<div id="node-field-wrapper">',
    '#suffix' => '</div>',
  ];

  // If Stock modules are detected, show the admin some options for
  // handling inventory quantities.
  // @todo: Allow for any other known Stock modules.
  if (module_exists('commerce_stock')) {
    $form['stock'] = [
      '#type' => 'fieldset',
      '#title' => t('Inventory'),
      '#description' => t("Stock module detected. The Fixed Inventory Amount will be used for any products that do not have stock fields configured."),
      '#collapsible' => FALSE,
    ];
    $form['stock']['mailchimp_ecommerce_use_fixed_inventory'] = [
      '#type' => 'checkbox',
      '#title' => t('Use fixed inventory'),
      '#description' => t('Enable to set the Fixed Inventory Amount for all products regardless of stock availability.'),
      '#default_value' => variable_get('mailchimp_ecommerce_use_fixed_inventory', FALSE),
    ];
    $form['stock']['mailchimp_ecommerce_fixed_inventory_amount'] = [
      '#type' => 'textfield',
      '#title' => t('Fixed inventory amount'),
      '#default_value' => variable_get('mailchimp_ecommerce_fixed_inventory_amount', 1),
    ];
  }

  // Identify Drupal Commerce to Mailchimp.
  $form['platform']['#default_value'] = 'Drupal Commerce';

  // Sync historical orders
  $form['sync']['orders'] = [
    '#markup' => '<br>' . l(t('Sync historical orders to MailChimp'), 'admin/config/services/mailchimp/ecommerce/sync-orders'),
  ];

  // Validate our settings.
  $form['#validate'][] = 'mailchimp_ecommerce_commerce_admin_validate';
}

/**
 * Validation handler for our admin settings form.
 */
function mailchimp_ecommerce_commerce_admin_validate(&$form, &$form_state) {

  // Make sure the inventory amount is an integer.
  $amount = isset($form_state['values']['mailchimp_ecommerce_fixed_inventory_amount']) ? $form_state['values']['mailchimp_ecommerce_fixed_inventory_amount'] : '';
  if (!empty($amount) && !is_numeric($amount) && !is_int($amount)) {
    form_set_error('mailchimp_ecommerce_fixed_inventory_amount', 'Inventory amount must be an integer.');
    return FALSE;
  }

  // If a blank string was passed in, reset the variable to our default.
  if ($amount == '') {
    $form_state['values']['mailchimp_ecommerce_fixed_inventory_amount'] = 1;
  }
}

/**
 * Callback for AJAX request triggered by checkbox to toggle advanced UI on/off.
 */
function mailchimp_ecommerce_commerce_admin_settings_form_ajax_callback($form, &$form_state) {
  return $form['product']['mailchimp_ecommerce_product_node_field'];
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function mailchimp_ecommerce_commerce_form_mailchimp_ecommerce_admin_sync_alter(&$form, &$form_state) {
  $form['#submit'][] = 'mailchimp_ecommerce_commerce_admin_sync_submit';
}

/**
 * Submit handler for the Mailchimp eCommerce sync form.
 */
function mailchimp_ecommerce_commerce_admin_sync_submit($form, &$form_state) {
  if (!empty($form_state['values']['sync_products'])) {
    $batch = [
      'title' => t('Adding products to Mailchimp'),
      'operations' => [],
    ];
    $query = new EntityFieldQuery();
    $result = $query
      ->entityCondition('entity_type', 'commerce_product')
      ->addTag('mailchimp_ecommerce_commerce_product_sync')
      ->execute();
    if (isset($result['commerce_product'])) {
      $product_ids = array_keys($result['commerce_product']);
      $batch['operations'][] = [
        'mailchimp_ecommerce_commerce_batch_add_products',
        [
          $product_ids,
        ],
      ];
    }
    batch_set($batch);
  }
}

/**
 * Batch callback used to add products to Mailchimp.
 */
function mailchimp_ecommerce_commerce_batch_add_products($product_ids, &$context) {
  if (!isset($context['sandbox']['progress'])) {
    $context['sandbox']['progress'] = 0;
    $context['sandbox']['total'] = count($product_ids);
    $context['results']['product_ids'] = $product_ids;
  }
  $batch_limit = variable_get('mailchimp_batch_limit', 100);
  $batch = array_slice($context['results']['product_ids'], $context['sandbox']['progress'], $batch_limit);
  foreach ($batch as $product_id) {
    $product = commerce_product_load($product_id);
    mailchimp_ecommerce_commerce_commerce_product_insert($product);
    $context['sandbox']['progress']++;
    $context['message'] = t('Sent @count of @total products to Mailchimp', [
      '@count' => $context['sandbox']['progress'],
      '@total' => $context['sandbox']['total'],
    ]);
    $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['total'];
  }
}

/**
 * Implements hook_commerce_order_update().
 *
 * When an in-progress cart order changes status, notify
 * Mailchimp if we're configured to do so.
 */
function mailchimp_ecommerce_commerce_commerce_order_update($order) {
  $send_carts = variable_get('mailchimp_ecommerce_send_carts', FALSE);
  $cart_states = array_keys(commerce_order_statuses([
    'state' => 'cart',
  ]));
  $checkout_states = array_keys(commerce_order_statuses([
    'state' => 'checkout',
  ]));

  // Only send a cart if we are in cart or checkout status, and the status has changed.
  if (in_array($order->status, $cart_states) || in_array($order->status, $checkout_states)) {
    if ($send_carts == TRUE && $order->original->status != $order->status) {

      // @todo: Handle admin-created orders.
      _mailchimp_ecommerce_commerce_send_cart($order);
    }
  }
  else {

    // Not a cart; update the order.
    $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
    $mc_order = _mailchimp_ecommerce_commerce_build_order($order_wrapper);
    mailchimp_ecommerce_update_order($order->order_id, $mc_order['order_data']);
  }
}

/**
 * Implements hook_commerce_order_delete().
 */
function mailchimp_ecommerce_commerce_commerce_order_delete($order) {

  // If this order was completed, it is an MC Order.
  $post_checkout = array_keys(commerce_order_statuses([
    'state' => 'completed',
  ]));
  if (in_array($order->status, $post_checkout)) {
    mailchimp_ecommerce_delete_order($order->order_id);
  }
  else {
    mailchimp_ecommerce_delete_cart($order->order_id);
  }
}

/**
 * Implements hook_commerce_cart_product_add().
 */
function mailchimp_ecommerce_commerce_commerce_cart_product_add($order, $product, $quantity, $line_item) {
  $send_carts = variable_get('mailchimp_ecommerce_send_carts', FALSE);

  // Do not send Carts to Mailchimp if we're not configured to.
  if ($send_carts == FALSE) {
    return;
  }

  // If this order is new, that means this is the first line item;
  // send the entire cart to Mailchimp first.
  if ($order->created == $order->changed) {
    _mailchimp_ecommerce_commerce_send_cart($order);
  }
  else {
    $line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item);
    $cart_product = [
      'product_id' => $product->product_id,
      'product_variant_id' => $product->sku,
      'quantity' => (int) $quantity,
      'price' => commerce_currency_amount_to_decimal($line_item_wrapper->commerce_unit_price->amount
        ->value(), $line_item_wrapper->commerce_unit_price->currency_code
        ->value()),
    ];
    mailchimp_ecommerce_add_cart_line($order->order_id, $line_item->line_item_id, $cart_product);
  }
}

/**
 * Implements hook_commerce_cart_product_remove().
 *
 * Removing a product from a shopping cart requires
 * deleting the cartLine from Mailchimp.
 */
function mailchimp_ecommerce_commerce_commerce_cart_product_remove($order, $product, $quantity, $line_item) {
  $send_carts = variable_get('mailchimp_ecommerce_send_carts', FALSE);

  // Do not send Carts to Mailchimp if we're not configured to.
  if ($send_carts == FALSE) {
    return;
  }
  mailchimp_ecommerce_delete_cart_line($order->order_id, $line_item->line_item_id);
}

/**
 * Implements hook_commerce_line_item_update().
 */
function mailchimp_ecommerce_commerce_commerce_line_item_update($line_item) {
  $send_carts = variable_get('mailchimp_ecommerce_send_carts', FALSE);

  // Do not send Carts to Mailchimp if we're not configured to.
  if ($send_carts == FALSE) {
    return;
  }
  $line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item);
  if (in_array($line_item_wrapper->type
    ->value(), commerce_product_line_item_types())) {
    $product = $line_item_wrapper->commerce_product
      ->value();
    $mc_product = [
      'product_id' => $product->product_id,
      'product_variant_id' => $product->sku,
      'quantity' => (int) $line_item->quantity,
      'price' => commerce_currency_amount_to_decimal($line_item_wrapper->commerce_unit_price->amount
        ->value(), $line_item_wrapper->commerce_unit_price->currency_code
        ->value()),
    ];
    mailchimp_ecommerce_update_cart_line($line_item->order_id, $line_item->line_item_id, $mc_product);
  }
}

/**
 * Implements hook_commerce_customer_profile_insert().
 */
function mailchimp_ecommerce_commerce_commerce_customer_profile_insert($customer_profile) {
  $first_name = '';
  $last_name = '';
  if (!($customer_email = _mailchimp_ecommmerce_commerce_get_customer_email($customer_profile))) {
    return;
  }
  $customer_id = _mailchimp_ecommerce_get_local_customer($customer_email);
  $customer_profile_wrapper = entity_metadata_wrapper('commerce_customer_profile', $customer_profile);
  if (isset($customer_profile_wrapper->commerce_customer_address) && !empty($customer_profile_wrapper->commerce_customer_address
    ->value()) && isset($customer_profile_wrapper->commerce_customer_address->first_name, $customer_profile_wrapper->commerce_customer_address->last_name)) {
    $first_name = $customer_profile_wrapper->commerce_customer_address->first_name
      ->value();
    $last_name = $customer_profile_wrapper->commerce_customer_address->last_name
      ->value();
  }
  $customer = [
    'id' => $customer_id,
    'email_address' => $customer_email,
    'first_name' => $first_name,
    'last_name' => $last_name,
    'address' => mailchimp_ecommerce_commerce_parse_customer_profile_address($customer_profile_wrapper),
  ];
  if (mailchimp_ecommerce_get_customer($customer_id)) {
    mailchimp_ecommerce_update_customer($customer);
  }
  else {
    mailchimp_ecommerce_add_customer($customer);
  }

  // Update the cart with the customer information, if customer profile exists.
  if (isset($customer_profile->entity_context['entity_id']) && !empty($customer_profile->entity_context['entity_id'])) {
    $order_id = $customer_profile->entity_context['entity_id'];
    $order = commerce_order_load($order_id);
    $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
    $mc_order = _mailchimp_ecommerce_commerce_build_order($order_wrapper);
    mailchimp_ecommerce_update_cart($order->order_id, $mc_order['customer'], $mc_order['order_data']);
  }
}

/**
 * Implements hook_commerce_customer_profile_update().
 */
function mailchimp_ecommerce_commerce_commerce_customer_profile_update($customer_profile) {
  $first_name = '';
  $last_name = '';
  if (isset($customer_profile->entity_context['entity_id'])) {
    $order_id = $customer_profile->entity_context['entity_id'];
  }
  else {
    return;
  }
  if (!($customer_email = _mailchimp_ecommmerce_commerce_get_customer_email($customer_profile))) {
    return;
  }
  $customer_id = _mailchimp_ecommerce_get_local_customer($customer_email);
  $customer_profile_wrapper = entity_metadata_wrapper('commerce_customer_profile', $customer_profile);
  if (isset($customer_profile_wrapper->commerce_customer_address) && !empty($customer_profile_wrapper->commerce_customer_address
    ->value()) && isset($customer_profile_wrapper->commerce_customer_address->first_name, $customer_profile_wrapper->commerce_customer_address->last_name)) {
    $first_name = $customer_profile_wrapper->commerce_customer_address->first_name
      ->value();
    $last_name = $customer_profile_wrapper->commerce_customer_address->last_name
      ->value();
  }
  $customer = [
    'id' => $customer_id,
    'email_address' => $customer_email,
    'first_name' => $first_name,
    'last_name' => $last_name,
    'address' => mailchimp_ecommerce_commerce_parse_customer_profile_address($customer_profile_wrapper),
  ];
  if (mailchimp_ecommerce_get_customer($customer_id)) {
    mailchimp_ecommerce_update_customer($customer);
  }
  else {
    mailchimp_ecommerce_add_customer($customer);
  }

  // Update the order with the customer information.
  $order = commerce_order_load($order_id);
  $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
  $mc_order = _mailchimp_ecommerce_commerce_build_order($order_wrapper);

  // TODO: Restore when this function supports orders that are not carts.

  //mailchimp_ecommerce_update_cart($order->order_id, $mc_order['customer'], $mc_order['order_data']);
}

/**
 * Implements hook_commerce_customer_profile_delete().
 */
function mailchimp_ecommerce_commerce_commerce_customer_profile_delete($customer_profile) {
}

/**
 * Implements hook_commerce_cart_order_refresh().
 */
function mailchimp_ecommerce_commerce_commerce_cart_order_refresh($order_wrapper) {
}

/**
 * Helper to pull the customer email address from a profile.
 */
function _mailchimp_ecommmerce_commerce_get_customer_email($customer_profile) {
  $customer_email = '';
  if ($customer_profile->uid != 0) {
    $account = user_load($customer_profile->uid);
    $customer_email = $account->mail;
  }
  else {
    if (!empty($customer_profile->entity_context['entity_id'])) {
      $order_id = $customer_profile->entity_context['entity_id'];
      $order = commerce_order_load($order_id);
      if ($order) {
        $customer_email = $order->mail;
      }
    }
    elseif (!empty($_POST['account']['login']['mail'])) {
      $customer_email = check_plain($_POST['account']['login']['mail']);
    }
  }
  return $customer_email;
}

/**
 * Implements hook_commerce_checkout_complete().
 */
function mailchimp_ecommerce_commerce_commerce_checkout_complete($order) {
  $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
  $mc_order = _mailchimp_ecommerce_commerce_build_order($order_wrapper);

  // Convert cart to order in Mailchimp.
  mailchimp_ecommerce_delete_cart($order->order_id);
  mailchimp_ecommerce_add_order($order->order_id, $mc_order['customer'], $mc_order['order_data']);
}

/**
 * Implements hook_node_insert().
 *
 * When a node gets created, send it to Mailchimp.
 */
function mailchimp_ecommerce_commerce_node_insert($node) {
  $product_node_type = variable_get('mailchimp_ecommerce_product_node_type', '');
  if (!empty($product_node_type) && $node->type == $product_node_type) {

    // See if the configured product field exists.
    $field = variable_get('mailchimp_ecommerce_product_node_field', '');
    if (!empty($field) && isset($node->{$field}) && !empty($node->{$field})) {
      $wrapper = entity_metadata_wrapper('node', $node);

      // Only add a product if one is already referenced in the field.
      if ($products = $wrapper->{$field}
        ->value()) {
        foreach ($products as $product) {
          mailchimp_ecommerce_commerce_commerce_product_insert($product);
        }
      }
    }
  }
}

/**
 * Implements hook_node_update().
 *
 * Update a node at Mailchimp when it is updated.
 */
function mailchimp_ecommerce_commerce_node_update($node) {
  $product_node_type = variable_get('mailchimp_ecommerce_product_node_type', '');
  if (!empty($product_node_type) && $node->type == $product_node_type) {

    // See if the configured product field exists.
    $field = variable_get('mailchimp_ecommerce_product_node_field', '');

    // If the product reference field exists and has data, loop through
    // the associated product IDs, looking for all product display nodes.
    if (!empty($field) && isset($node->{$field}) && !empty($node->{$field})) {
      $wrapper = entity_metadata_wrapper('node', $node);
      $ids = $wrapper->{$field}
        ->raw();
      if (!empty($ids)) {
        foreach (array_values($ids) as $product_id) {
          $query = new EntityFieldQuery();
          $query
            ->entityCondition('entity_type', 'node')
            ->entityCondition('bundle', $product_node_type)
            ->fieldCondition($field, 'product_id', $product_id)
            ->propertyCondition('status', TRUE);
          $result = $query
            ->execute();

          // Update our available products.
          if (!empty($result['node'])) {
            $product = commerce_product_load($product_id);
            mailchimp_ecommerce_commerce_commerce_product_update($product);
          }
        }
      }
    }
  }
}

/**
 * Implements hook_node_delete().
 *
 * Delete a product at Mailchimp, but only if no other nodes are referencing it.
 */
function mailchimp_ecommerce_commerce_node_delete($node) {
  $product_node_type = variable_get('mailchimp_ecommerce_product_node_type', '');
  if (!empty($product_node_type) && $node->type == $product_node_type) {

    // See if the configured product field exists.
    $field = variable_get('mailchimp_ecommerce_product_node_field', '');
    if (!empty($field) && isset($node->{$field}) && !empty($node->{$field})) {
      $wrapper = entity_metadata_wrapper('node', $node);
      $ids = $wrapper->{$field}
        ->raw();

      // Find all product IDs referenced in our configured field.
      if (!empty($ids)) {
        foreach (array_values($ids) as $product_id) {
          $query = new EntityFieldQuery();
          $query
            ->entityCondition('entity_type', 'node')
            ->entityCondition('bundle', $product_node_type)
            ->propertyCondition('nid', $node->nid, '<>')
            ->fieldCondition($field, 'product_id', $product_id)
            ->propertyCondition('status', TRUE);
          $result = $query
            ->execute();

          // If no other nodes reference this product, we can delete it.
          if (empty($result['node'])) {
            $product = commerce_product_load($product_id);
            mailchimp_ecommerce_commerce_commerce_product_delete($product);
          }
        }
      }
    }
  }
}

/**
 * Implements hook_commerce_product_insert().
 */
function mailchimp_ecommerce_commerce_commerce_product_insert($product) {
  $product_wrapper = entity_metadata_wrapper('commerce_product', $product);
  $price = commerce_currency_amount_to_decimal($product_wrapper->commerce_price->amount
    ->value(), $product_wrapper->commerce_price->currency_code
    ->value());
  $url = _mailchimp_ecommerce_commerce_build_product_url($product);
  $image_url = _mailchimp_ecommerce_commerce_build_image_url($product);
  $stock = _mailchimp_ecommerce_commerce_get_inventory($product);
  $description = _mailchimp_ecommerce_commerce_get_description($product);

  // TODO: Get product description from product display, if available.
  mailchimp_ecommerce_add_product($product->product_id, $product->sku, $product->title, $description, $product->type, $product->sku, $url, $price, $image_url, $stock);
}

/**
 * Implements hook_commerce_product_update().
 */
function mailchimp_ecommerce_commerce_commerce_product_update($product) {
  $product_wrapper = entity_metadata_wrapper('commerce_product', $product);
  $price = commerce_currency_amount_to_decimal($product_wrapper->commerce_price->amount
    ->value(), $product_wrapper->commerce_price->currency_code
    ->value());
  $url = _mailchimp_ecommerce_commerce_build_product_url($product);
  $image_url = _mailchimp_ecommerce_commerce_build_image_url($product);
  $stock = _mailchimp_ecommerce_commerce_get_inventory($product);
  $description = _mailchimp_ecommerce_commerce_get_description($product);
  mailchimp_ecommerce_update_product($product->product_id, $product->sku, $product->title, $description, $product->sku, $url, $price, $image_url, $stock);
}

/**
 * Implements hook_commerce_product_delete().
 */
function mailchimp_ecommerce_commerce_commerce_product_delete($product) {
  mailchimp_ecommerce_delete_product_variant($product->product_id, $product->sku);
}

/**
 * Implements hook_default_rules_configuration().
 */
function mailchimp_ecommerce_commerce_default_rules_configuration() {
  $rules = [];

  // Add a reaction rule to display the default Add to Cart message.
  $rule = rules_reaction_rule();
  $rule->label = t('Send cart to Mailchimp');
  $rule->tags = [
    'Commerce Cart',
  ];
  $rule->active = TRUE;
  $rule
    ->event('commerce_cart_product_add')
    ->event('commerce_cart_product_remove')
    ->action('mailchimp_ecommerce_commerce_send_cart', [
    'commerce_order:select' => 'commerce-order',
  ]);
  $rules['mailchimp_ecommerce_commerce_send_cart'] = $rule;
  return $rules;
}

/**
 * Implements hook_rules_action_info().
 */
function mailchimp_ecommerce_commerce_rules_action_info() {
  $actions = [];
  $actions['mailchimp_ecommerce_commerce_send_cart'] = [
    'label' => t('Send a cart to Mailchimp'),
    'parameter' => [
      'commerce_order' => [
        'type' => 'commerce_order',
        'label' => t('Order to send'),
      ],
    ],
    'group' => t('Commerce Order'),
    'callbacks' => [
      'execute' => '_mailchimp_ecommerce_commerce_send_cart',
    ],
  ];
  return $actions;
}

/**
 * Private function to send cart data to Mailchimp.
 */
function _mailchimp_ecommerce_commerce_send_cart($order) {

  // Do nothing with no email
  if (!$order->mail) {
    return;
  }
  if (empty($order->commerce_line_items)) {

    // Last line item has been deleted, remove the cart because you can't save
    // an empty cart in Mailchimp.
    mailchimp_ecommerce_delete_cart($order->order_id);
    return;
  }
  $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
  $mc_order = _mailchimp_ecommerce_commerce_build_order($order_wrapper);
  if ($order->created == $order->changed) {
    mailchimp_ecommerce_add_cart($order->order_id, $mc_order['customer'], $mc_order['order_data']);
  }
  else {
    mailchimp_ecommerce_update_cart($order->order_id, $mc_order['customer'], $mc_order['order_data']);
  }
}

/**
 * Parses an address from a Commerce customer profile.
 *
 * @param object $customer_profile_wrapper
 *   Commerce customer profile object.
 *
 * @return object
 *   Address object in a Mailchimp-friendly format.
 */
function mailchimp_ecommerce_commerce_parse_customer_profile_address($customer_profile_wrapper) {
  $address = [];
  if (isset($customer_profile_wrapper->commerce_customer_address) && !empty($customer_profile_wrapper->commerce_customer_address
    ->value()) && isset($customer_profile_wrapper->commerce_customer_address->thoroughfare, $customer_profile_wrapper->commerce_customer_address->premise, $customer_profile_wrapper->commerce_customer_address->locality, $customer_profile_wrapper->commerce_customer_address->administrative_area, $customer_profile_wrapper->commerce_customer_address->postal_code, $customer_profile_wrapper->commerce_customer_address->country)) {
    $address = [
      'address1' => $customer_profile_wrapper->commerce_customer_address->thoroughfare
        ->value(),
      'address2' => $customer_profile_wrapper->commerce_customer_address->premise
        ->value(),
      'city' => $customer_profile_wrapper->commerce_customer_address->locality
        ->value(),
      'province_code' => $customer_profile_wrapper->commerce_customer_address->administrative_area
        ->value(),
      'postal_code' => $customer_profile_wrapper->commerce_customer_address->postal_code
        ->value(),
      'country_code' => $customer_profile_wrapper->commerce_customer_address->country
        ->value(),
    ];
  }
  return $address;
}

/**
 * Builds a Mailchimp order from a Commerce order.
 *
 * @param object $order_wrapper
 *   Commerce order object wrapper.
 *
 * @return object
 *   Order object in a Mailchimp-friendly format.
 *
 * @throws Exception
 *   Throws an exception if Opt In Status is not set.
 */
function _mailchimp_ecommerce_commerce_build_order($order_wrapper) {
  $store_domain = variable_get('mailchimp_ecommerce_store_domain', '');
  $order = $order_wrapper
    ->value();
  $cart_uri = commerce_checkout_order_uri($order);
  $checkout_url = $store_domain ? $store_domain . '/' . $cart_uri : '';
  $billing_address = [];
  $processed_at_foreign = $order_wrapper->created
    ->value();
  $lines = [];
  if (isset($order_wrapper->mail)) {
    $customer_email = $order_wrapper->mail
      ->value();
  }
  else {
    $customer_email = check_plain($_POST['account']['login']['mail']);
  }
  $customer_id = _mailchimp_ecommerce_get_local_customer($customer_email);
  if ($order_wrapper->commerce_order_total
    ->__isset('currency_code') && $order_wrapper->commerce_order_total->currency_code
    ->value()) {
    $currency_code = $order_wrapper->commerce_order_total->currency_code
      ->value();
  }
  else {
    $currency_code = commerce_default_currency();
  }
  $order_total = commerce_currency_amount_to_decimal($order_wrapper->commerce_order_total->amount
    ->value(), $currency_code);
  $customer_wrapper = NULL;
  if ($order_wrapper
    ->__isset('commerce_customer_billing') && $order_wrapper->commerce_customer_billing
    ->value()) {
    $order_customer = commerce_customer_profile_load($order_wrapper->commerce_customer_billing
      ->getIdentifier());
    $customer_wrapper = entity_metadata_wrapper('commerce_customer_profile', $order_customer);

    // If a valid order customer was found, include the current address.
    if ($customer_wrapper
      ->__isset('commerce_customer_address')) {
      $billing_address = mailchimp_ecommerce_commerce_parse_customer_profile_address($customer_wrapper);
    }
  }
  foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {
    if (in_array($line_item_wrapper->type
      ->value(), commerce_product_line_item_types())) {
      $products[] = $line_item_wrapper->commerce_product;
      $line = [
        'id' => (string) $line_item_wrapper
          ->getIdentifier(),
        'product_id' => (string) $line_item_wrapper->commerce_product
          ->raw(),
        'product_variant_id' => $line_item_wrapper->commerce_product->sku
          ->value(),
        'quantity' => (int) $line_item_wrapper->quantity
          ->value(),
        'price' => commerce_currency_amount_to_decimal($line_item_wrapper->commerce_unit_price->amount
          ->value(), $line_item_wrapper->commerce_unit_price->currency_code
          ->value()),
      ];
      $lines[] = $line;
    }
  }

  // Customer parameters used in the 'customer' portions of the API wrapper.
  $customer = [
    'id' => $customer_id,
    'email_address' => $customer_email,
  ];

  // Order parameters that are used as the 'cart' portions of the API wrapper.
  $order_data = [
    'checkout_url' => $checkout_url,
    'currency_code' => $currency_code,
    'order_total' => $order_total,
    'billing_address' => $billing_address,
    'processed_at_foreign' => date('c', $processed_at_foreign),
    'lines' => $lines,
  ];

  // Order parameters that are required for Order Notifications.
  $order_status = $order_wrapper->status
    ->value();
  $mc_order_statuses = mailchimp_ecommerce_get_mc_order_statuses($order_status);
  if (!empty($mc_order_statuses['financial_status']) || !empty($mc_order_statuses['fulfillment_status'])) {
    $order_data['landing_site'] = $_SERVER['HTTP_HOST'];
    $order_data['financial_status'] = $mc_order_statuses['financial_status'];
    $order_data['fulfillment_status'] = $mc_order_statuses['fulfillment_status'];
  }
  return [
    'customer' => $customer,
    'order_data' => $order_data,
  ];
}

/**
 * Creates a URL from a product, as long as the product is referenced by a node.
 *
 * @param object $product
 *   The Commerce object.
 *
 * @return string
 *   The URL of the node referencing the product.
 */
function _mailchimp_ecommerce_commerce_build_product_url($product) {

  // Mailchimp will accept an empty string if no URL is available.
  $url = '';

  // Find the node associated with this product.
  $product_node_type = variable_get('mailchimp_ecommerce_product_node_type', '');
  $product_node_field = variable_get('mailchimp_ecommerce_product_node_field', '');
  if (!empty($product_node_type) && !empty($product_node_field)) {
    $query = new EntityFieldQuery();
    $query
      ->entityCondition('entity_type', 'node')
      ->entityCondition('bundle', $product_node_type)
      ->fieldCondition($product_node_field, 'product_id', $product->product_id)
      ->propertyCondition('status', NODE_PUBLISHED);
    $result = $query
      ->execute();
    if (!empty($result) && !empty($result['node'])) {
      $node = reset($result['node']);
      $url = url('node/' . $node->nid, [
        'absolute' => TRUE,
      ]);
    }
  }
  return $url;
}

/**
 * Creates an Image URL for a product variant.
 *
 * @param object $product
 *   The Commerce product entity.
 *
 * @return string
 *   The external URL of the stylized image.
 */
function _mailchimp_ecommerce_commerce_build_image_url($product) {
  $image_field = variable_get('mailchimp_ecommerce_commerce_image_field', '');
  $image_style = variable_get('mailchimp_ecommerce_commerce_image_style', '');
  $image_url = '';
  if (!empty($image_field)) {
    $image = NULL;
    $product_wrapper = entity_metadata_wrapper('commerce_product', $product);
    if ($product_wrapper
      ->__isset($image_field) && $product_wrapper->{$image_field}
      ->value()) {
      $image = $product_wrapper->{$image_field}
        ->value();
    }
    else {
      $node_type = variable_get('mailchimp_ecommerce_product_node_type', '');
      $product_reference_field = variable_get('mailchimp_ecommerce_product_node_field', '');
      if (!empty($node_type) && !empty($product_reference_field)) {
        $query = new EntityFieldQuery();
        $query
          ->entityCondition('entity_type', 'node')
          ->entityCondition('bundle', $node_type)
          ->fieldCondition($product_reference_field, 'product_id', $product->product_id, '=')
          ->range(0, 1);
        $result = $query
          ->execute();
        if ($result && !empty($result['node'])) {
          $node = array_pop($result['node']);
          $node = node_load($node->nid);
          if (isset($node->{$image_field}) && !empty($node->{$image_field})) {
            $wrapper = entity_metadata_wrapper('node', $node);
            if ($wrapper
              ->__isset($image_field) && $wrapper->{$image_field}
              ->value()) {
              $image = $wrapper->{$image_field}
                ->value();
            }
          }
        }
      }
    }

    // @TODO: Check cardinality of image field, and send all available images.
    if (is_array($image) && !isset($image['fid'])) {
      $image = reset($image);
    }
    if (isset($image['fid'])) {

      // Construct a public URL for this image.
      // If the product is being added via a node form, we must load the new file.
      $fid = $image['fid'];
      $file = file_load($fid);
      if (!empty($image_style)) {
        $image_url = image_style_url($image_style, $file->uri);
      }
      else {
        $image_url = file_create_url($file->uri);
      }
    }
  }
  return $image_url;
}

/**
 * Get the description for a given product from its configured field.
 */
function _mailchimp_ecommerce_commerce_get_description($product) {
  $description = '';
  $field_name = variable_get('mailchimp_ecommerce_description_field', '');
  $node_type = variable_get('mailchimp_ecommerce_product_node_type', '');
  $product_node_field = variable_get('mailchimp_ecommerce_product_node_field', '');
  if (!empty($field_name) && !empty($node_type) && !empty($product_node_field)) {

    // Query the database to find the display node.
    $query = new EntityFieldQuery();
    $query
      ->entityCondition('entity_type', 'node')
      ->entityCondition('bundle', $node_type)
      ->fieldCondition($product_node_field, 'product_id', $product->product_id, '=')
      ->range(0, 1);
    $result = $query
      ->execute();
    if ($result && !empty($result['node'])) {
      $node = array_pop($result['node']);
      $node = node_load($node->nid);
      if (isset($node->{$field_name}) && !empty($node->{$field_name})) {
        $wrapper = entity_metadata_wrapper('node', $node);
        $field_value = $wrapper->{$field_name}
          ->value();
        if (is_array($field_value)) {
          $description = strip_tags($field_value['value']);
        }
        else {
          $description = strip_tags($field_value);
        }
      }
    }
  }

  // Allow other modules to alter the description.
  drupal_alter('mailchimp_ecommerce_product_description', $description, $product);
  return $description;
}

/**
 * Find the inventory of a given product.
 *
 * @param object $product
 *   The Commerce product.
 *
 * @return int
 *   The stock amount, or 1 if no options are configured.
 */
function _mailchimp_ecommerce_commerce_get_inventory($product) {

  // A default inventory amount is required for product recommendations.
  // Setting to 1 is a safe fallback in case no stock modules are enabled.
  $inventory_amount = variable_get('mailchimp_ecommerce_fixed_inventory_amount', 1);
  if (variable_get('mailchimp_ecommerce_use_fixed_inventory', FALSE) == FALSE) {
    $product_wrapper = entity_metadata_wrapper('commerce_product', $product);
    if ($product_wrapper
      ->__isset('commerce_stock') && $product_wrapper->commerce_stock
      ->value()) {
      $inventory_amount = (int) $product_wrapper->commerce_stock
        ->value();
    }
  }
  return $inventory_amount;
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function mailchimp_ecommerce_commerce_form_mailchimp_ecommerce_admin_sync_orders_alter(&$form, &$form_state) {
  $form['#submit'][] = 'mailchimp_ecommerce_commerce_admin_sync_orders_submit';
}

/**
 * Submit handler for the MailChimp eCommerce sync form.
 */
function mailchimp_ecommerce_commerce_admin_sync_orders_submit($form, &$form_state) {
  if (!empty($form_state['values']['sync_orders'])) {
    $batch = [
      'title' => t('Adding orders to MailChimp'),
      'operations' => [],
    ];

    // get orders within specified time period (months)
    $months = abs(intval($form_state['values']['timespan'])) * -1;
    $min_timestamp = strtotime($months . ' months');
    $query = new EntityFieldQuery();
    $results = $query
      ->entityCondition('entity_type', 'commerce_order')
      ->execute();
    if (isset($results['commerce_order'])) {
      $order_ids = array_keys($results['commerce_order']);
      $batch['operations'][] = [
        'mailchimp_ecommerce_commerce_batch_add_orders',
        [
          $order_ids,
        ],
      ];
      batch_set($batch);
    }
  }
}

/**
 * Batch callback used to add orders to MailChimp.
 */
function mailchimp_ecommerce_commerce_batch_add_orders($order_ids, &$context) {
  if (!isset($context['sandbox']['progress'])) {
    $context['sandbox']['progress'] = 0;
    $context['sandbox']['total'] = count($order_ids);
    $context['results']['order_ids'] = $order_ids;
    $context['sandbox']['current_order'] = 0;
  }
  $batch_limit = variable_get('mailchimp_batch_limit', 100);
  $batch = array_slice($context['results']['order_ids'], $context['sandbox']['progress'], $batch_limit);
  $orders = commerce_order_load_multiple($batch);
  foreach ($orders as $order) {
    $context['sandbox']['current_order'] = $order->order_id;
    $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
    $mc_order = _mailchimp_ecommerce_commerce_build_order($order_wrapper);

    // create customer if they do not already exist
    $customer = mailchimp_ecommerce_get_customer($mc_order['customer']['id']);
    if ($customer) {
      mailchimp_ecommerce_update_customer($mc_order['customer']);
    }
    else {
      mailchimp_ecommerce_add_customer($mc_order['customer']);
    }
    if ($order->order_status == 'pending' || $order->order_status == 'complete') {
      $order = mailchimp_ecommerce_get_order($order->order_id);
      if ($order) {
        mailchimp_ecommerce_update_order($order->order_id, $mc_order['order_data']);
      }
      else {
        mailchimp_ecommerce_add_order($order->order_id, $mc_order['customer'], $mc_order['order_data']);
      }
    }
    else {

      // TODO What if the cart already exists?
      mailchimp_ecommerce_add_cart($order->order_id, $mc_order['customer'], $mc_order['order_data']);
    }
  }
  $context['sandbox']['progress']++;
  $context['message'] = t('Sent @count of @total orders to MailChimp', [
    '@count' => $context['sandbox']['progress'],
    '@total' => $context['sandbox']['total'],
  ]);
  $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['total'];
}

Functions

Namesort descending Description
mailchimp_ecommerce_commerce_admin_settings_form_ajax_callback Callback for AJAX request triggered by checkbox to toggle advanced UI on/off.
mailchimp_ecommerce_commerce_admin_sync_orders_submit Submit handler for the MailChimp eCommerce sync form.
mailchimp_ecommerce_commerce_admin_sync_submit Submit handler for the Mailchimp eCommerce sync form.
mailchimp_ecommerce_commerce_admin_validate Validation handler for our admin settings form.
mailchimp_ecommerce_commerce_batch_add_orders Batch callback used to add orders to MailChimp.
mailchimp_ecommerce_commerce_batch_add_products Batch callback used to add products to Mailchimp.
mailchimp_ecommerce_commerce_commerce_cart_order_refresh Implements hook_commerce_cart_order_refresh().
mailchimp_ecommerce_commerce_commerce_cart_product_add Implements hook_commerce_cart_product_add().
mailchimp_ecommerce_commerce_commerce_cart_product_remove Implements hook_commerce_cart_product_remove().
mailchimp_ecommerce_commerce_commerce_checkout_complete Implements hook_commerce_checkout_complete().
mailchimp_ecommerce_commerce_commerce_customer_profile_delete Implements hook_commerce_customer_profile_delete().
mailchimp_ecommerce_commerce_commerce_customer_profile_insert Implements hook_commerce_customer_profile_insert().
mailchimp_ecommerce_commerce_commerce_customer_profile_update Implements hook_commerce_customer_profile_update().
mailchimp_ecommerce_commerce_commerce_line_item_update Implements hook_commerce_line_item_update().
mailchimp_ecommerce_commerce_commerce_order_delete Implements hook_commerce_order_delete().
mailchimp_ecommerce_commerce_commerce_order_update Implements hook_commerce_order_update().
mailchimp_ecommerce_commerce_commerce_product_delete Implements hook_commerce_product_delete().
mailchimp_ecommerce_commerce_commerce_product_insert Implements hook_commerce_product_insert().
mailchimp_ecommerce_commerce_commerce_product_update Implements hook_commerce_product_update().
mailchimp_ecommerce_commerce_default_rules_configuration Implements hook_default_rules_configuration().
mailchimp_ecommerce_commerce_form_mailchimp_ecommerce_admin_settings_alter Implements hook_form_FORM_ID_alter().
mailchimp_ecommerce_commerce_form_mailchimp_ecommerce_admin_sync_alter Implements hook_form_FORM_ID_alter().
mailchimp_ecommerce_commerce_form_mailchimp_ecommerce_admin_sync_orders_alter Implements hook_form_FORM_ID_alter().
mailchimp_ecommerce_commerce_node_delete Implements hook_node_delete().
mailchimp_ecommerce_commerce_node_insert Implements hook_node_insert().
mailchimp_ecommerce_commerce_node_update Implements hook_node_update().
mailchimp_ecommerce_commerce_parse_customer_profile_address Parses an address from a Commerce customer profile.
mailchimp_ecommerce_commerce_rules_action_info Implements hook_rules_action_info().
_mailchimp_ecommerce_commerce_build_image_url Creates an Image URL for a product variant.
_mailchimp_ecommerce_commerce_build_order Builds a Mailchimp order from a Commerce order.
_mailchimp_ecommerce_commerce_build_product_url Creates a URL from a product, as long as the product is referenced by a node.
_mailchimp_ecommerce_commerce_get_description Get the description for a given product from its configured field.
_mailchimp_ecommerce_commerce_get_inventory Find the inventory of a given product.
_mailchimp_ecommerce_commerce_send_cart Private function to send cart data to Mailchimp.
_mailchimp_ecommmerce_commerce_get_customer_email Helper to pull the customer email address from a profile.