From 15d42e49d8a1d6654403815687a563b8fa8e4986 Mon Sep 17 00:00:00 2001
From: Brandon Bergren <bdragon@rtk0.net>
Date: Mon, 17 Oct 2011 09:32:01 -0500
Subject: [PATCH] WIP patch requested by rickmanelius. Work on #992252 by
 bdragon: CA -> Rules conversion.

---
 .../uc_recurring_subscription.info                 |    2 +-
 .../uc_recurring_subscription.module               |   23 +-
 .../uc_recurring_subscription.rules.inc            |  236 +++++++++++-
 .../uc_recurring_subscription.rules_defaults.inc   |   96 +++++-
 uc_recurring.ca.inc                                |  400 --------------------
 uc_recurring.info                                  |    1 -
 uc_recurring.module                                |   12 +-
 uc_recurring.rules.inc                             |  115 ++++++-
 uc_recurring.rules_defaults.inc                    |   68 ++++-
 9 files changed, 519 insertions(+), 434 deletions(-)
 delete mode 100644 uc_recurring.ca.inc

diff --git a/modules/uc_recurring_subscription/uc_recurring_subscription.info b/modules/uc_recurring_subscription/uc_recurring_subscription.info
index 76d4498..57057cc 100644
--- a/modules/uc_recurring_subscription/uc_recurring_subscription.info
+++ b/modules/uc_recurring_subscription/uc_recurring_subscription.info
@@ -1,5 +1,5 @@
 name = Subscription Manager
-description = Provides a Subscription Manager UI for paid membership features. Depends on Ubercart-6.x-2.5
+description = Provides a Subscription Manager UI for paid membership features.
 dependencies[] = uc_recurring
 dependencies[] = uc_recurring_product
 dependencies[] = uc_attribute
diff --git a/modules/uc_recurring_subscription/uc_recurring_subscription.module b/modules/uc_recurring_subscription/uc_recurring_subscription.module
index a1f2c6a..0dfeb4e 100644
--- a/modules/uc_recurring_subscription/uc_recurring_subscription.module
+++ b/modules/uc_recurring_subscription/uc_recurring_subscription.module
@@ -20,13 +20,6 @@ function uc_recurring_subscription_help($path, $arg) {
 }
 
 /**
- * Implements hook_init().
- */
-function uc_recurring_subscription_init() {
-  module_load_include('inc', 'uc_recurring_subscription', 'uc_recurring_subscription.ca');
-}
-
-/**
  * Implements hook_permission().
  */
 function uc_recurring_subscription_permission() {
@@ -172,6 +165,22 @@ function uc_recurring_subscription_node_load($nodes, $types) {
 }
 
 /**
+ * Implements hook_node_load().
+ */
+function uc_recurring_subscription_node_load($nodes, $types) {
+  if (!in_array(variable_get('uc_recurring_subscription_product_class', 'uc_recurring_subscription'), $types)) {
+    return;
+  }
+  $result = db_query('SELECT * FROM {uc_recurring_subscription} WHERE nid IN(:nids)', array(':nids' => array_keys($nodes)));
+  foreach ($result as $record) {
+    $nodes[$record->nid]->subscription = $record;
+    $nodes[$record->nid]->subscription->access = empty($record->access) ? array() : unserialize($record->access);
+  }
+}
+
+/////////////////////////////////////////////////////////
+
+/**
  * This loads the reucrring product features as well as the attribute option for
  * this feature.
  */
diff --git a/modules/uc_recurring_subscription/uc_recurring_subscription.rules.inc b/modules/uc_recurring_subscription/uc_recurring_subscription.rules.inc
index 9b403c5..34578bc 100644
--- a/modules/uc_recurring_subscription/uc_recurring_subscription.rules.inc
+++ b/modules/uc_recurring_subscription/uc_recurring_subscription.rules.inc
@@ -5,10 +5,8 @@
  * Rules definitions.
  */
 
-
-
 /**
- * Implement hook_rules_data_info().
+ * Implements hook_rules_data_info().
  */
 function uc_recurring_subscription_rules_data_info() {
   $types['uc_recurring_subscription'] = array(
@@ -21,25 +19,241 @@ function uc_recurring_subscription_rules_data_info() {
 }
 
 /**
- * Implement hook_rules_event_info().
+ * Implements hook_rules_event_info().
  */
-function uc_recurring_subscription_event_info() {
+function uc_recurring_subscription_rules_event_info() {
   $events = array();
   return $events;
 }
 
 /**
- * Implement hook_rules_condition_info().
+ * Implements hook_rules_condition_info().
  */
-function uc_recurring_subscription_condition_info() { 
+function uc_recurring_subscription_rules_condition_info() {
   $conditions = array();
   return $conditions;
 }
 
 /**
- * Implement hook_rules_action_info().
+ * Implements hook_rules_action_info().
  */
-function uc_recurring_subscription_action_info() {
-  $actions = array();
+function uc_recurring_subscription_rules_action_info() {
+  // actions to grant/revoke role on user account.
+  $actions['uc_recurring_subscription_grant_role'] = array(
+    'label' => t('Grant a role on an order.'),
+    'group' => t('Role'),
+    'parameter' => array(
+      'order' => array(
+        'type' => 'uc_order',
+        'label' => t('Order'),
+      ),
+      'role_option' => array(
+        'type' => 'text',
+        'label' => t('Where are the role(s) defined?'),
+        'options list' => '_uc_recurring_subscription_role_options',
+      ),
+      'roles' => array(
+        'type' => 'list<integer>',
+        'label' => t('Role'),
+        'options list' => '_uc_recurring_subscription_user_roles',
+        'optional' => TRUE,
+      ),
+    ),
+  );
+  $actions['uc_recurring_subscription_revoke_role'] = array(
+    'label' => t('Revoke a role on an order.'),
+    'group' => t('Role'),
+    'parameter' => array(
+      'order' => array(
+        'type' => 'uc_order',
+        'label' => t('Order'),
+      ),
+      'role_option' => array(
+        'type' => 'text',
+        'label' => t('Where are the role(s) defined?'),
+        'options list' => '_uc_recurring_subscription_role_options',
+      ),
+      'role_option' => array(
+        'type' => 'text',
+        'label' => t('Where are the role(s) defined?'),
+        'options list' => '_uc_recurring_subscription_role_options',
+      ),
+      'roles' => array(
+        'type' => 'list<integer>',
+        'label' => t('Role'),
+        'options list' => '_uc_recurring_subscription_user_roles',
+        'optional' => TRUE,
+      ),
+    ),
+  );
+
+  if (module_exists('og')) {
+    // actions to subscribe/unsubscribe user from organic group.
+    $actions['uc_recurring_subscription_grant_og'] = array(
+      'label' => t('Grant organic group membership on order.'),
+      'group' => t('Organic Group'),
+      'parameter' => array(
+        'order' => array(
+          'type' => 'uc_order',
+          'label' => t('Order'),
+        ),
+        'group_option' => array(
+          'type' => 'text',
+          'label' => t('Where are the role(s) defined?'),
+          'options list' => '_uc_recurring_subscription_role_options',
+        ),
+        'group' => array(
+          'type' => 'group',
+          'label' => t('Group'),
+        ),
+      ),
+    );
+    $actions['uc_recurring_subscription_revoke_og'] = array(
+      'label' => t('Revoke organic group membership on order.'),
+      'group' => t('Organic Group'),
+      'parameter' => array(
+        'order' => array(
+          'type' => 'uc_order',
+          'label' => t('Order'),
+        ),
+        'group_option' => array(
+          'type' => 'text',
+          'label' => t('Where are the role(s) defined?'),
+          'options list' => '_uc_recurring_subscription_role_options',
+        ),
+        'group' => array(
+          'type' => 'group',
+          'label' => t('Group'),
+        ),
+      ),
+    );
+  }
   return $actions;
-}
\ No newline at end of file
+}
+
+function _uc_recurring_subscription_role_options() {
+  return array(
+    'subscribe_grant' => t('Apply the roles set by the Subscription Manager on creation.'),
+    'expire_grant' => t('Apply the roles set by the Subscription Manager on expiration.'),
+    'expire_revoke' => t('Revoke the roles set by the Subscription Manager on expiration.'),
+    'custom' => t('Custom selected role (select from below)'),
+  );
+}
+
+function _uc_recurring_subscription_group_options() {
+  return array(
+    'subscribe_grant_og' => t('Apply the groups set by the Subscription Manager on creation.'),
+    'expire_grant_og' => t('Apply the groups set by the Subscription Manager on expiration.'),
+    'expire_revoke_og' => t('Revoke the groups set by the Subscription Manager on expiration.'),
+    'custom' => t('Custom selected groups (select from below)'),
+  );
+}
+
+function _uc_recurring_subscription_user_roles() {
+  $roles = user_roles(TRUE);
+  unset($roles[DRUPAL_AUTHENTICATED_RID]);
+  return $roles;
+}
+
+
+/**
+ * Grant a role.
+ */
+function uc_recurring_subscription_grant_role($order, $role_option, $roles) {
+  $account = user_load($order->uid);
+
+  $role_names = user_roles(TRUE);
+  if ($role_option == 'custom') {
+    foreach ($roles as $rid => $role) {
+      $account->roles[$rid] = $role_names[$rid];
+      watchdog('uc_recurring', 'Granted !role role to !user', array('!role' => $role_names[$rid], '!user' => $account->name));
+    }
+  }
+  else {
+    foreach ($order->products as $pid => $product) {
+      $subscription = uc_recurring_subscription_load($product->nid);
+      if (!empty($subscription->access[$role_option])) {
+        $account->roles += $subscription->access[$role_option];
+      }
+    }
+  }
+  user_save($account, array('roles' => $account->roles));
+}
+
+/**
+ * Revoke user role(s).
+ */
+function uc_recurring_subscription_revoke_role($order, $role_option, $roles) {
+  $account = user_load($order->uid);
+
+  $role_names = user_roles(TRUE);
+  if ($role_option == 'custom') {
+    foreach ($roles as $rid => $role) {
+      unset($account->roles[$rid]);
+      watchdog('uc_recurring', 'Revoked !role role from !user', array('!role' => $role_names[$rid], '!user' => $account->name));
+    }
+  }
+  else {
+    foreach ($order->products as $pid => $product) {
+      $subscription = uc_recurring_subscription_load($product->nid);
+      if (!empty($subscription->access[$role_option])) {
+        foreach ($subscription->access[$role_option] as $rid => $role) {
+          // @todo: we need to check other subscriptions to ensure that the role
+          // is no longer required by another subscription.
+          unset($account->roles[$rid]);
+        }
+      }
+    }
+  }
+  user_save($account, array('roles' => $account->roles));
+
+  $account = user_load($order->uid);
+}
+
+/**
+ * Grant user organic groups.
+ */
+function uc_recurring_subscription_grant_og($order, $group_option, $group) {
+  // Load user.
+  if (!$order->uid || !($account = user_load($order->uid))) {
+    return;
+  }
+  if ($group_option == 'custom' && is_array($group) && !empty($group)) {
+    og_subscribe_user_action($account, $group);
+  }
+  else {
+    foreach ($order->products as $pid => $product) {
+      $subscription = uc_recurring_subscription_load($product->nid);
+      $context['groups'] = $subscription->access[$group_option];
+      if (is_array($context['groups']) && !empty($context['groups'])) {
+        og_subscribe_user_action($account, $context);
+        $comment = t('User access granted to group(s): %group.', array('%group' => implode(',', $context['groups'])));
+        uc_order_comment_save($order->order_id, $account->uid, $comment);
+      }
+    }
+  }
+}
+
+/**
+ * Revoke user organic groups.
+ */
+function uc_recurring_subscription_revoke_og($order, $group_option, $group) {
+  // Load user.
+  if (!$order->uid || !($account = user_load($order->uid))) {
+    return;
+  }
+  if ($group_option == 'custom' && is_array($group) && !empty($group)) {
+    og_unsubscribe_user_action($account, $group);
+  }
+  else {
+    foreach ($order->products as $pid => $product) {
+      $subscription = uc_recurring_subscription_load($product->nid);
+      $context['groups'] = $subscription->access[$group_option];
+      if (is_array($context['groups']) && !empty($context['groups'])) {
+        og_unsubscribe_user_action($account, $context);
+        $comment = t('User access revoked for group(s): %group.', array('%group' => implode(',', $context['groups'])));
+        uc_order_comment_save($order->order_id, $account->uid, $comment);
+      }
+    }
+  }
+}
diff --git a/modules/uc_recurring_subscription/uc_recurring_subscription.rules_defaults.inc b/modules/uc_recurring_subscription/uc_recurring_subscription.rules_defaults.inc
index 3374689..d2c6c1b 100644
--- a/modules/uc_recurring_subscription/uc_recurring_subscription.rules_defaults.inc
+++ b/modules/uc_recurring_subscription/uc_recurring_subscription.rules_defaults.inc
@@ -6,5 +6,97 @@
  */
 
 /**
- * Implement hook_default_rules_configuration().
- */
\ No newline at end of file
+ * Implements hook_default_rules_configuration().
+ */
+function uc_recurring_subscription_default_rules_configuration() {
+  $configs = array();
+
+  // Grant user roles to user when checkout is complete.
+  $rule = rules_reaction_rule();
+  $rule->label = t('Grant user roles on subscription created.');
+  $rule->active = TRUE;
+  $rule->weight = 1;
+  $rule->event('uc_order_status_update')
+    // This is the entity way of doing things
+    //->condition('data_is', array('data:select' => 'order:order-status', 'value' => 'completed'))
+    // This is the traditional way of doing things
+    ->condition('uc_order_condition_order_state', array('order:select' => 'order', 'order_state' => 'completed'))
+    ->action('uc_recurring_subscription_grant_role', array(
+      'order:select' => 'order',
+      'role_option' => 'subscribe_grant',
+      'roles' => array(),
+    ));
+  $configs['uc_recurring_subscription_grant_roles'] = $rule;
+
+  // Grant user roles to user when subscription expires.
+  $rule = rules_reaction_rule();
+  $rule->label = t('Grant user roles on subscription created.');
+  $rule->active = TRUE;
+  $rule->weight = 1;
+  $rule->event('uc_recurring_renewal_expired')
+    ->action('uc_recurring_subscription_grant_role', array(
+      'order:select' => 'order',
+      'role_option' => 'expire_grant',
+      'roles' => array(),
+    ));
+  $configs['uc_recurring_subscription_expired_grant_roles'] = $rule;
+
+  // Revoke user roles to user when subscription expires.
+  $rule = rules_reaction_rule();
+  $rule->label = t('Grant user roles on subscription created.');
+  $rule->active = TRUE;
+  $rule->weight = 1;
+  $rule->event('uc_recurring_renewal_expired')
+    ->action('uc_recurring_subscription_revoke_role', array(
+      'order:select' => 'order',
+      'role_option' => 'expire_revoke',
+      'roles' => array(),
+    ));
+  $configs['uc_recurring_subscription_expired_revoke_roles'] = $rule;
+
+  if (module_exists('og')) {
+    // Grant organic groups to user when checkout is complete.
+    $rule = rules_reaction_rule();
+    $rule->label = t('Grant organic groups on subscription created.');
+    $rule->active = TRUE;
+    $rule->weight = 1;
+    $rule->event('uc_order_status_update')
+      // This is the entity way of doing things
+      //->condition('data_is', array('data:select' => 'updated-order:order-status', 'value' => 'completed'))
+      // This is the traditional way of doing things
+      ->condition('uc_order_condition_order_state', array('order:select' => 'updated-order', 'order_state' => 'completed'))
+      ->action('uc_recurring_subscription_grant_og', array(
+        'order:select' => 'order',
+        'group_option' => 'subscribe_grant_og',
+        'group' => NULL,
+      ));
+    $configs['uc_recurring_subscription_grant_og'] = $rule;
+
+    // Grant organic groups to user when subscription expires.
+    $rule = rules_reaction_rule();
+    $rule->label = t('Grant organic groups on subscription created.');
+    $rule->active = TRUE;
+    $rule->weight = 1;
+    $rule->event('uc_recurring_renewal_expired')
+      ->action('uc_recurring_subscription_grant_og', array(
+        'order:select' => 'order',
+        'group_option' => 'expire_grant_og', // @@@ Was '' in CA -- this rule was broken in 6?
+        'group' => NULL,
+      ));
+    $configs['uc_recurring_subscription_expired_grant_og'] = $rule;
+
+    // Revoke organic groups to user when subscription expires.
+    $rule = rules_reaction_rule();
+    $rule->label = t('Revoke organic groups on subscription expiration.');
+    $rule->active = TRUE;
+    $rule->weight = 1;
+    $rule->event('uc_recurring_renewal_expired')
+      ->action('uc_recurring_subscription_revoke_og', array(
+        'order:select' => 'order',
+        'role_option' => 'expire_revoke_og',
+        'role' => NULL,
+      ));
+    $configs['uc_recurring_subscription_expired_revoke_og'] = $rule;
+  }
+  return $configs;
+}
\ No newline at end of file
diff --git a/uc_recurring.ca.inc b/uc_recurring.ca.inc
deleted file mode 100644
index 975cdb4..0000000
--- a/uc_recurring.ca.inc
+++ /dev/null
@@ -1,400 +0,0 @@
-<?php
-
-/**
- * @file
- * This file contains the Conditional Actions hooks and functions necessary to make the
- * recurring-related entity, conditions, events, and actions work.
- */
-
-/******************************************************************************
- * Conditional Actions Hooks                                                  *
- ******************************************************************************/
-
-/**
- * Implements hook_ca_entity().
- *
- * An entity is defined in order to get the recurring fee down to token in the
- * email.
- */
-function uc_recurring_ca_entity() {
-  // CA entity for a recurring fee object.
-  $entities['uc_recurring_fee'] = array(
-    '#title' => t('Recurring Fee'),
-    '#type' => 'object',
-    '#load' => 'uc_recurring_fee_user_load',
-    '#save' => 'uc_recurring_fee_user_save',
-  );
-
-  return $entities;
-}
-
-/**
- * Implements hook_ca_predicate().
- */
-function uc_recurring_ca_predicate() {
-  $predicates = array();
-
-  // Set the order status to "Completed" when checkout is complete, none
-  // of the products are shippable, and the balance is less than or equal to 0.
-  $predicates['uc_recurring_renewal_complete_paid'] = array(
-    '#title' => t('Update order status upon renewal completion with full payment'),
-    '#trigger' => 'uc_recurring_renewal_complete',
-    '#class' => 'payment',
-    '#status' => 1,
-    '#weight' => 1,
-    '#conditions' => array(
-      '#operator' => 'AND',
-      '#conditions' => array(
-        array(
-          '#name' => 'uc_payment_condition_order_balance',
-          '#title' => t('If the balance is less than or equal to $0.00.'),
-          '#argument_map' => array(
-            'order' => 'order',
-          ),
-          '#settings' => array(
-            'negate' => FALSE,
-            'balance_comparison' => 'less_equal',
-          ),
-        ),
-        array(
-          '#name' => 'uc_order_condition_is_shippable',
-          '#title' => t('If the order is not shippable.'),
-          '#argument_map' => array(
-            'order' => 'order',
-          ),
-          '#settings' => array(
-            'negate' => TRUE,
-          ),
-        ),
-      ),
-    ),
-    '#actions' => array(
-      array(
-        '#name' => 'uc_order_update_status',
-        '#title' => t('Update the order status to Completed.'),
-        '#argument_map' => array(
-          'order' => 'order',
-        ),
-        '#settings' => array(
-          'order_status' => 'completed',
-        ),
-      ),
-    ),
-  );
-
-  $predicates['uc_recurring_renewal_email_completed'] = array(
-    '#title' => t('Notify customer when a renewal has occurred.'),
-    '#trigger' => 'uc_recurring_renewal_complete',
-    '#class' => 'notification',
-    '#status' => 0,
-    '#weight' => 2,
-    '#conditions' => array(
-      '#operator' => 'AND',
-      '#conditions' => array(
-        array(
-          '#name' => 'uc_recurring_renewal_status_condition',
-          '#title' => t('If the order contains product renewal.'),
-          '#argument_map' => array(
-            'order' => 'order',
-          ),
-          '#settings' => array(
-          ),
-        ),
-      ),
-    ),
-    '#actions' => array(
-      array(
-        '#name' => 'uc_recurring_renewal_email',
-        '#title' => t('Send an order email regarding order renewal.'),
-        '#argument_map' => array(
-          'order' => 'order',
-          'recurring_fee' => 'recurring_fee',
-        ),
-        '#settings' => array(
-          'from' => uc_store_email_from(),
-          'addresses' => '[order-email]',
-          'subject' => uc_get_message('uc_recurring_renewal_completed_subject'),
-          'message' => uc_get_message('uc_recurring_renewal_completed_message'),
-          'format' => 1,
-        ),
-      ),
-    ),
-  );
-  $predicates['uc_recurring_renewal_email_failed'] = array(
-    '#title' => t('Notify customer when a renewal has failed.'),
-    '#trigger' => 'uc_recurring_renewal_failed',
-    '#class' => 'notification',
-    '#status' => 0,
-    '#weight' => 1,
-    '#conditions' => array(
-      '#operator' => 'AND',
-      '#conditions' => array(
-        array(
-          '#name' => 'uc_recurring_renewal_status_condition',
-          '#title' => t('If the order contains product renewal.'),
-          '#argument_map' => array(
-            'order' => 'order',
-          ),
-          '#settings' => array(
-          ),
-        ),
-      ),
-    ),
-    '#actions' => array(
-      array(
-        '#name' => 'uc_recurring_renewal_email',
-        '#title' => t('Send an order email regarding order renewal.'),
-        '#argument_map' => array(
-          'order' => 'order',
-          'recurring_fee' => 'recurring_fee',
-        ),
-        '#settings' => array(
-          'from' => uc_store_email_from(),
-          'addresses' => '[order-email]',
-          'subject' => uc_get_message('uc_recurring_renewal_failed_subject'),
-          'message' => uc_get_message('uc_recurring_renewal_failed_message'),
-          'format' => 1,
-        ),
-      ),
-    ),
-  );
-  $predicates['uc_recurring_renewal_email_expired'] = array(
-    '#title' => t('Notify customer when a renewal has expired due to failed payment.'),
-    '#trigger' => 'uc_recurring_renewal_failed',
-    '#class' => 'notification',
-    '#status' => 0,
-    '#weight' => 1,
-    '#conditions' => array(
-      '#operator' => 'AND',
-      '#conditions' => array(
-        array(
-          '#name' => 'uc_recurring_renewal_status_condition',
-          '#title' => t('If the order contains product renewal.'),
-          '#argument_map' => array(
-            'order' => 'order',
-          ),
-          '#settings' => array(
-          ),
-        ),
-        array(
-          '#name' => 'uc_recurring_renewal_expired_condition',
-          '#title' => t('If the recurring fee has expired.'),
-          '#argument_map' => array(
-            'recurring_fee' => 'recurring_fee',
-          ),
-          '#settings' => array(
-          ),
-        ),
-      ),
-    ),
-    '#actions' => array(
-      array(
-        '#name' => 'uc_recurring_renewal_email',
-        '#title' => t('Send an order email regarding order renewal.'),
-        '#argument_map' => array(
-          'order' => 'order',
-          'recurring_fee' => 'recurring_fee',
-        ),
-        '#settings' => array(
-          'from' => uc_store_email_from(),
-          'addresses' => '[order-email]',
-          'subject' => uc_get_message('uc_recurring_renewal_expired_subject'),
-          'message' => uc_get_message('uc_recurring_renewal_expired_message'),
-          'format' => 1,
-        ),
-      ),
-    ),
-  );
-
-  return $predicates;
-}
-
-/**
- * Implements hook_ca_condition().
- */
-function uc_recurring_ca_condition() {
-  $conditions['uc_recurring_renewal_status_condition'] = array(
-    '#title' => t('Check if the order is a renewal'),
-    '#description' => t('Returns TRUE if the current order is a renewal.'),
-    '#category' => t('Order: Recurring'),
-    '#callback' => 'uc_recurring_condition_order_contains_renewals',
-    '#arguments' => array(
-      'order' => array(
-        '#entity' => 'uc_order',
-        '#title' => t('Order'),
-      ),
-    ),
-  );
-  $conditions['uc_recurring_renewal_expired_condition'] = array(
-    '#title' => t('Check if the recurring fee has expired'),
-    '#description' => t('Returns TRUE if the current recurring fee has expired.'),
-    '#category' => t('Order: Recurring'),
-    '#callback' => 'uc_recurring_condition_order_has_expired',
-    '#arguments' => array(
-      'recurring_fee' => array(
-        '#entity' => 'uc_recurring_fee',
-        '#title' => t('Recurring Fee'),
-      ),
-    ),
-  );
-
-  return $conditions;
-}
-
-/**
- * Implements hook_ca_action().
- */
-function uc_recurring_ca_action() {
-  // Send an email to an order with a role expiration
-  $actions['uc_recurring_renewal_email'] = array(
-    '#title' => t('Send an order email regarding order renewal.'),
-    '#category' => t('Notification'),
-    '#callback' => 'uc_recurring_action_renewal_email',
-    '#arguments' => array(
-      'order' => array(
-        '#entity' => 'uc_order',
-        '#title' => t('Order'),
-      ),
-      'recurring_fee' => array(
-        '#entity' => 'uc_recurring_fee',
-        '#title' => t('Recurring Fee'),
-      ),
-    ),
-  );
-
-  return $actions;
-}
-
-/**
- * Implements hook_ca_trigger().
- */
-function uc_recurring_ca_trigger() {
-  $order = array(
-    '#entity' => 'uc_order',
-    '#title' => t('Order'),
-  );
-  $recurring_fee = array(
-    '#entity' => 'uc_recurring_fee',
-    '#title' => t('Recurring Fee'),
-  );
-
-  $triggers['uc_recurring_renewal_complete'] = array(
-    '#title' => t('Recurring order renewal succeeded'),
-    '#category' => t('Recurring Fees'),
-    '#arguments' => array(
-      'order' => $order,
-      'recurring_fee' => $recurring_fee,
-    ),
-  );
-
-  $triggers['uc_recurring_renewal_failed'] = array(
-    '#title' => t('Recurring order renewal failed'),
-    '#category' => t('Recurring Fees'),
-    '#arguments' => array(
-      'order' => $order,
-      'recurring_fee' => $recurring_fee,
-    ),
-  );
-
-  $triggers['uc_recurring_cancel'] = array(
-    '#title' => t('Recurring order cancelled'),
-    '#category' => t('Recurring Fees'),
-    '#arguments' => array(
-      'order' => $order,
-      'recurring_fee' => $recurring_fee,
-    ),
-  );
-
-  $triggers['uc_recurring_renewal_expired'] = array(
-    '#title' => t('Recurring order renewal expired'),
-    '#category' => t('Recurring Fees'),
-    '#arguments' => array(
-      'order' => $order,
-      'recurring_fee' => $recurring_fee,
-    ),
-  );
-
-  return $triggers;
-}
-
-/**
- * Check if the order contains a renewal product.
- *
- * @param $order
- *   The order object.
- * @param $recurring_fee
- *   The recurring fee object.
- * @param $settings
- *   The order settings.
- */
-function uc_recurring_condition_order_contains_renewals($order, $settings) {
-  foreach ($order->products as $products) {
-    if (!empty($products->data['recurring_fee'])) {
-      return TRUE;
-    }
-  }
-  return FALSE;
-}
-
-/**
- * Check if the recurring fee has expired.
- */
-function uc_recurring_condition_order_has_expired($recurring_fee, $settings) {
-  return ($recurring_fee->remaining_intervals == 0);
-}
-
-/**
- * Send an email with order and recurring fee replacement tokens.
- *
- * The recipients, subject, and message fields take order token replacements.
- *
- * @see uc_recurring_action_renewal_email_form()
- */
-function uc_recurring_action_renewal_email($order, $recurring_fee, $settings) {
-  $account = uc_order_user_load($order);
-
-  // Token replacements for the subject and body
-  $settings['replacements'] = array(
-    'global' => NULL,
-    'order' => $order,
-    'user' => $account,
-    'recurring_fee' => $recurring_fee,
-  );
-
-  // Replace tokens and parse recipients.
-  $recipients = array();
-  $addresses = token_replace_multiple($settings['addresses'], $settings['replacements']);
-  foreach (explode("\n", $addresses) as $address) {
-    $recipients[] = trim($address);
-  }
-
-  // Send to each recipient.
-  foreach ($recipients as $email) {
-    $sent = drupal_mail('uc_order', 'action-mail', $email, uc_store_mail_recipient_language($email), $settings, $settings['from']);
-
-    if (!$sent['result']) {
-      watchdog('ca', 'Attempt to e-mail @email concerning order @order_id failed.', array('@email' => $email, '@order_id' => $order->order_id), WATCHDOG_ERROR);
-    }
-  }
-}
-
-/**
- * Email settings form.
- *
- * @see uc_recurring_action_renewal_email()
- */
-function uc_recurring_action_renewal_email_form($form_state, $settings = array()) {
-  // ca_build_email_form() function seems to be incorrectly part of uc_roles so
-  // we add this hack to include the uc_roles.ca.inc here.
-  module_load_include('inc', 'uc_roles', 'uc_roles.ca');
-  $settings += array(
-    'from' => uc_store_email_from(),
-    'addresses' => '[order-email]',
-    'subject' => uc_get_message('uc_recurring_renewal_completed_subject'),
-    'message' => uc_get_message('uc_recurring_renewal_completed_message'),
-    'format' => 1,
-  );
-  return ca_build_email_form($form_state, $settings, array('global', 'order', 'user', 'recurring_fee'));
-}
-
-
diff --git a/uc_recurring.info b/uc_recurring.info
index 6ac4151..3bbad33 100644
--- a/uc_recurring.info
+++ b/uc_recurring.info
@@ -7,7 +7,6 @@ php = 5.0
 
 files[] = uc_recurring.admin.inc
 files[] = uc_recurring.api.php
-files[] = uc_recurring.ca.inc
 files[] = uc_recurring.install
 files[] = uc_recurring.module
 files[] = uc_recurring.pages.inc
diff --git a/uc_recurring.module b/uc_recurring.module
index 06fcc7e..85b15fc 100644
--- a/uc_recurring.module
+++ b/uc_recurring.module
@@ -377,8 +377,7 @@ function uc_recurring_renew($fee) {
     uc_recurring_fee_user_save($fee);
 
     module_invoke_all('recurring_renewal_completed', $order, $fee);
-    // @todo - replace with rules
-    //ca_pull_trigger('uc_recurring_renewal_complete', $order, $fee);
+    rules_invoke_event('uc_recurring_renewal_complete', $order, $fee);
 
     // Return the new order ID.
     return $order->order_id;
@@ -405,8 +404,7 @@ function uc_recurring_renew($fee) {
     }
 
     module_invoke_all('recurring_renewal_failed', $order, $fee);
-    // @todo - replace with rules
-    //ca_pull_trigger('uc_recurring_renewal_failed', $order, $fee);
+    rules_invoke_event('uc_recurring_renewal_failed', $order, $fee);
 
     uc_order_comment_save($fee->order_id, $user->uid, t('New recurring fee failed on order <a href="@store-orders">@order_id</a>.', array('@store-orders' => url('admin/store/orders/' . $order->order_id), '@order_id' => $order->order_id)));
   }
@@ -490,8 +488,7 @@ function uc_recurring_expire($fee) {
   $fee->status = UC_RECURRING_FEE_STATUS_EXPIRED;
   uc_recurring_fee_user_save($fee);
 
-  // @todo - replace with rules
-  //ca_pull_trigger('uc_recurring_renewal_expired', $order, $fee);
+  rules_invoke_event('uc_recurring_renewal_expired', $order, $fee);
 }
 
 /**
@@ -795,8 +792,7 @@ function uc_recurring_fee_cancel($rfid, $fee = NULL) {
   module_invoke_all('uc_recurring_cancel', $fee);
 
   $order = uc_order_load($fee->order_id);
-  // @todo - replace with rules
-  //ca_pull_trigger('uc_recurring_cancel', $order, $fee);
+  rules_invoke_event('uc_recurring_cancel', $order, $fee);
 }
 
 /**
diff --git a/uc_recurring.rules.inc b/uc_recurring.rules.inc
index f9f78a6..34a53b0 100644
--- a/uc_recurring.rules.inc
+++ b/uc_recurring.rules.inc
@@ -6,7 +6,7 @@
  */
 
 /**
- * Implement hook_rules_data_info().
+ * Implements hook_rules_data_info().
  */
 function uc_recurring_rules_data_info() {
   $types['uc_recurring'] = array(
@@ -18,7 +18,57 @@ function uc_recurring_rules_data_info() {
 }
 
 /**
- * Implement hook_rules_event_info().
+ * Implements hook_rules_action_info().
+ */
+function uc_recurring_rules_action_info() {
+  // Adapted from uc_roles.rules.inc.
+  $email_args = array(
+    'from' => array(
+      'type' => 'text',
+      'label' => t('Sender'),
+    ),
+    'addresses' => array(
+      'type' => 'text',
+      'label' => t('Recipients'),
+    ),
+    'subject' => array(
+      'type' => 'text',
+      'label' => t('Subject'),
+    ),
+    'message' => array(
+      'type' => 'text',
+      'label' => t('Message'),
+    ),
+    'format' => array(
+      'type' => 'text',
+      'label' => t('Message format'),
+      'options list' => 'uc_recurring_message_formats',
+    ),
+  );
+
+  // Send an email to an order with a role expiration
+  $actions['uc_recurring_renewal_email'] = array(
+    'label' => t('Send an order email regarding order renewal.'),
+    'group' => t('Recurring Fee'),
+    'parameter' => array(
+      'order' => array(
+        'type' => 'uc_order',
+        'label' => t('Order'),
+      ),
+      'recurring_fee' => array(
+        'type' => 'uc_recurring_fee',
+        'label' => t('Recurring Fee'),
+        'restriction' => 'selector',
+      ),
+    ) + $email_args,
+  );
+
+  return $actions;
+}
+
+
+/**
+ * Implements hook_rules_event_info().
  */
 function uc_recurring_rules_event_info() {
   $events['uc_recurring_renewal_complete'] = array(
@@ -85,7 +135,7 @@ function uc_recurring_rules_event_info() {
 }
 
 /**
- * Implement hook_rules_condition_info().
+ * Implements hook_rules_condition_info().
  */
 function uc_recurring_rules_condition_info() {
   $conditions['uc_recurring_condition_order_contains_renewals'] = array(
@@ -117,6 +167,23 @@ function uc_recurring_rules_condition_info() {
 }
 
 /**
+ * Options list callback for message formats.
+ */
+function uc_recurring_message_formats() {
+  // Copied from uc_roles.rules.inc.
+  global $user;
+
+  $options = array();
+  $formats = filter_formats($user);
+  foreach ($formats as $format) {
+    $options[$format->format] = $format->name;
+  }
+
+  return $options;
+}
+
+
+/**
  * Check if the order contains a renewal product.
  *
  * @param $order
@@ -139,3 +206,45 @@ function uc_recurring_condition_order_contains_renewals($order) {
 function uc_recurring_condition_order_has_expired($recurring_fee, $settings) {
   return ($recurring_fee->remaining_intervals == 0);
 }
+
+/**
+ * Send an email with order and recurring fee replacement tokens.
+ *
+ * The recipients, subject, and message fields take order token replacements.
+ *
+ */
+function uc_recurring_renewal_email($order, $recurring_fee, $from, $addresses, $subject, $message, $format) {
+  $settings = array(
+    'from' => $from,
+    'addresses' => $addresses,
+    'subject' => $subject,
+    'message' => $message,
+    'format' => $format,
+  );
+
+  $account = uc_order_user_load($order);
+
+  // Token replacements for the subject and body
+  $settings['replacements'] = array(
+    'global' => NULL,
+    'order' => $order,
+    'user' => $account,
+    'recurring_fee' => $recurring_fee,
+  );
+
+  // Replace tokens and parse recipients.
+  $recipients = array();
+  $addresses = token_replace($settings['addresses'], $settings['replacements']);
+  foreach (explode("\n", $addresses) as $address) {
+    $recipients[] = trim($address);
+  }
+
+  // Send to each recipient.
+  foreach ($recipients as $email) {
+    $sent = drupal_mail('uc_order', 'action-mail', $email, uc_store_mail_recipient_language($email), $settings, $settings['from']);
+
+    if (!$sent['result']) {
+      watchdog('uc_recurring', 'Attempt to e-mail @email concerning order @order_id failed.', array('@email' => $email, '@order_id' => $order->order_id), WATCHDOG_ERROR);
+    }
+  }
+}
\ No newline at end of file
diff --git a/uc_recurring.rules_defaults.inc b/uc_recurring.rules_defaults.inc
index 4d17021..740ad6e 100644
--- a/uc_recurring.rules_defaults.inc
+++ b/uc_recurring.rules_defaults.inc
@@ -6,7 +6,7 @@
  */
 
 /**
- * Implement hook_default_rules_configuration().
+ * Implements hook_default_rules_configuration().
  */
 function uc_recurring_default_rules_configuration() {
   $configs = array();
@@ -31,5 +31,71 @@ function uc_recurring_default_rules_configuration() {
     ));
   $configs['uc_recurring_renewal_paid'] = $rule;
 
+  $rule = rules_reaction_rule();
+  $rule->label = t('Notify customer when a renewal has occurred.');
+  $rule->weight = 2;
+  $rule->active = FALSE;
+  $rule->event('uc_recurring_renewal_complete')
+    ->condition('uc_recurring_condition_order_contains_renewals', array(
+      'order:select' => 'order',
+    ))
+    ->action('uc_recurring_renewal_email', array(
+      'order:select' => 'order',
+      'recurring_fee:select' => 'recurring_fee',
+      'from' => uc_store_email_from(),
+      'addresses' => '[order:email]',
+      'subject' => uc_get_message('uc_recurring_renewal_completed_subject'),
+      'message' => uc_get_message('uc_recurring_renewal_completed_message'),
+      'format' => filter_fallback_format(),
+    ));
+  $configs['uc_recurring_renewal_email_completed'] = $rule;
+
+  $rule = rules_reaction_rule();
+  $rule->label = t('Notify customer when a renewal has failed.');
+  $rule->weight = 1;
+  $rule->active = FALSE;
+  $rule->event('uc_recurring_renewal_failed')
+    ->condition(rules_and()
+      ->condition('uc_recurring_condition_order_contains_renewals', array(
+        'order:select' => 'order',
+      ))
+      //@@@ Um, rules_and() what exactly? Some way to differentiate between
+      // this and uc_recurring_renewal_email_expired maybe?
+    )
+    ->action('uc_recurring_renewal_email', array(
+      'order:select' => 'order',
+      'recurring_fee:select' => 'recurring_fee',
+      'from' => uc_store_email_from(),
+      'addresses' => '[order:email]',
+      'subject' => uc_get_message('uc_recurring_renewal_failed_subject'),
+      'message' => uc_get_message('uc_recurring_renewal_failed_message'),
+      'format' => filter_fallback_format(),
+    ));
+  $configs['uc_recurring_renewal_email_failed'] = $rule;
+
+  $rule = rules_reaction_rule();
+  $rule->label = t('Notify customer when a renewal has expired due to failed payment.');
+  $rule->weight = 1;
+  $rule->active = FALSE;
+  $rule->event('uc_recurring_renewal_failed')
+    ->condition(rules_and()
+      ->condition('uc_recurring_condition_order_contains_renewals', array(
+        'order:select' => 'order',
+      ))
+      ->condition('uc_recurring_condition_order_has_expired', array(
+        'recurring_fee:select' => 'recurring_fee',
+      ))
+    )
+    ->action('uc_recurring_renewal_email', array(
+      'order:select' => 'order',
+      'recurring_fee:select' => 'recurring_fee',
+      'from' => uc_store_email_from(),
+      'addresses' => '[order:email]',
+      'subject' => uc_get_message('uc_recurring_renewal_expired_subject'),
+      'message' => uc_get_message('uc_recurring_renewal_expired_message'),
+      'format' => filter_fallback_format(),
+    ));
+  $configs['uc_recurring_renewal_email_expired'] = $rule;
+
   return $configs;
 }
-- 
1.7.7

