You are here

uc_order.rules.inc in Ubercart 8.4

Same filename and directory in other branches
  1. 7.3 uc_order/uc_order.rules.inc

Rules integration for order-related entity events, conditions, and actions.

File

uc_order/uc_order.rules.inc
View source
<?php

/**
 * @file
 * Rules integration for order-related entity events, conditions, and actions.
 */
use Drupal\node\Entity\Node;
use Drupal\uc_order\OrderInterface;

/**
 * Implements hook_rules_data_info().
 */
function uc_order_rules_data_info() {
  $types['uc_order'] = [
    'label' => t('Ubercart order object'),
    'wrap' => TRUE,
    'group' => t('Ubercart'),
  ];
  $types['uc_order_product'] = [
    'label' => t('Ubercart ordered product'),
    'wrap' => TRUE,
    'parent' => 'node',
    'group' => t('Ubercart'),
  ];
  $types['uc_line_item'] = [
    'label' => t('Order line item'),
    'wrap' => TRUE,
    'group' => t('Ubercart'),
    'token type' => FALSE,
  ];
  return $types;
}

/**
 * Implements hook_rules_action_info().
 */
function uc_order_rules_action_info() {
  $order_arg = [
    'type' => 'uc_order',
    'label' => t('Order'),
  ];
  $actions['uc_order_update_status'] = [
    'label' => t('Update the order status'),
    'group' => t('Order'),
    'base' => 'uc_order_action_update_status',
    'parameter' => [
      'order' => $order_arg,
      'order_status' => [
        'type' => 'text',
        'label' => t('Status'),
        'options list' => 'Drupal\\uc_order\\Entity\\OrderStatus::getOptionsList',
      ],
    ],
  ];
  $actions['uc_order_action_add_comment'] = [
    'label' => t('Add a comment to the order'),
    'group' => t('Order'),
    'base' => 'uc_order_action_add_comment',
    'parameter' => [
      'order' => $order_arg,
      'comment' => [
        'type' => 'text',
        'label' => t('Comment'),
      ],
      'comment_type' => [
        'type' => 'text',
        'label' => t('Comment type'),
        'restriction' => 'input',
        'options list' => 'uc_order_action_order_comment_types',
      ],
    ],
  ];
  $actions['uc_order_email'] = [
    'label' => t('Send an order email'),
    'group' => t('Order'),
    'base' => 'uc_order_action_email',
    'parameter' => [
      'order' => $order_arg,
      'from' => [
        'type' => 'text',
        'label' => t('Sender'),
        'description' => t("Enter the 'From' email address, or leave blank to use your store email address. You may use order tokens for dynamic email addresses."),
        'optional' => TRUE,
      ],
      'addresses' => [
        'type' => 'text',
        'label' => t('Recipients'),
        'description' => t('Enter the email addresses to receive the notifications, one on each line. You may use order tokens for dynamic email addresses.'),
      ],
      'subject' => [
        'type' => 'text',
        'label' => t('Subject'),
        'translatable' => TRUE,
      ],
      'message' => [
        'type' => 'text',
        'label' => t('Message'),
        'translatable' => TRUE,
      ],
      'format' => [
        'type' => 'text',
        'label' => t('Message format'),
        'options list' => 'uc_order_message_formats',
      ],
    ],
  ];
  $actions['uc_order_email_invoice'] = [
    'label' => t('Email an order invoice'),
    'group' => t('Order'),
    'base' => 'uc_order_action_email_invoice',
    'parameter' => [
      'order' => $order_arg,
      'from' => [
        'type' => 'text',
        'label' => t('Sender'),
        'description' => t("Enter the 'From' email address, or leave blank to use your store email address. You may use order tokens for dynamic email addresses."),
        'optional' => TRUE,
      ],
      'addresses' => [
        'type' => 'text',
        'label' => t('Recipients'),
        'description' => t('Enter the email addresses to receive the invoice, one on each line. You may use order tokens for dynamic email addresses.'),
      ],
      'subject' => [
        'type' => 'text',
        'label' => t('Subject'),
        'translatable' => TRUE,
      ],
      'template' => [
        'type' => 'text',
        'label' => t('Invoice template'),
        'options list' => 'uc_order_template_options',
        'restriction' => 'input',
      ],
      'view' => [
        'type' => 'text',
        'label' => t('Included information'),
        'options list' => 'uc_order_action_email_invoice_view_options',
        'restriction' => 'input',
      ],
    ],
  ];
  return $actions;
}

/**
 * Checks the current order state.
 */
function uc_order_condition_order_state($order, $order_state) {
  return $order
    ->getStateId() == $order_state;
}

/**
 * Options callback.
 *
 * @see uc_order_condition_order_state()
 */
function uc_order_condition_order_state_options() {
  return uc_order_state_options_list();
}

/**
 * Checks that the order has one of the selected delivery countries.
 */
function uc_order_condition_delivery_country($order, $countries) {
  return in_array($order->delivery_country, $countries);
}

/**
 * Checks that the order has one of the selected billing countries.
 */
function uc_order_condition_billing_country($order, $countries) {
  return in_array($order->billing_country, $countries);
}

/**
 * Checks that the order has the selected combination of product classes.
 *
 * @param \Drupal\uc_order\OrderInterface $order
 *   The order to check.
 * @param array $product_classes
 *   An array of strings containing the product classes (node content
 *   types) to check against.
 * @param bool $required
 *   TRUE to require all product classes be present in the order. FALSE
 *   to require at least one be present.
 * @param bool $forbidden
 *   TRUE to require that only the listed product classes be present. FALSE
 *   to allow products with other classes.
 *
 * @return bool
 *   Whether the order meets the specified conditions.
 */
function uc_order_condition_has_product_class(OrderInterface $order, array $product_classes, $required, $forbidden) {
  $order_product_classes = [];
  foreach ($order->products as $product) {
    if (!empty($product->type)) {

      // If present, use the product type from {uc_order_products}.data.type.
      $order_product_classes[] = $product->type;
    }
    else {

      // Otherwise, use the node type. If the node can't be loaded, ignore
      // this product.
      $node = Node::load($product->nid);
      if (!empty($node)) {
        $order_product_classes[] = $node->type;
      }
    }
  }
  $required_product_classes = array_intersect($product_classes, $order_product_classes);
  if ($required) {
    $required_check = $required_product_classes == $product_classes;
  }
  else {
    $required_check = (bool) count($required_product_classes);
  }
  if ($forbidden) {
    $forbidden_product_classes = array_diff($order_product_classes, $product_classes);
    $forbidden_check = (bool) count($forbidden_product_classes);
  }
  else {
    $forbidden_check = FALSE;
  }
  return $required_check && !$forbidden_check;
}

/**
 * Options callback.
 *
 * @return array
 *   Associative array of all Ubercart product classes indexed by class ID.
 *
 * @see uc_order_condition_has_product_class()
 */
function uc_order_condition_has_product_class_classes_options() {
  $options = [];
  $result = db_query('SELECT * FROM {uc_product_classes}');
  foreach ($result as $class) {
    $options += [
      $class->pcid => $class->name,
    ];
  }
  return $options;
}

/**
 * Checks that the order has the selected combination of products.
 */
function uc_order_condition_has_products($order, $products, $required, $forbidden) {
  $order_products = [];
  foreach ($order->products as $product) {
    $order_products[] = $product->model;
  }
  $required_products = array_intersect($products, $order_products);
  if ($required) {
    $required_check = $required_products == $products;
  }
  else {
    $required_check = (bool) count($required_products);
  }
  if ($forbidden) {
    $forbidden_products = array_diff($order_products, $products);
    $forbidden_check = (bool) count($forbidden_products);
  }
  else {
    $forbidden_check = FALSE;
  }
  return $required_check && !$forbidden_check;
}

/**
 * Options callback.
 *
 * @see uc_order_condition_has_products()
 */
function uc_order_condition_has_products_options() {
  $options = [];
  $result = db_query('SELECT nid FROM {uc_products}');
  foreach ($result as $row) {
    $options += uc_product_get_models($row->nid, FALSE);
  }
  asort($options);
  return $options;
}

/**
 * Checks that the order has the selected number of products.
 *
 * @see uc_order_condition_count_products_form()
 */
function uc_order_condition_count_products($order, $products, $count, $op) {
  $totals = [
    'all' => 0,
  ];
  $total = 0;
  foreach ($order->products as $product) {
    $totals['all'] += $product->qty;
    if (isset($totals[$product->nid])) {
      $totals[$product->nid] += $product->qty;
    }
    else {
      $totals[$product->nid] = $product->qty;
    }
  }
  if (in_array('all', $products)) {
    $total = $totals['all'];
  }
  else {
    foreach ($products as $product) {
      if (isset($totals[$product])) {
        $total += $totals[$product];
      }
    }
  }
  return uc_order_condition_value_operator_comparison($total, $op, $count);
}

/**
 * Product options callback.
 */
function uc_order_condition_products_options() {
  $options = [
    'all' => t('- All products -'),
  ];
  $options += db_query('SELECT nid, model FROM {uc_products} ORDER BY model')
    ->fetchAllKeyed();
  return $options;
}

/**
 * Operator options callback.
 */
function uc_order_condition_value_operator_options() {
  return [
    'less' => t('Total is less than specified value.'),
    'less_equal' => t('Total is less than or equal to specified value.'),
    'equal' => t('Total is equal to specified value.'),
    'greater_equal' => t('Total is greater than or equal to specified value.'),
    'greater' => t('Total is greater than specified value.'),
  ];
}

/**
 * Checks the weight of the order's products.
 *
 * @see uc_order_condition_products_weight_form()
 */
function uc_order_condition_products_weight($order, $products, $weight_units, $weight_value, $op) {
  $totals = [
    'all' => 0,
  ];
  $total = 0;
  foreach ($order->products as $product) {
    $unit_conversion = uc_weight_conversion($product->weight_units, $weight_units);
    $totals['all'] += $product->qty * $product->weight * $unit_conversion;
    $totals[$product->nid] = $product->qty * $product->weight * $unit_conversion;
  }
  if (in_array('all', $products)) {
    $total = $totals['all'];
  }
  else {
    foreach ($products as $product) {
      if (isset($totals[$product])) {
        $total += $totals[$product];
      }
    }
  }
  return uc_order_condition_value_operator_comparison($total, $op, $weight_value);
}

/**
 * Weight units options callback.
 */
function uc_order_condition_weight_units_options() {
  return [
    'lb' => t('Pounds'),
    'kg' => t('Kilograms'),
    'oz' => t('Ounces'),
    'g' => t('Grams'),
  ];
}

/**
 * Checks that the order is shippable.
 */
function uc_order_condition_is_shippable($order, $settings) {
  return $order
    ->isShippable();
}

/**
 * Updates an order's status.
 *
 * @see uc_order_action_update_status_form()
 */
function uc_order_action_update_status($order, $status) {
  $order
    ->setStatusId($status)
    ->save();
}

/**
 * Adds a comment to an order.
 *
 * @see uc_order_action_add_comment_form()
 */
function uc_order_action_add_comment($order, $comment, $comment_type) {
  uc_order_comment_save($order
    ->id(), 0, \Drupal::token()
    ->replace($comment, [
    'uc_order' => $order,
  ]), $comment_type == 'admin' ? 'admin' : 'order', $order
    ->getStatusId(), $comment_type == 'notified');
}

/**
 * @see uc_order_action_add_comment()
 */
function uc_order_action_order_comment_types() {
  return [
    'admin' => t('Enter this as an admin comment.'),
    'order' => t('Enter this as a customer order comment.'),
    'notified' => t('Enter this as a customer order comment with a notified icon.'),
  ];
}

/**
 * Sends an email concerning an order.
 *
 * The 'Sender', 'Recipients', 'Subject', and 'Message' fields accept
 * order token replacements.
 *
 * @see uc_order_action_email_form()
 */
function uc_order_action_email($order, $from, $addresses, $subject, $message, $format) {
  $token_service = \Drupal::token();
  $settings = [
    'from' => $from,
    'addresses' => $addresses,
    'subject' => $subject,
    'message' => $message,
    'format' => $format,
  ];

  // Token replacements for the subject and body.
  $settings['replacements'] = [
    'uc_order' => $order,
  ];

  // Apply token replacements to the 'from' e-mail address.
  $from = $token_service
    ->replace($settings['from'], $settings['replacements']);
  if (empty($from)) {
    $from = uc_store_email_from();
  }

  // Apply token replacements to 'recipient' e-mail addresses.
  $addresses = $token_service
    ->replace($settings['addresses'], $settings['replacements']);

  // Split up our recipient e-mail addresses.
  $recipients = [];
  foreach (explode("\n", $addresses) as $address) {
    $address = trim($address);

    // Remove blank lines.
    if (!empty($address)) {
      $recipients[] = $address;
    }
  }
  foreach ($recipients as $email) {
    $sent = \Drupal::service('plugin.manager.mail')
      ->mail('uc_order', 'rules-action-email', $email, uc_store_mail_recipient_langcode($email), $settings, $from);
    if (!$sent['result']) {
      \Drupal::logger('uc_order')
        ->error('Attempt to e-mail @email concerning order @order_id failed.', [
        '@email' => $email,
        '@order_id' => $order
          ->id(),
      ]);
    }
  }
}

/**
 * Options list callback for message formats.
 */
function uc_order_message_formats() {
  global $user;
  $options = [];
  $formats = filter_formats($user);
  foreach ($formats as $format) {
    $options[$format->format] = $format->name;
  }
  return $options;
}

/**
 * Emails an invoice.
 *
 * The 'Sender', 'Subject' and 'Addresses' fields take order token replacements.
 */
function uc_order_action_email_invoice($order, $from, $addresses, $subject, $template, $view) {
  $token_service = \Drupal::token();
  $settings = [
    'from' => $from,
    'addresses' => $addresses,
    'subject' => $subject,
    'template' => $template,
    'view' => $view,
  ];

  // Token replacements for the from, subject and body.
  $settings['replacements'] = [
    'uc_order' => $order,
  ];

  // Apply token replacements to the 'from' e-mail address.
  $from = $token_service
    ->replace($settings['from'], $settings['replacements']);
  if (empty($from)) {
    $from = uc_store_email_from();
  }

  // Apply token replacements to 'recipient' e-mail addresses.
  $addresses = $token_service
    ->replace($settings['addresses'], $settings['replacements']);

  // Split up our recipient e-mail addresses.
  $recipients = [];
  foreach (explode("\n", $addresses) as $address) {
    $address = trim($address);

    // Remove blank lines.
    if (!empty($address)) {
      $recipients[] = $address;
    }
  }
  $message = [
    '#theme' => 'uc_order_invoice',
    '#order' => $order,
    '#op' => $settings['view'],
    '#template' => $settings['template'],
  ];
  $settings['message'] = \Drupal::service('renderer')
    ->renderPlain($message);
  foreach ($recipients as $email) {
    $sent = \Drupal::service('plugin.manager.mail')
      ->mail('uc_order', 'rules-action-email', $email, uc_store_mail_recipient_langcode($email), $settings, $from);
    if (!$sent['result']) {
      \Drupal::logger('uc_order')
        ->error('Attempt to e-mail invoice for order @order_id to @email failed.', [
        '@email' => $email,
        '@order_id' => $order
          ->id(),
      ]);
    }
  }
}

/**
 * @see uc_order_action_email_invoice()
 */
function uc_order_action_email_invoice_view_options() {
  return [
    'print' => t('Show the business header and shipping method.'),
    'admin-mail' => t('Show all of the above plus the help text, email text, and store footer.'),
    'checkout-mail' => t('Show all of the above plus the "thank you" message.'),
  ];
}

/**
 * Value comparison.
 *
 * @param float $source
 *   The source value.
 * @param string $op
 *   The comparison operator.
 * @param float $target
 *   The target value.
 *
 * @return bool
 *   Whether the comparison meets the specified conditions.
 *
 * @see uc_order_condition_value_operator_options
 */
function uc_order_condition_value_operator_comparison($source, $op, $target) {
  switch ($op) {
    case 'less':
      return $source < $target;
    case 'less_equal':
      return $source <= $target;
    case 'equal':
      return $source == $target;
    case 'greater_equal':
      return $source >= $target;
    case 'greater':
      return $source > $target;
  }
}

/**
 * Compare order total.
 *
 * @param object $order
 *   The order to check.
 * @param string $op
 *   The comparison operator.
 * @param float $value
 *   The target value.
 *
 * @return bool
 *   Whether the order total meets the specified condition.
 *
 * @see uc_order_condition_value_operator_options
 */
function uc_order_condition_total($order, $op, $value) {
  return uc_order_condition_value_operator_comparison($order->order_total, $op, $value);
}

/**
 * Compare order subtotal.
 *
 * @param object $order
 *   The order to check.
 * @param string $op
 *   The comparison operator.
 * @param float $value
 *   The target value.
 *
 * @return bool
 *   Whether the order subtotal meets the specified condition.
 *
 * @see uc_order_condition_value_operator_options
 */
function uc_order_condition_subtotal($order, $op, $value) {
  if (is_array($order->line_items)) {
    foreach ($order->line_items as $line_item) {
      if ($line_item['type'] == 'subtotal') {
        $subtotal = $line_item['amount'];
        return uc_order_condition_value_operator_comparison($subtotal, $op, $value);
      }
    }
  }
  return FALSE;
}

Functions

Namesort descending Description
uc_order_action_add_comment Adds a comment to an order.
uc_order_action_email Sends an email concerning an order.
uc_order_action_email_invoice Emails an invoice.
uc_order_action_email_invoice_view_options
uc_order_action_order_comment_types
uc_order_action_update_status Updates an order's status.
uc_order_condition_billing_country Checks that the order has one of the selected billing countries.
uc_order_condition_count_products Checks that the order has the selected number of products.
uc_order_condition_delivery_country Checks that the order has one of the selected delivery countries.
uc_order_condition_has_products Checks that the order has the selected combination of products.
uc_order_condition_has_products_options Options callback.
uc_order_condition_has_product_class Checks that the order has the selected combination of product classes.
uc_order_condition_has_product_class_classes_options Options callback.
uc_order_condition_is_shippable Checks that the order is shippable.
uc_order_condition_order_state Checks the current order state.
uc_order_condition_order_state_options Options callback.
uc_order_condition_products_options Product options callback.
uc_order_condition_products_weight Checks the weight of the order's products.
uc_order_condition_subtotal Compare order subtotal.
uc_order_condition_total Compare order total.
uc_order_condition_value_operator_comparison Value comparison.
uc_order_condition_value_operator_options Operator options callback.
uc_order_condition_weight_units_options Weight units options callback.
uc_order_message_formats Options list callback for message formats.
uc_order_rules_action_info Implements hook_rules_action_info().
uc_order_rules_data_info Implements hook_rules_data_info().