diff -urpN uc_promo/uc_promo.admin.inc uc_promo/uc_promo.admin.inc
--- uc_promo/uc_promo.admin.inc	1969-12-31 19:00:00.000000000 -0500
+++ uc_promo/uc_promo.admin.inc	2009-04-10 11:08:42.000000000 -0400
@@ -0,0 +1,161 @@
+<?php

+// $Id$ uc_promo.admin.inc,v 2.1 2009/04/09 11:06:00 dwkitchen Exp $

+

+// Displays an admin table for existing promotions and a settings form.

+function uc_promo_admin() {

+  $header = array(

+    array('data' => t('ID'), 'field' => 'promo_id'),

+    array('data' => t('Created'), 'field' => 'created', 'sort' => 'desc'),

+    array('data' => t('Name'), 'field' => 'promo_name'),

+    array('data' => t('Status'), 'field' => 'promo_status'),

+    t('Operations'),

+  );

+

+  $rows = array();

+

+  // Get all the promotions from the database.

+  $result = pager_query("SELECT * FROM {uc_promos}". tablesort_sql($header), 30);

+  while ($data = db_fetch_array($result)) {

+    $ops = array(

+      l(t('codes'), 'admin/store/settings/promo/'. $data['promo_id'] .'/codes'),

+      l(t('edit'), 'admin/store/settings/promo/'. $data['promo_id'] .'/edit'),

+      l(t('delete'), 'admin/store/settings/promo/'. $data['promo_id'] .'/delete'),

+    );

+

+    $row = array(

+      check_plain($data['promo_id']),

+      array('data' => format_date($data['created'], 'short'), 'style' => 'white-space: nowrap'),

+      check_plain($data['promo_name']),

+      $data['promo_status'] ? t('Active') : t('Disabled'),

+      array('data' => implode(' ', $ops), 'style' => 'white-space: nowrap;'),

+    );

+

+    $rows[] = $row;

+  }

+

+  if (count($rows) == 0) {

+    $rows[] = array(

+      array('data' => t('No promotions created yet.'), 'colspan' => 4),

+    );

+  }

+

+  return theme('table', $header, $rows) . theme('pager') . l(t('Add a promotion.'), 'admin/store/settings/promo/add');

+}

+

+// Form builder for adding or editing a promotion.

+function uc_promo_promotion_form($form_state, $promo = null) {

+  $form = array();

+

+  $form['promo_id'] = array (

+    '#type' => 'value',

+    '#value' => empty ($promo) ? 0 : $promo['promo_id']

+  );

+

+  $form['promo_name'] = array(

+    '#type' => 'textfield',

+    '#title' => t('Name'),

+    '#description' => t('Displayed on the admin overview and to the user when they redeem a code for this promotion.'),

+    '#max_length' => 255,

+    '#default_value' => empty ($promo) ? '' : $promo['promo_name'],

+    '#required' => TRUE,

+  );

+

+  $form['landing_page'] = array(

+    '#type' => 'textfield',

+    '#title' => t('Landing page'),

+    '#description' => t('Specify the path to which users who redeem this promotion should be redirected.<br /><a href="!url">Uses global tokens.</a>', array('!url' => url('admin/store/help/tokens'))),

+    '#default_value' => empty ($promo) ? '' : $promo['landing_page'],

+    '#size' => 32,

+    '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='),

+  );

+

+  $form['redeem_once'] = array(

+    '#type' => 'checkbox',

+    '#title' => t('Users can only redeem this promotion once with any code.'),

+    '#description' => t('Use this to prevent users from taking advantage of unlimited use promotion codes.'),

+    '#default_value' =>  empty ($promo) ? '' : $promo['redeem_once'],

+  );

+

+  $form['promo_status'] = array(

+    '#type' => 'radios',

+    '#title' => t('Status'),

+    '#options' => array(

+      0 => t('Disabled'),

+      1 => t('Active'),

+    ),

+    '#default_value' => empty ($promo) ? 1 : $promo['promo_status'],

+    '#required' => TRUE,

+  );

+

+  $form['save'] = array(

+    '#type' => 'submit',

+    '#value' => t('Save'),

+    '#suffix' => l(t('Cancel'), 'admin/store/settings/promo'),

+  );

+

+  return $form;

+}

+

+function uc_promo_promotion_form_submit($form, &$form_state) {

+  uc_promo_save_promotion($form_state['values']);

+  drupal_set_message(t('Promotion saved.'));

+  $form_state['redirect'] = 'admin/store/settings/promo';

+}

+

+// Form builder for the promo admin settings form.

+function uc_promo_admin_form() {

+  $form = array();

+

+  $form['uc_promo_login_redirect'] = array(

+    '#type' => 'checkbox',

+    '#title' => t('Redirect users without access to redeem promotions to the login page.'),

+    '#description' => t('Check this setting if authenticated users has access but anonymous users do not.'),

+    '#default_value' => variable_get('uc_promo_login_redirect', TRUE),

+  );

+  $form['uc_promo_delete_behavior'] = array(

+    '#type' => 'radios',

+    '#title' => t('Promotion delete behavior'),

+    '#options' => array(

+      0 => t('Delete the promotion and any related promotional codes.'),

+      1 => t('Delete the promotion and any related unredeemed promotional codes.'),

+      2 => t('Delete the promotion.'),

+    ),

+    '#default_value' => variable_get('uc_promo_delete_behavior', 0),

+  );

+

+  return system_settings_form($form);

+}

+

+// Form buider for deleting a promotion.

+function uc_promo_delete_promotion_form ($form_state, $promo = null) {

+  $form = array();

+

+  // Fail if the promotion doesn't exist.

+  if (empty ($promo)) {

+    drupal_set_message(t("The specified promotion doesn't exist."), 'error');

+    drupal_goto('admin/store/settings/promo');

+  }

+

+  $form['promo_id'] = array(

+    '#type' => 'value',

+    '#value' => $promo['promo_id'],

+  );

+

+  return confirm_form(

+    $form,

+    t('Delete promotion'),

+    'admin/store/settings/promo',

+    t('Are you sure you want to delete %name?', array('%name' => $data['promo_name'])),

+    t('Delete')

+  );

+}

+

+function uc_promo_delete_promotion_form_submit($form, &$form_state) {

+  uc_promo_delete_promotion($form_state['values']['promo_id']);

+

+  drupal_set_message(t('The promotion has been deleted.'));

+

+

+  $form_state['redirect'] = 'admin/store/settings/promo';

+  $form_state['nid'] = $node->nid;

+}

diff -urpN uc_promo/uc_promo.info uc_promo/uc_promo/uc_promo.info
--- uc_promo/uc_promo.info	2008-12-10 11:45:54.000000000 -0500
+++ uc_promo/uc_promo.info	2009-04-10 11:08:42.000000000 -0400
@@ -1,11 +1,10 @@
-; $Id: uc_promo.info,v 1.1 2008/12/10 16:11:12 rszrama Exp $
+; $Id: uc_promo.info,v 2.1 2009/04/09 11:06:00 dwkitchen Exp $
 name = Promotions
+version = 6.x-1.-x-dev
 description = Create promotions with redeemable promotional codes that users can redeem to perform special actions in your store.
-dependencies = token workflow_ng
+dependencies[] = token
+dependencies[] = uc_roles
+dependencies[] = rules
 package = Ubercart - extra
-
-; Information added by drupal.org packaging script on 2008-12-10
-version = "5.x-1.0"
 project = "uc_promo"
-datestamp = "1228927554"
-
+core = 6.x
diff -urpN uc_promo/uc_promo.install uc_promo/uc_promo.install
--- uc_promo/uc_promo.install	2008-12-10 11:11:12.000000000 -0500
+++ uc_promo/uc_promo.install	2009-04-10 11:08:44.000000000 -0400
@@ -1,47 +1,184 @@
 <?php
-// $Id: uc_promo.install,v 1.1 2008/12/10 16:11:12 rszrama Exp $
+// $Id: uc_promo.install,v 2.1 2009/04/09 11:06:00 dwkitchen Exp $
+/**
+ * @file
+ * Create promotions with redeemable promotional codes that users can redeem to
+ *   perform special actions in your store.
+ */
+
+/**
+ * Implementation of hook_schema().
+ */
+function uc_promo_schema() {
+  $schema['uc_promos'] = array(
+    'description' => t('TODO'),
+    'fields' => array(
+      'promo_id' => array(
+        'description' => t('TODO'),
+        'type' => 'serial',
+        'not null' => TRUE,
+      ),
+      'promo_name' => array(
+        'description' => t('TODO'),
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'landing_page' => array(
+        'description' => t('TODO'),
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'redeem_once' => array(
+        'description' => t('TODO'),
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'promo_status' => array(
+        'description' => t('TODO'),
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'created' => array(
+        'description' => t('TODO'),
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'primary key' => array('promo_id'),
+  );
+
+  $schema['uc_promo_codes'] = array(
+    'description' => t('TODO'),
+    'fields' => array(
+      'promo_code' => array(
+        'description' => t('TODO'),
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'promo_id' => array(
+        'description' => t('TODO'),
+        'type' => 'int',
+        'not null' => TRUE,
+
+      ),
+      'promo_code_type' => array(
+        'description' => t('TODO'),
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'bulkgen_seed' => array(
+        'description' => t('TODO'),
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'redemptions' => array(
+        'description' => t('TODO'),
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'max_redemptions' => array(
+        'description' => t('TODO'),
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'expiration' => array(
+        'description' => t('TODO'),
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'created' => array(
+        'description' => t('TODO'),
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'last_redemption' => array(
+        'description' => t('TODO'),
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'indexes' => array(
+      'promo_id' => array('promo_id'),
+    ),
+    'primary key' => array('promo_code'),
+  );
+
+  $schema['uc_promo_code_redemptions'] = array(
+    'description' => t('TODO'),
+    'fields' => array(
+      'redemption_id' => array(
+        'description' => t('TODO'),
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'promo_code' => array(
+        'description' => t('TODO'),
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'bulk_code' => array(
+        'description' => t('TODO'),
+        'type' => 'varchar',
+        'length' => 30,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'promo_id' => array(
+        'description' => t('TODO'),
+        'type' => 'int',
+        'not null' => TRUE,
+      ),
+      'uid' => array(
+        'description' => t('TODO'),
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'redeemed' => array(
+        'description' => t('TODO'),
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'indexes' => array(
+      'promo_code' => array('promo_code'),
+    ),
+  );
+
+  return $schema;
+}

 function uc_promo_install() {
-  switch ($GLOBALS['db_type']) {
-    case 'mysql':
-    case 'mysqli':
-      db_query("CREATE TABLE {uc_promos} (
-        promo_id int NOT NULL default 0,
-        promo_name varchar(255) NOT NULL default '',
-        landing_page varchar(255) NOT NULL default '',
-        redeem_once smallint NOT NULL default 0,
-        promo_status smallint NOT NULL default 0,
-        created int NOT NULL default 0,
-        PRIMARY KEY (promo_id)
-      ) /*!40100 DEFAULT CHARACTER SET UTF8 */ ;");
-      db_query("CREATE TABLE {uc_promo_codes} (
-        promo_code varchar(32) NOT NULL default '',
-        promo_id int NOT NULL default 0,
-        promo_code_type smallint NOT NULL default 0,
-        bulkgen_seed varchar(128) NOT NULL default '',
-        redemptions int NOT NULL default 0,
-        max_redemptions int NOT NULL default 0,
-        expiration int NOT NULL default 0,
-        created int NOT NULL default 0,
-        last_redemption int NOT NULL default 0,
-        PRIMARY KEY (promo_code),
-        INDEX promo_id (promo_id)
-      ) /*!40100 DEFAULT CHARACTER SET UTF8 */ ;");
-      db_query("CREATE TABLE {uc_promo_code_redemptions} (
-        redemption_id int NOT NULL default 0,
-        promo_code varchar(32) NOT NULL default '',
-        bulk_code varchar(30) NOT NULL default '',
-        promo_id int NOT NULL default 0,
-        uid int NOT NULL default 0,
-        redeemed int NOT NULL default 0,
-        INDEX promo_code (promo_code)
-      ) /*!40100 DEFAULT CHARACTER SET UTF8 */ ;");
-    break;
-  }
+  // Create tables.
+  drupal_install_schema('uc_promo');
 }

 function uc_promo_uninstall() {
-  db_query("DROP TABLE {uc_promos}");
-  db_query("DROP TABLE {uc_promo_codes}");
-  db_query("DROP TABLE {uc_promo_code_redemptions}");
+  // Remove tables.
+  drupal_uninstall_schema('uc_promo');
+
 }
diff -urpN uc_promo/uc_promo.module uc_promo/uc_promo.module
--- uc_promo/uc_promo.module	2008-12-10 11:11:12.000000000 -0500
+++ uc_promo/uc_promo.module	2009-04-10 11:07:06.000000000 -0400
@@ -1,1111 +1,1066 @@
-<?php
-
-// $Id: uc_promo.module,v 1.1 2008/12/10 16:11:12 rszrama Exp $
-
-/**
- * @file
- * Create promotions with redeemable promotional codes that users can redeem to
- *   perform special actions in your store.
- */
-
-
-/**
- * Implementation of hook_menu().
- */
-function uc_promo_menu($may_cache) {
-  $items = array();
-
-  if ($may_cache) {
-    $items[] = array(
-      'path' => 'promo',
-      'title' => t('Redeem a promotion'),
-      'description' => t('Form to redeem a promotional code.'),
-      'callback' => 'uc_promo_redeem',
-      'callback arguments' => array(''),
-      'access' => TRUE,
-      'type' => MENU_CALLBACK,
-    );
-    $items[] = array(
-      'path' => 'admin/store/settings/promo',
-      'title' => t('Promotions'),
-      'description' => t('Setup and administer your promotions.'),
-      'callback' => 'uc_promo_admin',
-      'access' => user_access('administer uc promotions'),
-      'type' => MENU_NORMAL_ITEM,
-    );
-    $items[] = array(
-      'path' => 'admin/store/settings/promo/overview',
-      'title' => t('Overview'),
-      'description' => t('Setup and administer your promotions.'),
-      'type' => MENU_DEFAULT_LOCAL_TASK,
-      'weight' => -10,
-    );
-    $items[] = array(
-      'path' => 'admin/store/settings/promo/add',
-      'title' => t('Add a promotion'),
-      'description' => t('Setup and administer your promotions.'),
-      'callback' => 'drupal_get_form',
-      'callback arguments' => array('uc_promo_promotion_form', 0),
-      'access' => user_access('administer uc promotions'),
-      'type' => MENU_LOCAL_TASK,
-      'weight' => 0,
-    );
-    $items[] = array(
-      'path' => 'admin/store/settings/promo/settings',
-      'title' => t('Settings'),
-      'description' => t('Adjust the general promotions settings.'),
-      'callback' => 'drupal_get_form',
-      'callback arguments' => array('uc_promo_admin_form'),
-      'access' => user_access('administer uc promotions'),
-      'type' => MENU_LOCAL_TASK,
-      'weight' => 10,
-    );
-  }
-  else {
-    if (arg(3) === 'promo' && intval(arg(4)) > 0) {
-      $items[] = array(
-        'path' => 'admin/store/settings/promo/'. arg(4) .'/edit',
-        'title' => t('Edit promotion'),
-        'description' => t('Delete an existing promotion.'),
-        'callback' => 'drupal_get_form',
-        'callback arguments' => array('uc_promo_promotion_form', arg(4)),
-        'access' => user_access('administer uc promotions'),
-        'type' => MENU_CALLBACK,
-      );
-      $items[] = array(
-        'path' => 'admin/store/settings/promo/'. arg(4) .'/delete',
-        'title' => t('Delete promotion'),
-        'description' => t('Delete a promotion.'),
-        'callback' => 'drupal_get_form',
-        'callback arguments' => array('uc_promo_delete_promotion_form', arg(4)),
-        'access' => user_access('administer uc promotions'),
-        'type' => MENU_CALLBACK,
-      );
-
-      if (arg(5) === 'codes') {
-        $items[] = array(
-          'path' => 'admin/store/settings/promo/'. arg(4) .'/codes',
-          'title' => t('Promotional codes'),
-          'description' => t('Administer promotional codes for a given promotion.'),
-          'callback' => 'uc_promo_codes',
-          'callback arguments' => array(arg(4)),
-          'access' => user_access('administer uc promotions'),
-          'type' => MENU_CALLBACK,
-        );
-        $items[] = array(
-          'path' => 'admin/store/settings/promo/'. arg(4) .'/codes/overview',
-          'title' => t('Overview'),
-          'description' => t('Administer promotional codes for a given promotion.'),
-          'type' => MENU_DEFAULT_LOCAL_TASK,
-          'weight' => -10,
-        );
-        $items[] = array(
-          'path' => 'admin/store/settings/promo/'. arg(4) .'/codes/add',
-          'title' => t('Add code'),
-          'description' => t('Add a promotional code for the promotion.'),
-          'callback' => 'drupal_get_form',
-          'callback arguments' => array('uc_promo_codes_form', arg(4), 0),
-          'access' => user_access('administer uc promotions'),
-          'type' => MENU_LOCAL_TASK,
-          'weight' => 0,
-        );
-        $items[] = array(
-          'path' => 'admin/store/settings/promo/'. arg(4) .'/codes/'. arg(6) .'/download',
-          'title' => t('Download bulk generated codes'),
-          'callback' => 'uc_promo_code_bulkgen',
-          'callback arguments' => array(arg(4), arg(6)),
-          'access' => user_access('administer uc promotions'),
-          'type' => MENU_CALLBACK,
-        );
-        $items[] = array(
-          'path' => 'admin/store/settings/promo/'. arg(4) .'/codes/'. arg(6) .'/edit',
-          'title' => t('Edit code'),
-          'description' => t('Edit a promotional code.'),
-          'callback' => 'drupal_get_form',
-          'callback arguments' => array('uc_promo_codes_form', arg(4), arg(6)),
-          'access' => user_access('administer uc promotions'),
-          'type' => MENU_CALLBACK,
-        );
-        $items[] = array(
-          'path' => 'admin/store/settings/promo/'. arg(4) .'/codes/'. arg(6) .'/delete',
-          'title' => t('Delete code'),
-          'description' => t('Delete a promotional code.'),
-          'callback' => 'drupal_get_form',
-          'callback arguments' => array('uc_promo_delete_code_form', arg(4), arg(6)),
-          'access' => user_access('administer uc promotions'),
-          'type' => MENU_CALLBACK,
-        );
-        $items[] = array(
-          'path' => 'admin/store/settings/promo/'. arg(4) .'/codes/'. arg(6) .'/redemptions',
-          'title' => t('Promotional code redemptions'),
-          'description' => t('A table of redemptions for a promotional code.'),
-          'callback' => 'uc_promo_redemptions',
-          'callback arguments' => array(arg(4), arg(6)),
-          'access' => user_access('administer uc promotions'),
-          'type' => MENU_CALLBACK,
-        );
-        $items[] = array(
-          'path' => 'admin/store/settings/promo/'. arg(4) .'/codes/back',
-          'title' => t('Back to promotions'),
-          'callback' => 'drupal_goto',
-          'callback arguments' => array('admin/store/settings/promo'),
-          'access' => user_access('administer uc promotions'),
-          'type' => MENU_LOCAL_TASK,
-          'weight' => 10,
-        );
-      }
-    }
-  }
-
-  return $items;
-}
-
-/**
- * Implementation of hook_perm().
- */
-function uc_promo_perm() {
-  return array('administer uc promotions', 'redeem uc promotions');
-}
-
-/**
- * Implementation of Workflow-ng hook_event_info().
- */
-function uc_promo_event_info() {
-  $events = array();
-
-  $result = db_query("SELECT promo_id, promo_name FROM {uc_promos}");
-  while ($promo = db_fetch_object($result)) {
-    $events['uc_promo_'. $promo->promo_id] = array(
-      '#label' => t('@name', array('@name' => $promo->promo_name)),
-      '#module' => 'Promotions',
-      '#arguments' => array(
-        'user' => array('#entity' => 'user', '#label' => t('User')),
-      ),
-    );
-  }
-
-  return $events;
-}
-
-/**
- * Implementation of Workflow-ng hook_action_info().
- */
-function uc_promo_action_info() {
-  $actions = array();
-
-  // Provide integration for File Downloads module.
-  if (module_exists('uc_file')) {
-    // Add a file download to a user's account.
-    $actions['uc_promo_user_add_download'] = array(
-      '#label' => t('Give a user a file download.'),
-      '#arguments' => array(
-        'user' => array('#entity' => 'user', '#label' => t('User account')),
-      ),
-      '#module' => t('Promotions'),
-    );
-  }
-
-  return $actions;
-}
-
-// Displays a form for redeeming promotions.
-function uc_promo_redeem() {
-  // Check for redemption access.
-  if (!user_access('redeem uc promotions')) {
-    if (variable_get('uc_promo_login_redirect', TRUE)) {
-      // Redirect if specified in the promotions settings.
-      drupal_set_message(t('You must login or create an account before you can redeem your promotional code.'));
-      drupal_goto('user/login', 'destination=promo');
-    }
-    else {
-      drupal_access_denied();
-    }
-  }
-
-  return drupal_get_form('uc_promo_redeem_form', $code);
-}
-
-// Form builder function for a promotional code redemption form.
-function uc_promo_redeem_form() {
-  $form = array();
-
-  $form['promo_code'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Promotional code'),
-    '#description' => t('Enter the code for the promotion you would like to redeem.'),
-    '#required' => TRUE,
-  );
-
-  $form['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Submit'),
-  );
-
-  return $form;
-}
-
-function uc_promo_redeem_form_validate($form_id, $form_values) {
-  global $user;
-
-  // Check to make sure the code is valid.
-  if (!uc_promo_validate_code($form_values['promo_code'], $user)) {
-    form_set_error('promo_code', t('You have entered an invalid promotional code.  Make sure you have entered code correctly and try again.'));
-  }
-}
-
-function uc_promo_redeem_form_submit($form_id, $form_values) {
-  global $user;
-
-  // Load the promo code data.
-  $code_data = uc_promo_load_code($form_values['promo_code']);
-
-  // Check if this is a bulk generated code; if so, we redeem it differently.
-  if ($code_data['promo_code'] !== $form_values['promo_code']) {
-    $bulk_code = $form_values['promo_code'];
-  }
-  else {
-    $bulk_code = NULL;
-  }
-
-  // Make the appropriate database updates for the redemption.
-  uc_promo_redeem_code($code_data['promo_code'], $user, $bulk_code);
-
-  $promo_data = uc_promo_load_promotion($code_data['promo_id']);
-
-  // Trigger any configurations assigned to this promotion's trigger.
-  workflow_ng_invoke_event('uc_promo_'. $code_data['promo_id'], $user);
-
-  // Send the user to the promotion's landing page.
-  $path = token_replace($promo_data['landing_page'], 'global');
-
-  return $path;
-}
-
-// Displays an admin table for existing promotions and a settings form.
-function uc_promo_admin() {
-  $header = array(
-    array('data' => t('ID'), 'field' => 'promo_id'),
-    array('data' => t('Created'), 'field' => 'created', 'sort' => 'desc'),
-    array('data' => t('Name'), 'field' => 'promo_name'),
-    array('data' => t('Status'), 'field' => 'promo_status'),
-    t('Operations'),
-  );
-
-  $rows = array();
-
-  // Get all the promotions from the database.
-  $result = pager_query("SELECT * FROM {uc_promos}". tablesort_sql($header), 30);
-  while ($data = db_fetch_array($result)) {
-    $ops = array(
-      l(t('codes'), 'admin/store/settings/promo/'. $data['promo_id'] .'/codes'),
-      l(t('edit'), 'admin/store/settings/promo/'. $data['promo_id'] .'/edit'),
-      l(t('delete'), 'admin/store/settings/promo/'. $data['promo_id'] .'/delete'),
-    );
-
-    $row = array(
-      check_plain($data['promo_id']),
-      array('data' => format_date($data['created'], 'short'), 'style' => 'white-space: nowrap'),
-      check_plain($data['promo_name']),
-      $data['promo_status'] ? t('Active') : t('Disabled'),
-      array('data' => implode(' ', $ops), 'style' => 'white-space: nowrap;'),
-    );
-
-    $rows[] = $row;
-  }
-
-  if (count($rows) == 0) {
-    $rows[] = array(
-      array('data' => t('No promotions created yet.'), 'colspan' => 4),
-    );
-  }
-
-  return theme('table', $header, $rows) . theme('pager') . l(t('Add a promotion.'), 'admin/store/settings/promo/add');
-}
-
-// Form builder for the promo admin settings form.
-function uc_promo_admin_form() {
-  $form = array();
-
-  $form['uc_promo_login_redirect'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Redirect users without access to redeem promotions to the login page.'),
-    '#description' => t('Check this setting if authenticated users has access but anonymous users do not.'),
-    '#default_value' => variable_get('uc_promo_login_redirect', TRUE),
-  );
-  $form['uc_promo_delete_behavior'] = array(
-    '#type' => 'radios',
-    '#title' => t('Promotion delete behavior'),
-    '#options' => array(
-      0 => t('Delete the promotion and any related promotional codes.'),
-      1 => t('Delete the promotion and any related unredeemed promotional codes.'),
-      2 => t('Delete the promotion.'),
-    ),
-    '#default_value' => variable_get('uc_promo_delete_behavior', 0),
-  );
-
-  return system_settings_form($form);
-}
-
-// Form builder for adding or editing a promotion.
-function uc_promo_promotion_form($promo_id) {
-  $form = array();
-
-  // Load the specified promotion; will be FALSE if $promo_id == 0.
-  $data = uc_promo_load_promotion($promo_id);
-
-  $form['promo_id'] = array(
-    '#type' => 'value',
-    '#value' => $data === FALSE ? 0 : $promo_id,
-  );
-
-  $form['promo_name'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Name'),
-    '#description' => t('Displayed on the admin overview and to the user when they redeem a code for this promotion.'),
-    '#max_length' => 255,
-    '#default_value' => $data['promo_name'],
-    '#required' => TRUE,
-  );
-
-  $form['landing_page'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Landing page'),
-    '#description' => t('Specify the path to which users who redeem this promotion should be redirected.<br /><a href="!url">Uses global tokens.</a>', array('!url' => url('admin/store/help/tokens'))),
-    '#default_value' => $data['landing_page'],
-    '#size' => 32,
-    '#field_prefix' => url(NULL, NULL, NULL, TRUE) . (variable_get('clean_url', 0) ? '' : '?q='),
-  );
-
-  $form['redeem_once'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Users can only redeem this promotion once with any code.'),
-    '#description' => t('Use this to prevent users from taking advantage of unlimited use promotion codes.'),
-    '#default_value' => $data['redeem_once'],
-  );
-
-  $form['promo_status'] = array(
-    '#type' => 'radios',
-    '#title' => t('Status'),
-    '#options' => array(
-      0 => t('Disabled'),
-      1 => t('Active'),
-    ),
-    '#default_value' => $data === FALSE ? 1 : $data['promo_status'],
-    '#required' => TRUE,
-  );
-
-  $form['save'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save'),
-    '#suffix' => l(t('Cancel'), 'admin/store/settings/promo'),
-  );
-
-  return $form;
-}
-
-function uc_promo_promotion_form_submit($form_id, $form_values) {
-  uc_promo_save_promotion($form_values);
-
-  drupal_set_message(t('Promotion saved.'));
-
-  return 'admin/store/settings/promo';
-}
-
-// Form buider for deleting a promotion.
-function uc_promo_delete_promotion_form($promo_id) {
-  $data = uc_promo_load_promotion($promo_id);
-  $form = array();
-
-  // Fail if the promotion doesn't exist.
-  if (!$data) {
-    drupal_set_message(t("The specified promotion doesn't exist."), 'error');
-    drupal_goto('admin/store/settings/promo');
-  }
-
-  $form['promo_id'] = array(
-    '#type' => 'value',
-    '#value' => $promo_id,
-  );
-
-  return confirm_form($form, t('Delete promotion'), 'admin/store/settings/promo', t('Are you sure you want to delete %name?', array('%name' => $data['promo_name'])), t('Delete'));
-}
-
-function uc_promo_delete_promotion_form_submit($form_id, $form_values) {
-  uc_promo_delete_promotion($form_values['promo_id']);
-
-  drupal_set_message(t('The promotion has been deleted.'));
-
-  return 'admin/store/settings/promo';
-}
-
-/**
- * Loads a promotion from the database.
- *
- * @param $promo_id
- *   The ID of the promotion to load.
- * @return
- *   An array of data for the promotion.
- */
-function uc_promo_load_promotion($promo_id) {
-  $result = db_query("SELECT * FROM {uc_promos} WHERE promo_id = %d", $promo_id);
-
-  return db_fetch_array($result);
-}
-
-/**
- * Saves or inserts a new promotion.
- *
- * @param $data
- *   An array of data representing the promotion.
- */
-function uc_promo_save_promotion($data) {
-  // First try to update an existing promotion.
-  if ($data['promo_id'] > 0) {
-    db_query("UPDATE {uc_promos} SET promo_name = '%s', landing_page = '%s', redeem_once = %d, promo_status = %d WHERE promo_id = %d",
-      $data['promo_name'], $data['landing_page'], $data['redeem_once'],
-      $data['promo_status'], $data['promo_id']);
-  }
-
-  // Otherwise insert a new promotion.
-  if ($data['promo_id'] === 0 || db_affected_rows() == 0) {
-    db_query("INSERT INTO {uc_promos} (promo_id, promo_name, landing_page, redeem_once, promo_status, created) VALUES (%d, '%s', '%s', %d, %d, %d)",
-      $data['promo_id'] == 0 ? db_next_id('{uc_promos}_promo_id') : $data['promo_id'],
-      $data['promo_name'], $data['landing_page'], $data['redeem_once'],
-      $data['promo_status'], time());
-  }
-}
-
-/**
- * Delete a promotion from the database.
- *
- * @param $promo_id
- *   The ID of the promotion to delete.
- */
-function uc_promo_delete_promotion($promo_id) {
-  // Delete related codes per the user settings.
-  switch (variable_get('uc_promo_delete_behavior', 0)) {
-    case 0:
-      // Delete any related promotional codes.
-      db_query("DELETE FROM {uc_promo_code_redemptions} WHERE promo_id = %d", $promo_id);
-      db_query("DELETE FROM {uc_promo_codes} WHERE promo_id = %d", $promo_id);
-      break;
-
-    case 1:
-      // Delete related promotional codes that haven't been redeemed.
-      db_query("DELETE FROM {uc_promo_codes} WHERE promo_id = %d AND redemptions > 0", $promo_id);
-      break;
-  }
-
-  // Get the name for use in the log entry.
-  $name = db_result(db_query("SELECT promo_name FROM {uc_promos} WHERE promo_id = %d", $promo_id));
-
-  // Delete the promotion itself.
-  db_query("DELETE FROM {uc_promos} WHERE promo_id = %d", $promo_id);
-
-  watchdog('uc_promo', t('Promotion %name deleted.', array('%name' => !empty($name) ? $name : $promo_id)));
-}
-
-function uc_promo_codes($promo_id) {
-  // Load the specified promo code; will be FALSE if $code == 0.
-  $data = uc_promo_load_promotion($promo_id);
-
-  if ($data === FALSE) {
-    drupal_set_message(t('Specified promotion does not exist.'), 'error');
-    drupal_goto('admin/store/settings/promo');
-  }
-
-  $output = t('Promotion: %name', array('%name' => check_plain($data['promo_name'])));
-
-  $header = array(
-    array('data' => t('Code'), 'field' => 'promo_code'),
-    array('data' => t('Type'), 'field' => 'promo_code_type'),
-    array('data' => t('Redemptions'), 'field' => 'redemptions'),
-    array('data' => t('Last redemption'), 'field' => 'last_redemption', 'sort' => 'desc'),
-    t('Operations'),
-  );
-
-  $rows = array();
-
-  // Get all the promotions from the database.
-  $result = pager_query("SELECT * FROM {uc_promo_codes} WHERE promo_id = %d". tablesort_sql($header), 30, 0, NULL, $promo_id);
-  while ($data = db_fetch_array($result)) {
-    // Create a download link for bulk generated codes.
-    if (!empty($data['bulkgen_seed'])) {
-      $download = '<br />'. l(t('Download codes.'), 'admin/store/settings/promo/'. $data['promo_id'] .'/codes/'. $data['promo_code'] .'/download');
-    }
-    else {
-      $download = '';
-    }
-
-    // Create an array of operations for the code.
-    $ops = array(
-      l(t('redemptions'), 'admin/store/settings/promo/'. $data['promo_id'] .'/codes/'. $data['promo_code'] .'/redemptions'),
-      l(t('edit'), 'admin/store/settings/promo/'. $data['promo_id'] .'/codes/'. $data['promo_code'] .'/edit'),
-      l(t('delete'), 'admin/store/settings/promo/'. $data['promo_id'] .'/codes/'. $data['promo_code'] .'/delete'),
-    );
-
-    switch ($data['promo_code_type']) {
-      case 1:
-        $type = t('Unlimited');
-        break;
-      case 2:
-        $type = t('Limited: @number', array('@number' => $data['max_redemptions']));
-        break;
-      case 3:
-        $type = t('Expires @date', array('@date' => format_date($data['expiration'], 'short')));
-        break;
-    }
-
-    $row = array(
-      'data' => array(
-        check_plain($data['promo_code']) . $download,
-        array('data' => $type, 'style' => 'white-space: nowrap;'),
-        $data['redemptions'],
-        array('data' => $data['last_redemption'] > 0 ? format_date($data['last_redemption'], 'short') : '-', 'style' => 'white-space: nowrap;'),
-        array('data' => implode(' ', $ops), 'style' => 'white-space: nowrap;'),
-      ),
-      'valign' => 'top',
-    );
-
-    $rows[] = $row;
-  }
-
-  if (count($rows) == 0) {
-    $rows[] = array(
-      array('data' => t('No promotional codes created yet.'), 'colspan' => 5),
-    );
-  }
-
-  $output .= theme('table', $header, $rows) . theme('pager') . l(t('Add promotional codes.'), 'admin/store/settings/promo/'. $promo_id .'/codes/add');
-
-  return $output;
-}
-
-function uc_promo_codes_form($promo_id, $code) {
-  $form = array();
-
-  // Load the specified promotion; will be FALSE if $code == 0.
-  $data = uc_promo_load_promotion($promo_id);
-
-  if ($data === FALSE) {
-    drupal_set_message(t('Specified promotion does not exist.'), 'error');
-    drupal_goto('admin/store/settings/promo');
-  }
-
-  $form['promo_name'] = array(
-    '#value' => t('Promotion: %name', array('%name' => check_plain($data['promo_name']))),
-  );
-
-  $form['promo_id'] = array(
-    '#type' => 'value',
-    '#value' => $promo_id,
-  );
-
-  // Load the specified promo code; will be FALSE if $code == 0.
-  $data = uc_promo_load_code($code);
-
-  $code_disabled = FALSE;
-
-  if ($data !== FALSE) {
-    $form['promo_code_orig'] = array(
-      '#type' => 'value',
-      '#value' => $code,
-    );
-
-    // Disable editing the code if we're dealing with a bulkgen code.
-    if (!empty($data['bulkgen_seed'])) {
-      $code_disabled = TRUE;
-    }
-    else {
-      // Otherwise disable it if it's already been redeemed.
-      $count = db_result(db_query("SELECT COUNT(*) FROM {uc_promo_code_redemptions} WHERE promo_code = '%s'", $code));
-      if ($count > 0) {
-        $code_disabled = TRUE;
-      }
-    }
-  }
-
-
-  $form['promo_code'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Promotional code'),
-    '#description' => t('Promotional codes may only contain letters, numbers, underscores, and dashes.<br />Codes may not be changed once they have been redeemed.<br/>Do NOT manually set a code to start with "BULKGEN".'),
-    '#default_value' => $data === FALSE ? '' : $data['promo_code'],
-    '#required' => $data === FALSE ? FALSE : TRUE,
-    '#disabled' => $code_disabled,
-  );
-
-  if ($code === 0) {
-    $form['bulk'] = array(
-      '#type' => 'fieldset',
-      '#title' => t('Bulk code generation'),
-      '#description' => t('You may choose to bulk generate alphanumeric promotional codes based on the settings below.<br />If so, the promotional code entered above will be ignored.'),
-      '#collapsible' => TRUE,
-      '#collapsed' => TRUE,
-    );
-    $form['bulk']['bulk_generate'] = array(
-      '#type' => 'checkbox',
-      '#title' => t('Bulk generate codes for this promotion.'),
-    );
-    /*$form['bulk']['code_rule'] = array(
-      '#type' => 'radios',
-      '#title' => t('Code generation rule'),
-      '#options' => array(
-        'numeric' => t('Random numeric promotional codes.'),
-        'alpha_numeric' => t('Random alpha-numeric promotional codes.'),
-      ),
-    );*/
-    $form['bulk']['number_codes'] = array(
-      '#type' => 'textfield',
-      '#title' => t('Number of codes'),
-      '#description' => t('Enter in the number of codes you want to generate.'),
-    );
-    $form['bulk']['code_length'] = array(
-      '#type' => 'select',
-      '#title' => t('Code length'),
-      '#options' => drupal_map_assoc(range(10, 30)),
-    );
-  }
-
-  $form['promo_code_type'] = array(
-    '#type' => 'radios',
-    '#title' => t('Promotional code type'),
-    '#description' => t('Limited or expirable promotional codes should specify their settings below.'),
-    '#options' => array(
-      1 => t('Unlimited number of redemptions'),
-      2 => t('Limited number of redemptions'),
-      3 => t('Expires on a certain date'),
-    ),
-    '#default_value' => $data['promo_code_type'],
-    '#required' => TRUE,
-  );
-
-  $form['max_redemptions'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Maximum number of redemptions'),
-    '#description' => t('Only applicable to limited promotional codes.'),
-    '#default_value' => $data['max_redemptions'],
-  );
-
-  $form['expiration'] = array(
-    '#type' => 'date',
-    '#title' => t('Expiration date'),
-    '#description' => t('The code will fail after 23:59 of the specified date.<br />Only applicable to expirable promotional codes.'),
-    '#default_value' => array(
-      'year' => date('Y', $data === FALSE ? time() : $data['expiration']),
-      'month' => date('n', $data === FALSE ? time() : $data['expiration']),
-      'day' => date('j', $data === FALSE ? time() : $data['expiration']),
-    ),
-  );
-
-  $form['save'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save'),
-  );
-
-  $form['save_add'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save and add another'),
-    '#suffix' => l(t('Cancel'), 'admin/store/settings/promo/'. $promo_id .'/codes'),
-  );
-
-  return $form;
-}
-
-function uc_promo_codes_form_validate($form_id, $form_values) {
-  // Fail if no promo code was entered and bulk generation was not selected.
-  if (!$form_values['bulk_generate'] && empty($form_values['promo_code'])) {
-    form_set_error('promo_code', t('You must specify a promotional code.'));
-  }
-
-  // Fail if the user entered an invalid bulk generated code length.
-  if ($form_values['bulk_generate'] && (intval($form_values['code_length']) < 10 || intval($form_values['code_length']) > 30)) {
-    form_set_error('code_length', t('You must specify a code length between 10 and 30.'));
-  }
-
-  // Fail if the user entered an invalid number of bulk generated codes.
-  if ($form_values['bulk_generate'] && intval($form_values['number_codes']) <= 0) {
-    form_set_error('number_codes', t('You must specify a number of codes greater than 0.'));
-  }
-
-  // Fail if the promotional code has invalid characters.
-  if (!empty($form_values['promo_code']) && !preg_match('!^[a-zA-Z0-9_-]+$!', $form_values['promo_code'])) {
-    form_set_error('promo_code', t('The promotional code can only consist of letters, numbers, underscores, and dashes.'));
-  }
-
-  // Fail if the promotional code entered is non-unique.
-  if (!isset($form_values['promo_code_orig']) || $form_values['promo_code_orig'] != $form_values['promo_code']) {
-    $exists = db_result(db_query("SELECT COUNT(*) FROM {uc_promo_codes} WHERE UPPER(promo_code) = '%s'", strtoupper($form_values['promo_code'])));
-    if ($exists > 0) {
-      form_set_error('promo_code', t('That promotional code has already been used.'));
-    }
-  }
-
-  // Fail if the user entered an invalid value for redemptions.
-  if ($form_values['promo_code_type'] == 2 && intval($form_values['max_redemptions']) <= 0) {
-    form_set_error('max_redemptions', t('You must specify a number of redemptions greater than 0.'));
-  }
-}
-
-function uc_promo_codes_form_submit($form_id, $form_values) {
-  // Convert the expiration date to a timestamp.
-  $form_values['expiration'] = gmmktime(23, 59, 59, $form_values['expiration']['month'], $form_values['expiration']['day'], $form_values['expiration']['year']);
-
-  // Special handling for bulk generated codes.
-  if ($form_values['bulk_generate']) {
-    $bulkgen_id = variable_get('uc_promo_bulkgen_id', 1);
-    variable_set('uc_promo_bulkgen_id', $bulkgen_id + 1);
-
-    // Manually set the promo code name.
-    $form_values['promo_code'] = 'BULKGEN'. $bulkgen_id .'-'. $form_values['code_length'] .'-'. $form_values['number_codes'];
-
-    // Setup the bulk generator seed.
-    $form_values['bulkgen_seed'] = sha1(mktime() . rand());
-  }
-
-  uc_promo_save_code($form_values);
-
-  drupal_set_message(t('Promotional code saved.'));
-
-  if ($form_values['op'] == t('Save')) {
-    return 'admin/store/settings/promo/'. $form_values['promo_id'] .'/codes';
-  }
-}
-
-// Form buider for deleting a promotional code.
-function uc_promo_delete_code_form($promo_id, $code) {
-  $data = uc_promo_load_code($code);
-  $form = array();
-
-  // Fail if the promotional code doesn't exist.
-  if (!$data) {
-    drupal_set_message(t("The specified promotional code doesn't exist."), 'error');
-    drupal_goto('admin/store/settings/promo/'. $promo_id .'/codes');
-  }
-
-  $form['promo_id'] = array(
-    '#type' => 'value',
-    '#value' => $promo_id,
-  );
-
-  $form['promo_code'] = array(
-    '#type' => 'value',
-    '#value' => $code,
-  );
-
-  return confirm_form($form, t('Delete promotional code'), 'admin/store/settings/promo/'. $promo_id .'/codes', t('Are you sure you want to delete %code?', array('%code' => $data['promo_code'])), t('Delete'));
-}
-
-function uc_promo_delete_code_form_submit($form_id, $form_values) {
-  uc_promo_delete_code($form_values['promo_code']);
-
-  drupal_set_message(t('The promotional code has been deleted.'));
-
-  return 'admin/store/settings/promo/'. $form_values['promo_id'] .'/codes';
-}
-
-/**
- * Loads a promotional code's data from the database.
- *
- * @param $code
- *   The promotional code to load.
- * @return
- *   An array of data for the promotional code; FALSE if the code isn't found.
- */
-function uc_promo_load_code($code) {
-  if (empty($code)) {
-    return FALSE;
-  }
-
-  // Check to make sure non-priveleged users don't match directly on the bulk
-  // generated special string.
-  if (substr($code, 0, 7) === 'BULKGEN' && !user_access('administer uc promotions')) {
-    return FALSE;
-  }
-
-  // Look for an exact match in the database.
-  $result = db_query("SELECT * FROM {uc_promo_codes} WHERE UPPER(promo_code) = '%s'", strtoupper($code));
-
-  // Return the data if an exact match was found.
-  if ($data = db_fetch_array($result)) {
-    return $data;
-  }
-
-  // Otherwise look for a bulk generated match in codes for active promotions
-  // that haven't passed their expiration date.
-  $result = db_query("SELECT pc.* FROM {uc_promo_codes} AS pc LEFT JOIN {uc_promos} AS p ON pc.promo_id = p.promo_id WHERE p.promo_status = 1 AND pc.expiration > %d AND pc.bulkgen_seed <> ''", time());
-  while ($data = db_fetch_array($result)) {
-    // Parse the promotion code
-    $parts = explode('-', $data['promo_code']);
-
-    // First check that the code is the correct length.
-    if (strlen($code) != $parts[1]) {
-      continue;
-    }
-
-    // Second check that the sequence part of the code is in the valid range.
-    // The code sequence is encoded in the first 5 characters of the code.
-    $sequence = explode("U", substr(strtoupper($code), 0, 5));
-    if (preg_match("/^[0-9ABCDEF]{1,5}$/", $sequence[0])) {
-      $sequence = hexdec($sequence[0]);
-      if ($sequence >= $parts[2]) {
-        continue;
-      }
-    }
-    else {
-      continue;
-    }
-
-    // Last, check that the MD5 hash part of code is valid.
-    $hash = substr(md5($sequence . $data['promo_id'] . $data['bulkgen_seed']), 0, ($parts[1] - 5));
-
-    if (strcasecmp($hash, substr($code, 5)) == 0) {
-      return $data;
-    }
-  }
-
-  return FALSE;
-}
-
-/**
- * Saves or inserts a new promotional code.
- *
- * @param $data
- *   An array of data representing the promotional code.
- */
-function uc_promo_save_code($data) {
-  // First try to update an existing promotional code.
-  db_query("UPDATE {uc_promo_codes} SET promo_code_type = %d, max_redemptions = %d, expiration = %d WHERE UPPER(promo_code) = '%s'",
-    $data['promo_code_type'], $data['max_redemptions'], $data['expiration'], strtoupper($data['promo_code']));
-
-  // Otherwise insert a new promotional code.
-  if (db_affected_rows() == 0) {
-    db_query("INSERT INTO {uc_promo_codes} (promo_code, promo_id, promo_code_type, bulkgen_seed, redemptions, max_redemptions, expiration, created, last_redemption) VALUES ('%s', %d, %d, '%s', %d, %d, %d, %d, %d)",
-      strtoupper($data['promo_code']), $data['promo_id'], $data['promo_code_type'],
-      $data['bulkgen_seed'], 0, $data['max_redemptions'], $data['expiration'],
-      time(), 0);
-  }
-}
-
-/**
- * Delete a promotional code from the database.
- *
- * @param $promo_code
- *   The ID of the promotional code to delete.
- */
-function uc_promo_delete_code($code) {
-  // Delete any related promotional code redemptions.
-  db_query("DELETE FROM {uc_promo_code_redemptions} WHERE promo_code = '%s'", $code);
-
-  // Delete the code itself.
-  db_query("DELETE FROM {uc_promo_codes} WHERE promo_code = '%s'", $code);
-
-  watchdog('uc_promo', t('Promotional code %code deleted.', array('%code' => $code)));
-}
-
-// Displays a table of redemptions for the specified promotional code.
-function uc_promo_redemptions($promo_id, $code) {
-  // Load the specified promotion; will be FALSE if $code == 0.
-  $data = uc_promo_load_promotion($promo_id);
-
-  if ($data === FALSE) {
-    drupal_set_message(t('Specified promotion does not exist.'), 'error');
-    drupal_goto('admin/store/settings/promo');
-  }
-
-  $output = '<div>'. t('<strong>Promotion:</strong> @name', array('@name' => check_plain($data['promo_name']))) .'<br />';
-
-  // Load the specified promotional code; will be FALSE if $code == 0.
-  $data = uc_promo_load_code($code);
-
-  if ($data === FALSE) {
-    drupal_set_message(t('Specified promotional code does not exist.'), 'error');
-    drupal_goto('admin/store/settings/promo/'. $promo_id .'/codes');
-  }
-
-  $output .= t('<strong>Code:</strong> @code', array('@code' => check_plain($code))) .'</div>';
-
-  $output .= '<div>'. l(t('Return to promotional codes listing.'), 'admin/store/settings/promo/'. $promo_id .'/codes') .'</div>';
-
-  $header = array(
-    array('data' => t('Redeemed on'), 'field' => 'redeemed', 'sort' => 'desc'),
-    array('data' => t('User'), 'field' => 'uid'),
-  );
-
-  if (substr($code, 0, 7) === 'BULKGEN') {
-    $header[] = array('data' => t('Bulk code'), 'field' => 'bulk_code');
-  }
-
-  $rows = array();
-
-  $result = pager_query("SELECT * FROM {uc_promo_code_redemptions} WHERE promo_code = '%s'". tablesort_sql($header), 30, 0, NULL, $code);
-  while ($data = db_fetch_array($result)) {
-    $row = array(
-      array('data' => format_date($data['redeemed'], 'short'), 'style' => 'white-space: nowrap'),
-      array('data' => $data['uid'] > 0 ? l($data['uid'], 'user/'. $data['uid']) : '0', 'width' => substr($code, 0, 7) === 'BULKGEN' ? '33%' : '50%'),
-    );
-
-    if (substr($code, 0, 7) === 'BULKGEN') {
-      $row[] = $data['bulk_code'];
-    }
-
-    $rows[] = $row;
-  }
-
-  if (count($rows) == 0) {
-    $rows[] = array(
-      array('data' => t('This promotional code has not been redeemed.'), 'colspan' => 5),
-    );
-  }
-
-  $output .= theme('table', $header, $rows) . theme('pager');
-
-  return $output;
-}
-
-/**
- * Checks the database to see if a code is valid.
- *
- * @param $code
- *   The promotional code to validate.
- * @return
- *   TRUE or FALSE depending on whether the code is valid or not.
- */
-function uc_promo_validate_code($code, $user) {
-  // Return FALSE if the code is not found.
-  if (!$data = uc_promo_load_code($code)) {
-    return FALSE;
-  }
-
-  // Validate the code based on its type.
-  switch ($data['promo_code_type']) {
-    // Unlimited use promotional code.
-    case 1:
-      // Nothing should cause these to fail.
-      break;
-
-    // Specified number of uses.
-    case 2:
-      // Return FALSE if the code has been used up; handle bulk codes in a way
-      // that accounts for redemptions of individual bulk generated codes.
-      if (substr($data['promo_code'], 0, 7) === 'BULKGEN') {
-        $count = db_result(db_query("SELECT COUNT(*) FROM {uc_promo_code_redemptions} WHERE promo_code = '%s' AND bulk_code = '%s'", $data['promo_code'], $code));
-        if ($count >= $data['max_redemptions']) {
-          return FALSE;
-        }
-      }
-      else {
-        if ($data['redemptions'] >= $data['max_redemptions']) {
-          return FALSE;
-        }
-      }
-      break;
-
-    // Expiration date.
-    case 3:
-      // Return FALSE if the code has expired.
-      if (time() >= $data['expiration']) {
-        return FALSE;
-      }
-      break;
-  }
-
-  // Return FALSE if the promotion is no longer available or is disabled.
-  $active = db_result(db_query("SELECT promo_id FROM {uc_promos} WHERE promo_id = %d AND promo_status = 1", $data['promo_id']));
-  if (!$active) {
-    return FALSE;
-  }
-
-  $redeem_once = db_result(db_query("SELECT redeem_once FROM {uc_promos} WHERE promo_id = %d", $data['promo_id']));
-  if ($redeem_once == 1) {
-    $redeemed = db_result(db_query("SELECT COUNT(redemption_id) FROM {uc_promo_code_redemptions} WHERE promo_id = %d AND uid = %d", $data['promo_id'], $user->uid));
-    if ($redeemed > 0) {
-      return FALSE;
-    }
-  }
-
-  return TRUE;
-}
-
-/**
- * Redeems a promotional code for a user by adding a redemption in the database
- *   and updating the promotional code's data as needed.
- *
- * @param $code
- *   The promotional code to redeem.
- * @param $user
- *   The user account for which the code is being redeemed.
- * @param $bulk_id
- *   If the code is a special BULKGEN code, $bulk_code should contain the actual
- *     unique code used to redeem the promotion.
- */
-function uc_promo_redeem_code($code, $user, $bulk_code = NULL) {
-  // Load the appropriate promotion ID.
-  $promo_id = db_result(db_query("SELECT promo_id FROM {uc_promo_codes} WHERE promo_code = '%s'", $code));
-
-  // Insert the redemption into the database.
-  db_query("INSERT INTO {uc_promo_code_redemptions} (redemption_id, promo_code, bulk_code, promo_id, uid, redeemed) VALUES (%d, '%s', '%s', %d, %d, %d)",
-    db_next_id('{uc_promo_code_redemptions}_redemption_id'), $code, $bulk_code,
-    $promo_id, $user->uid, time());
-
-  // Update the promotion to reflect the redemption.
-  db_query("UPDATE {uc_promo_codes} SET redemptions = redemptions + 1, last_redemption = %d WHERE promo_code = '%s'", time(), $code);
-}
-
-// Downloads a file containing a bulk generated set of promotional codes.
-function uc_promo_code_bulkgen($promo_id, $code) {
-  // Load the data directly from the database.
-  $result = db_query("SELECT * FROM {uc_promo_codes} WHERE UPPER(promo_code) = '%s'", strtoupper($code));
-
-  if ($data = db_fetch_array($result)) {
-    $filename = $code .'.csv';
-
-    header('Content-Type: application/octet-stream');
-    header('Content-Disposition: attachment; filename="' . $filename . '";');
-
-    $parts = explode('-', $code);
-
-    $codes = '';
-    for ($i = 0; $i < $parts[2]; $i++) {
-      $codes .= strtoupper(str_pad(dechex($i), 5, 'U' . md5($data['bulkgen_seed'] . $i)) . substr(md5($i . $promo_id . $data['bulkgen_seed']), 0, $parts[1] - 5)) .",\n";
-    }
-
-    echo rtrim($codes, ",\n");
-
-    exit();
-  }
-  else {
-    return drupal_not_found();
-  }
-}
-
-// Workflow-ng action callback and form functions.
-function uc_promo_user_add_download($account, $settings) {
-  _user_table_action('allow', $settings['fid'], $account->uid, NULL);
-}
-
-function uc_promo_user_add_download_form($settings = array()) {
-  if (!is_dir(variable_get('uc_file_base_dir', NULL))) {
-    drupal_set_message(t('A file directory needs to be configured in <a href="!url">product feature settings</a> before a file can be selected.', array('!url' => url('admin/store/settings/products/edit/features'))), 'error');
-  }
-  $form['uc_file_filename'] = array(
-    '#type' => 'textfield',
-    '#title' => t('File download'),
-    '#default_value' => $default_filename,
-    '#autocomplete_path' => '_autocomplete_file',
-    '#description' => t('The file that can be downloaded when product is purchased (enter a path relative to the %dir directory).', array('%dir' => variable_get('uc_file_base_dir', NULL))),
-  );
-
-  return $form;
-}
-
-function uc_promo_user_add_download_validate($form_id, $form_values) {
-  if (!db_result(db_query("SELECT fid FROM {uc_files} WHERE filename = '%s'", $form_values['uc_file_filename']))) {
-    form_set_error('uc_file_filename', t('%file is not a valid file or directory inside file download directory.', array('%file' => $form_values['uc_file_filename'])));
-  }
-}
-
-function uc_promo_user_add_download_submit($form_id, $form_values) {
-  return array('fid' => db_result(db_query("SELECT fid FROM {uc_files} WHERE filename = '%s'", $form_values['uc_file_filename'])));
-}
-
+<?php

+// $Id: uc_promo.module,v 2.1 2009/04/09 11:06:00 dwkitchen Exp $

+

+/* TODO Remove $row argument from db_result() method

+   The $row argument of db_result() was removed from the database abstraction

+   layer in 6.x core, as it was a database dependent option. Developers need to

+   use other handling to replace the needs of this method. */

+

+/* TODO Change 'Submit' to 'Save' on buttons

+   It has been agreed on that the description 'Submit' for a button is not a

+   good choice since it does not indicate what actually happens. While for

+   example on node editing forms, 'Preview' and 'Delete' describe exactly what

+   will happen when the user clicks on the button, 'Submit' only gives a vague

+   idea. When labelling your buttons, make sure that it is clear what this

+   button does when the user clicks on it. */

+

+/* TODO New user_mail_tokens() method may be useful.

+   user.module now provides a user_mail_tokens() function to return an array

+   of the tokens available for the email notification messages it sends when

+   accounts are created, activated, blocked, etc. Contributed modules that

+   wish to make use of the same tokens for their own needs are encouraged

+   to use this function. */

+

+/* TODO

+   There is a new hook_watchdog in core. This means that contributed modules

+   can implement hook_watchdog to log Drupal events to custom destinations.

+   Two core modules are included, dblog.module (formerly known as watchdog.module),

+   and syslog.module. Other modules in contrib include an emaillog.module,

+   included in the logging_alerts module. See syslog or emaillog for an

+   example on how to implement hook_watchdog.

+function example_watchdog($log = array()) {

+  if ($log['severity'] == WATCHDOG_ALERT) {

+    mysms_send($log['user']->uid,

+      $log['type'],

+      $log['message'],

+      $log['variables'],

+      $log['severity'],

+      $log['referer'],

+      $log['ip'],

+      format_date($log['timestamp']));

+  }

+} */

+

+/* TODO Implement the hook_theme registry. Combine all theme registry entries

+   into one hook_theme function in each corresponding module file.

+function uc_promo_theme() {

+  return array(

+  );

+} */

+

+/* TODO Form buttons can define custom #submit and #validate handlers.

+   All forms can have #validate and #submit properties containing lists of

+   validation and submission handlers to be executed when a user submits data.

+   Previously, if a form featured multiple submission buttons to initiate

+   different actions (updating a record versus deleting, for example), it was

+   necessary to check the incoming form_values['op'] for the name of the

+   clicked button, then execute different code based on its value. Now, it is

+   possible to define #validate and #submit properties on each individual form

+   button if desired. */

+

+/**

+ * @file

+ * Create promotions with redeemable promotional codes that users can redeem to

+ *   perform special actions in your store.

+ */

+

+/**

+ * Implementation of hook_menu().

+ */

+function uc_promo_menu() {

+  $items = array();

+

+  $items['promo'] = array(

+    'title' => 'Redeem a promotion',

+    'description' => 'Form to redeem a promotional code.',

+    'page callback' => 'uc_promo_redeem',

+    'page arguments' => array(''),

+    'access arguments' => array('access content'),

+    'type' => MENU_CALLBACK,

+    'file' => 'uc_promo.redeem.inc',

+  );

+

+  $items['admin/store/settings/promo'] = array(

+    'title' => 'Promotions',

+    'description' => 'Setup and administer your promotions.',

+    'page callback' => 'uc_promo_admin',

+    'access arguments' => array('administer uc promotions'),

+    'type' => MENU_NORMAL_ITEM,

+    'file' => 'uc_promo.admin.inc',

+  );

+

+  $items['admin/store/settings/promo/overview'] = array(

+    'title' => 'Overview',

+    'description' => 'Setup and administer your promotions.',

+    'type' => MENU_DEFAULT_LOCAL_TASK,

+    'weight' => -10,

+  );

+

+  $items['admin/store/settings/promo/add'] = array(

+    'title' => 'Add a promotion',

+    'description' => 'Setup and administer your promotions.',

+    'page callback' => 'drupal_get_form',

+    'page arguments' => array('uc_promo_promotion_form'),

+    'access arguments' => array('administer uc promotions'),

+    'type' => MENU_LOCAL_TASK,

+    'weight' => 0,

+    'file' => 'uc_promo.admin.inc',

+  );

+

+  $items['admin/store/settings/promo/settings'] = array(

+    'title' => 'Settings',

+    'description' => 'Adjust the general promotions settings.',

+    'page callback' => 'drupal_get_form',

+    'page arguments' => array('uc_promo_admin_form'),

+    'access arguments' => array('administer uc promotions'),

+    'type' => MENU_LOCAL_TASK,

+    'weight' => 10,

+    'file' => 'uc_promo.admin.inc',

+  );

+

+  $items['admin/store/settings/promo/%uc_promo/edit'] = array(

+    'title' => 'Edit promotion',

+    'description' => 'Edit an existing promotion.',

+    'page callback' => 'drupal_get_form',

+    'page arguments' => array('uc_promo_promotion_form', 4),

+    'access arguments' => array('administer uc promotions'),

+    'type' => MENU_CALLBACK,

+    'file' => 'uc_promo.admin.inc',

+  );

+

+  $items['admin/store/settings/promo/%uc_promo/delete'] = array(

+    'title' => 'Delete promotion',

+    'description' => 'Delete a promotion.',

+    'page callback' => 'drupal_get_form',

+    'page arguments' => array('uc_promo_delete_promotion_form', 4),

+    'access arguments' => array('administer uc promotions'),

+    'type' => MENU_CALLBACK,

+    'file' => 'uc_promo.admin.inc',

+  );

+

+  $items['admin/store/settings/promo/%uc_promo/codes'] = array(

+    'title' => 'Promotional codes',

+    'description' => 'Administer promotional codes for a given promotion.',

+    'page callback' => 'uc_promo_codes',

+    'page arguments' => array(4),

+    'access arguments' => array('administer uc promotions'),

+    'type' => MENU_CALLBACK,

+  );

+

+  $items['admin/store/settings/promo/%/codes/overview'] = array (

+    'title' => 'Overview',

+    'description' => 'Administer promotional codes for a given promotion.',

+    'type' => MENU_DEFAULT_LOCAL_TASK,

+    'weight' => -10,

+  );

+

+  $items['admin/store/settings/promo/%uc_promo/codes/add'] = array(

+    'title' => 'Add code',

+    'description' => 'Add a promotional code for the promotion.',

+    'page callback' => 'drupal_get_form',

+    'page arguments' => array('uc_promo_codes_form', 4),

+    'access arguments' => array('administer uc promotions'),

+    'type' => MENU_LOCAL_TASK,

+    'weight' => 0,

+  );

+

+  $items['admin/store/settings/promo/%uc_promo/codes/%uc_promo_code/edit'] = array(

+    'title' => 'Edit code',

+    'description' => 'Edit a promotional code.',

+    'page callback' => 'drupal_get_form',

+    'page arguments' => array('uc_promo_codes_form', 4, 6),

+    'access arguments' => array('administer uc promotions'),

+    'type' => MENU_CALLBACK,

+  );

+

+  $items['admin/store/settings/promo/%uc_promo/codes/%uc_promo_code/download'] = array (

+    'title' => 'Download bulk generated codes',

+    'page callback' => 'uc_promo_code_bulkgen',

+    'page arguments' => array(4, 6),

+    'access arguments' => array('administer uc promotions'),

+    'type' => MENU_CALLBACK,

+  );

+

+  $items['admin/store/settings/promo/%uc_promo/codes/%uc_promo_code/delete'] = array(

+    'title' => 'Delete code',

+    'description' => 'Delete a promotional code.',

+    'page callback' => 'drupal_get_form',

+    'page arguments' => array('uc_promo_delete_code_form', 4, 6),

+    'access arguments' => array('administer uc promotions'),

+    'type' => MENU_CALLBACK,

+  );

+

+  $items['admin/store/settings/promo/%uc_promo/codes/%uc_promo_code/redemptions'] = array(

+    'title' => 'Promotional code redemptions',

+    'description' => 'A table of redemptions for a promotional code.',

+    'page callback' => 'uc_promo_redemptions',

+    'page arguments' => array(4, 6),

+    'access arguments' => array('administer uc promotions'),

+    'type' => MENU_CALLBACK,

+  );

+

+  $items['admin/store/settings/promo/%uc_promo/codes/back'] = array(

+    'title' => 'Back to promotions',

+    'page callback' => 'drupal_goto',

+    'page arguments' => array('admin/store/settings/promo'),

+    'access arguments' => array('administer uc promotions'),

+    'type' => MENU_LOCAL_TASK,

+    'weight' => 10,

+  );

+

+  return $items;

+}

+

+/**

+ * Implementation of hook_perm().

+ */

+function uc_promo_perm() {

+  return array('administer uc promotions', 'redeem uc promotions');

+}

+

+/**

+ * Implementation of hook_rules_event_info ().

+ */

+function uc_promo_rules_event_info() {

+  $events = array();

+

+  $result = db_query("SELECT promo_id, promo_name FROM {uc_promos}");

+  while ($promo = db_fetch_object($result)) {

+    $events['uc_promo_'. $promo->promo_id] = array(

+      'label'  => t('@name', array('@name' => $promo->promo_name)),

+      'module' => 'Promotions',

+      'arguments' => array(

+        'user' => array('type' => 'user', 'label' => t('User')),

+      ),

+    );

+  }

+

+  return $events;

+}

+

+/**

+ * Implementation of hook_action_info()

+ */

+function uc_promo_rules_action_info () {

+  $actions = array ();

+

+  // Provide integration for File Downloads module.

+  if (module_exists ('uc_file')) {

+    // Add a file download to a user's account.

+    $actions['uc_promo_user_add_download'] = array (

+      'label' => t('Give a user a file download'),

+      'arguments' => array (

+        'user' => array('type' => 'user', 'label' => t('User account')),

+      ),

+      'module' => t('Promotions'),

+    );

+  }

+

+  // Provide integration for Role module -- Added By dwkitchen 2009.01.03

+  if (module_exists('uc_roles')) {

+    // Add a role to a user's account.

+    $actions['uc_promo_user_add_role'] = array (

+      'label' => t('Give a user a role'),

+      'arguments' => array(

+        'user' => array('type' => 'user', 'label' => t('User account')),

+      ),

+      'module' => t('Promotions'),

+    );

+  }

+

+  return $actions;

+}

+

+/**

+ * Loads a promotion from the database.

+ *

+ * @param $promo_id

+ *   The ID of the promotion to load.

+ * @return

+ *   An array of data for the promotion.

+ */

+function uc_promo_load ($promo_id) {

+  return uc_promo_load_promotion($promo_id);

+}

+

+function uc_promo_load_promotion($promo_id) {

+  $result = db_query("SELECT * FROM {uc_promos} WHERE promo_id = %d", $promo_id);

+  return db_fetch_array($result);

+}

+

+/**

+ * Saves or inserts a new promotion.

+ *

+ * @param $data

+ *   An array of data representing the promotion.

+ */

+function uc_promo_save_promotion($data) {

+  // First try to update an existing promotion.

+  if ($data['promo_id'] > 0) {

+    db_query("UPDATE {uc_promos} SET promo_name = '%s', landing_page = '%s', redeem_once = %d, promo_status = %d WHERE promo_id = %d",

+      $data['promo_name'], $data['landing_page'], $data['redeem_once'],

+      $data['promo_status'], $data['promo_id']);

+  }

+

+  // Otherwise insert a new promotion.

+  if ($data['promo_id'] === 0 || db_affected_rows() == 0) {

+    db_query("INSERT INTO {uc_promos} (promo_id, promo_name, landing_page, redeem_once, promo_status, created) VALUES (%d, '%s', '%s', %d, %d, %d)",

+      $data['promo_id'] == 0 ? db_last_insert_id('uc_promos', 'promo_id') : $data['promo_id'],

+      $data['promo_name'], $data['landing_page'], $data['redeem_once'],

+      $data['promo_status'], time());

+  }

+}

+

+/**

+ * Delete a promotion from the database.

+ *

+ * @param $promo_id

+ *   The ID of the promotion to delete.

+ */

+function uc_promo_delete_promotion($promo_id) {

+  // Delete related codes per the user settings.

+  switch (variable_get('uc_promo_delete_behavior', 0)) {

+    case 0:

+      // Delete any related promotional codes.

+      db_query("DELETE FROM {uc_promo_code_redemptions} WHERE promo_id = %d", $promo_id);

+      db_query("DELETE FROM {uc_promo_codes} WHERE promo_id = %d", $promo_id);

+      break;

+

+    case 1:

+      // Delete related promotional codes that haven't been redeemed.

+      db_query("DELETE FROM {uc_promo_codes} WHERE promo_id = %d AND redemptions > 0", $promo_id);

+      break;

+  }

+

+  // Get the name for use in the log entry.

+  $name = db_result(db_query("SELECT promo_name FROM {uc_promos} WHERE promo_id = %d", $promo_id));

+

+  // Delete the promotion itself.

+  db_query("DELETE FROM {uc_promos} WHERE promo_id = %d", $promo_id);

+

+  watchdog('uc_promo', 'Promotion %name deleted.', array('%name' => !empty($name) ? $name : $promo_id));

+}

+

+function uc_promo_codes($promo = false) {

+  if (preg_match ('`admin/store/settings/promo/\d+/codes/[^/]+/(edit|delete|download|redemptions)`', $_GET['q'])) {

+    $promo = uc_promo_load (arg (4));

+    $code = uc_promo_code_load (arg (6));

+

+    switch (arg (7)) {

+      case 'edit':

+        return drupal_get_form ('uc_promo_codes_form', $promo, $code);

+      case 'delete':

+        return drupal_get_form ('uc_promo_delete_code_form', $promo, $code);

+      case 'download':

+        return uc_promo_code_bulkgen ($promo, $code);

+      case 'redemptions':

+        return uc_promo_redemptions ($promo, $code);

+    }

+  }

+

+  if ($promo === FALSE) {

+    drupal_set_message(t('Specified promotion does not exist.'), 'error');

+    drupal_goto('admin/store/settings/promo');

+  }

+

+  $output = t('Promotion: %name', array('%name' => check_plain($promo['promo_name'])));

+

+  $header = array(

+    array('data' => t('Code'), 'field' => 'promo_code'),

+    array('data' => t('Type'), 'field' => 'promo_code_type'),

+    array('data' => t('Redemptions'), 'field' => 'redemptions'),

+    array('data' => t('Last redemption'), 'field' => 'last_redemption', 'sort' => 'desc'),

+    t('Operations'),

+  );

+

+  $rows = array();

+

+  // Get all the promotions from the database.

+  $result = pager_query("SELECT * FROM {uc_promo_codes} WHERE promo_id = %d". tablesort_sql($header), 30, 0, NULL, $promo['promo_id']);

+  while ($data = db_fetch_array($result)) {

+    // Create a download link for bulk generated codes.

+    if (!empty($data['bulkgen_seed'])) {

+      $download = '<br />'. l(t('Download codes.'), 'admin/store/settings/promo/'. $data['promo_id'] .'/codes/'. $data['promo_code'] .'/download');

+    }

+    else {

+      $download = '';

+    }

+

+    // Create an array of operations for the code.

+    $ops = array(

+      l(t('redemptions'), 'admin/store/settings/promo/'. $data['promo_id'] .'/codes/'. $data['promo_code'] .'/redemptions'),

+      l(t('edit'), 'admin/store/settings/promo/'. $data['promo_id'] .'/codes/'. $data['promo_code'] .'/edit'),

+      l(t('delete'), 'admin/store/settings/promo/'. $data['promo_id'] .'/codes/'. $data['promo_code'] .'/delete'),

+    );

+

+    switch ($data['promo_code_type']) {

+      case 1:

+        $type = t('Unlimited');

+        break;

+      case 2:

+        $type = t('Limited: @number', array('@number' => $data['max_redemptions']));

+        break;

+      case 3:

+        $type = t('Expires @date', array('@date' => format_date($data['expiration'], 'short')));

+        break;

+    }

+

+    $row = array(

+      'data' => array(

+        check_plain($data['promo_code']) . $download,

+        array('data' => $type, 'style' => 'white-space: nowrap;'),

+        $data['redemptions'],

+        array('data' => $data['last_redemption'] > 0 ? format_date($data['last_redemption'], 'short') : '-', 'style' => 'white-space: nowrap;'),

+        array('data' => implode(' ', $ops), 'style' => 'white-space: nowrap;'),

+      ),

+      'valign' => 'top',

+    );

+

+    $rows[] = $row;

+  }

+

+  if (count($rows) == 0) {

+    $rows[] = array(

+      array('data' => t('No promotional codes created yet.'), 'colspan' => 5),

+    );

+  }

+

+  $output .= theme('table', $header, $rows) . theme('pager') . l(t('Add promotional codes.'), 'admin/store/settings/promo/'. $promo['promo_id'] .'/codes/add');

+

+  return $output;

+}

+

+function uc_promo_codes_form ($form_state, $promo = false, $code = false) {

+  $form = array();

+

+  if ($promo === FALSE) {

+    drupal_set_message(t('Specified promotion does not exist.'), 'error');

+    drupal_goto('admin/store/settings/promo');

+  }

+

+  $form['promo_name'] = array (

+    '#value' =>

+      empty ($promo) ? '' : t('Promotion: %name', array('%name' => check_plain($promo['promo_name']))),

+  );

+

+  $form['promo_id'] = array (

+    '#type' => 'value',

+    '#value' => $promo['promo_id'],

+  );

+

+  $code_disabled = FALSE;

+

+  if ($code !== FALSE) {

+    $form['promo_code_orig'] = array(

+      '#type' => 'value',

+      '#value' => $code,

+    );

+

+    // Disable editing the code if we're dealing with a bulkgen code.

+    if (!empty($code['bulkgen_seed'])) {

+      $code_disabled = TRUE;

+    }

+    else {

+      // Otherwise disable it if it's already been redeemed.

+      $count = db_result(db_query("SELECT COUNT(*) FROM {uc_promo_code_redemptions} WHERE promo_code = '%s'", $code['promo_code']));

+      if ($count > 0) {

+        $code_disabled = TRUE;

+      }

+    }

+  }

+

+  $form['promo_code'] = array(

+    '#type' => 'textfield',

+    '#title' => t('Promotional code'),

+    '#description' => t('Promotional codes may only contain letters, numbers, underscores, and dashes.<br />Codes may not be changed once they have been redeemed.<br/>Do NOT manually set a code to start with "BULKGEN".'),

+    '#default_value' => $code === FALSE ? '' : $code['promo_code'],

+    '#required' => $code === FALSE ? FALSE : TRUE,

+    '#disabled' => $code_disabled,

+  );

+

+  if (!$code) {

+    $form['bulk'] = array(

+      '#type' => 'fieldset',

+      '#title' => t('Bulk code generation'),

+      '#description' => t('You may choose to bulk generate alphanumeric promotional codes based on the settings below.<br />If so, the promotional code entered above will be ignored.'),

+      '#collapsible' => TRUE,

+      '#collapsed' => TRUE,

+    );

+    $form['bulk']['bulk_generate'] = array(

+      '#type' => 'checkbox',

+      '#title' => t('Bulk generate codes for this promotion.'),

+    );

+

+    $form['bulk']['number_codes'] = array(

+      '#type' => 'textfield',

+      '#title' => t('Number of codes'),

+      '#description' => t('Enter in the number of codes you want to generate.'),

+    );

+    $form['bulk']['code_length'] = array(

+      '#type' => 'select',

+      '#title' => t('Code length'),

+      '#options' => drupal_map_assoc(range(10, 30)),

+    );

+  }

+

+  $form['promo_code_type'] = array (

+    '#type' => 'radios',

+    '#title' => t('Promotional code type'),

+    '#description' => t('Limited or expirable promotional codes should specify their settings below.'),

+    '#options' => array(

+      1 => t('Unlimited number of redemptions'),

+      2 => t('Limited number of redemptions'),

+      3 => t('Expires on a certain date'),

+    ),

+    '#default_value' => $code['promo_code_type'],

+    '#required' => TRUE,

+  );

+

+  $form['max_redemptions'] = array(

+    '#type' => 'textfield',

+    '#title' => t('Maximum number of redemptions'),

+    '#description' => t('Only applicable to limited promotional codes.'),

+    '#default_value' => $code['max_redemptions'],

+  );

+

+  $form['expiration'] = array (

+    '#type' => 'date',

+    '#title' => t('Expiration date'),

+    '#description' => t('The code will fail after 23:59 of the specified date.<br />Only applicable to expirable promotional codes.'),

+    '#default_value' => array(

+      'year' => date('Y', $code === FALSE ? time() : $code['expiration']),

+      'month' => date('n', $code === FALSE ? time() : $code['expiration']),

+      'day' => date('j', $code === FALSE ? time() : $code['expiration']),

+    ),

+  );

+

+  $form['save'] = array(

+    '#type' => 'submit',

+    '#value' => t('Save'),

+  );

+

+  $form['save_add'] = array(

+    '#type' => 'submit',

+    '#value' => t('Save and add another'),

+    '#suffix' => l(t('Cancel'), 'admin/store/settings/promo/'. $promo['promo_id'] .'/codes'),

+  );

+

+  return $form;

+}

+

+function uc_promo_codes_form_validate($form, &$form_state) {

+  // Fail if no promo code was entered and bulk generation was not selected.

+  if (!$form_state['values']['bulk_generate'] && empty($form_state['values']['promo_code'])) {

+    form_set_error('promo_code', t('You must specify a promotional code.'));

+  }

+

+  // Fail if the user entered an invalid bulk generated code length.

+  if ($form_state['values']['bulk_generate'] && (intval($form_state['values']['code_length']) < 10 || intval($form_state['values']['code_length']) > 30)) {

+    form_set_error('code_length', t('You must specify a code length between 10 and 30.'));

+  }

+

+  // Fail if the user entered an invalid number of bulk generated codes.

+  if ($form_state['values']['bulk_generate'] && intval($form_state['values']['number_codes']) <= 0) {

+    form_set_error('number_codes', t('You must specify a number of codes greater than 0.'));

+  }

+

+  // Fail if the promotional code has invalid characters.

+  if (!empty($form_state['values']['promo_code']) && !preg_match('!^[a-zA-Z0-9_-]+$!', $form_state['values']['promo_code'])) {

+    form_set_error('promo_code', t('The promotional code can only consist of letters, numbers, underscores, and dashes.'));

+  }

+

+  // Fail if the promotional code entered is non-unique.

+  if (!isset($form_state['values']['promo_code_orig']) || $form_state['values']['promo_code_orig']['promo_code'] != $form_state['values']['promo_code']) {

+    $exists = db_result(db_query(

+      "SELECT COUNT(*) FROM {uc_promo_codes} WHERE UPPER(promo_code) = '%s'",

+      drupal_strtoupper($form_state['values']['promo_code'])

+    ));

+

+    if ($exists > 0) {

+      form_set_error('promo_code', t('That promotional code has already been used.'));

+    }

+  }

+

+  // Fail if the user entered an invalid value for redemptions.

+  if ($form_state['values']['promo_code_type'] == 2 && intval($form_state['values']['max_redemptions']) <= 0) {

+    form_set_error('max_redemptions', t('You must specify a number of redemptions greater than 0.'));

+  }

+}

+

+function uc_promo_codes_form_submit($form, &$form_state) {

+  // Convert the expiration date to a timestamp.

+  $form_state['values']['expiration'] = gmmktime(23, 59, 59, $form_state['values']['expiration']['month'], $form_state['values']['expiration']['day'], $form_state['values']['expiration']['year']);

+

+  // Special handling for bulk generated codes.

+  if ($form_state['values']['bulk_generate']) {

+    $bulkgen_id = variable_get('uc_promo_bulkgen_id', 1);

+    variable_set('uc_promo_bulkgen_id', $bulkgen_id + 1);

+

+    // Manually set the promo code name.

+    $form_state['values']['promo_code'] = 'BULKGEN'. $bulkgen_id .'-'. $form_state['values']['code_length'] .'-'. $form_state['values']['number_codes'];

+

+    // Setup the bulk generator seed.

+    $form_state['values']['bulkgen_seed'] = sha1(mktime() . rand());

+  }

+

+  uc_promo_save_code ($form_state['values']);

+

+  drupal_set_message(t('Promotional code saved.'));

+

+/* TODO The 'op' element in the form values is deprecated.

+   Each button can have #validate and #submit functions associated with it.

+   Thus, there should be one button that submits the form and which invokes

+   the normal form_id_validate and form_id_submit handlers. Any additional

+   buttons which need to invoke different validate or submit functionality

+   should have button-specific functions. */

+  if ($form_state['values']['op'] == t('Save')) {

+    drupal_goto ('admin/store/settings/promo/'. $form_state['values']['promo_id'] .'/codes');

+  }

+}

+

+// Form buider for deleting a promotional code.

+function uc_promo_delete_code_form($form_state, $promo, $code) {

+  $form = array();

+

+  // Fail if the promotional code doesn't exist.

+  if (!$code) {

+    drupal_set_message(t("The specified promotional code doesn't exist."), 'error');

+    drupal_goto('admin/store/settings/promo/'. $promo['promo_id'] .'/codes');

+  }

+

+  $form['promo_id'] = array(

+    '#type' => 'value',

+    '#value' => $promo['promo_id'],

+  );

+

+  $form['promo_code'] = array(

+    '#type' => 'value',

+    '#value' => $code['promo_code'],

+  );

+

+  return confirm_form(

+    $form,

+    t('Delete promotional code'),

+    'admin/store/settings/promo/'. $promo['promo_id'] .'/codes',

+    t('Are you sure you want to delete %code?', array('%code' => $code['promo_code'])),

+    t('Delete')

+  );

+}

+

+function uc_promo_delete_code_form_submit($form, &$form_state) {

+  uc_promo_delete_code($form_state['values']['promo_code']);

+

+  drupal_set_message(t('The promotional code has been deleted.'));

+  drupal_goto ('admin/store/settings/promo/'. $form_state['values']['promo_id'] .'/codes');

+}

+

+/**

+ * Loads a promotional code's data from the database.

+ *

+ * @param $code

+ *   The promotional code to load.

+ * @return

+ *   An array of data for the promotional code; FALSE if the code isn't found.

+ */

+function uc_promo_code_load ($code) {

+  return uc_promo_load_code($code);

+}

+

+function uc_promo_load_code($code) {

+  if (empty($code)) {

+    return FALSE;

+  }

+

+  // Check to make sure non-priveleged users don't match directly on the bulk

+  // generated special string.

+  if (drupal_substr($code, 0, 7) === 'BULKGEN' && !user_access('administer uc promotions')) {

+    return FALSE;

+  }

+

+  // Look for an exact match in the database.

+  $result = db_query("SELECT * FROM {uc_promo_codes} WHERE UPPER(promo_code) = '%s'", drupal_strtoupper($code));

+

+  // Return the data if an exact match was found.

+  if ($data = db_fetch_array($result)) {

+    return $data;

+  }

+

+  // Otherwise look for a bulk generated match in codes for active promotions

+  // that haven't passed their expiration date.

+  $result = db_query("SELECT pc.* FROM {uc_promo_codes} AS pc LEFT JOIN {uc_promos} AS p ON pc.promo_id = p.promo_id WHERE p.promo_status = 1 AND pc.expiration > %d AND pc.bulkgen_seed <> ''", time());

+  while ($data = db_fetch_array($result)) {

+    // Parse the promotion code

+    $parts = explode('-', $data['promo_code']);

+

+    // First check that the code is the correct length.

+    if (drupal_strlen($code) != $parts[1]) {

+      continue;

+    }

+

+    // Second check that the sequence part of the code is in the valid range.

+    // The code sequence is encoded in the first 5 characters of the code.

+    $sequence = explode("U", drupal_substr(drupal_strtoupper($code), 0, 5));

+    if (preg_match("/^[0-9ABCDEF]{1,5}$/", $sequence[0])) {

+      $sequence = hexdec($sequence[0]);

+      if ($sequence >= $parts[2]) {

+        continue;

+      }

+    }

+    else {

+      continue;

+    }

+

+    // Last, check that the MD5 hash part of code is valid.

+    $hash = drupal_substr(md5($sequence . $data['promo_id'] . $data['bulkgen_seed']), 0, ($parts[1] - 5));

+

+    if (strcasecmp($hash, drupal_substr($code, 5)) == 0) {

+      return $data;

+    }

+  }

+

+  return FALSE;

+}

+

+/**

+ * Saves or inserts a new promotional code.

+ *

+ * @param $data

+ *   An array of data representing the promotional code.

+ */

+function uc_promo_save_code ($data) {

+  // First try to update an existing promotional code.

+  if (isset ($data['promo_code_orig'])) {

+    db_query (

+      "UPDATE {uc_promo_codes} SET promo_code = '%s', promo_code_type = %d, max_redemptions = %d, expiration = %d WHERE UPPER(promo_code) = '%s'",

+      $data['promo_code'],

+      $data['promo_code_type'],

+      $data['max_redemptions'],

+      $data['expiration'],

+      drupal_strtoupper($data['promo_code_orig']['promo_code'])

+    );

+  }

+  // Otherwise insert a new promotional code.

+  else {

+    db_query(

+      "INSERT INTO {uc_promo_codes} (promo_code, promo_id, promo_code_type, bulkgen_seed, redemptions, max_redemptions, expiration, created, last_redemption) VALUES ('%s', %d, %d, '%s', %d, %d, %d, %d, %d)",

+      drupal_strtoupper($data['promo_code']),

+      $data['promo_id'],

+      $data['promo_code_type'],

+      $data['bulkgen_seed'],

+      0,

+      $data['max_redemptions'],

+      $data['expiration'],

+      time(),

+      0

+    );

+  }

+}

+

+/**

+ * Delete a promotional code from the database.

+ *

+ * @param $promo_code

+ *   The ID of the promotional code to delete.

+ */

+function uc_promo_delete_code($code) {

+  // Delete any related promotional code redemptions.

+  db_query("DELETE FROM {uc_promo_code_redemptions} WHERE promo_code = '%s'", $code);

+

+  // Delete the code itself.

+  db_query("DELETE FROM {uc_promo_codes} WHERE promo_code = '%s'", $code);

+

+  watchdog('uc_promo', 'Promotional code %code deleted.', array('%code' => $code));

+}

+

+// Displays a table of redemptions for the specified promotional code.

+function uc_promo_redemptions($promo = false, $code) {

+  if ($promo === FALSE) {

+    drupal_set_message(t('Specified promotion does not exist.'), 'error');

+    drupal_goto('admin/store/settings/promo');

+  }

+

+  $output = '<div>'. t('<strong>Promotion:</strong> @name', array('@name' => check_plain($promo['promo_name']))) .'<br />';

+

+  if ($code === FALSE) {

+    drupal_set_message(t('Specified promotional code does not exist.'), 'error');

+    drupal_goto('admin/store/settings/promo/'. $promo['promo_id'] .'/codes');

+  }

+

+  $output .= t('<strong>Code:</strong> @code', array('@code' => check_plain($code['promo_code']))) .'</div>';

+

+  $output .= '<div>'. l(t('Return to promotional codes listing.'), 'admin/store/settings/promo/'. $promo['promo_id'] .'/codes') .'</div>';

+

+  $header = array(

+    array('data' => t('Redeemed on'), 'field' => 'redeemed', 'sort' => 'desc'),

+    array('data' => t('User'), 'field' => 'uid'),

+  );

+

+  if (drupal_substr($code, 0, 7) === 'BULKGEN') {

+    $header[] = array('data' => t('Bulk code'), 'field' => 'bulk_code');

+  }

+

+  $rows = array();

+

+  $result = pager_query("SELECT * FROM {uc_promo_code_redemptions} WHERE promo_code = '%s'". tablesort_sql($header), 30, 0, NULL, $code['promo_code']);

+  while ($data = db_fetch_array($result)) {

+    $row = array(

+      array('data' => format_date($data['redeemed'], 'short'), 'style' => 'white-space: nowrap'),

+      array('data' => $data['uid'] > 0 ? l($data['uid'], 'user/'. $data['uid']) : '0', 'width' => drupal_substr($code, 0, 7) === 'BULKGEN' ? '33%' : '50%'),

+    );

+

+    if (drupal_substr($code, 0, 7) === 'BULKGEN') {

+      $row[] = $data['bulk_code'];

+    }

+

+    $rows[] = $row;

+  }

+

+  if (count($rows) == 0) {

+    $rows[] = array(

+      array('data' => t('This promotional code has not been redeemed.'), 'colspan' => 5),

+    );

+  }

+

+  $output .= theme('table', $header, $rows) . theme('pager');

+

+  return $output;

+}

+

+/**

+ * Checks the database to see if a code is valid.

+ *

+ * @param $code

+ *   The promotional code to validate.

+ * @return

+ *   TRUE or FALSE depending on whether the code is valid or not.

+ */

+function uc_promo_validate_code($code, $user) {

+  // Return FALSE if the code is not found.

+  if (!$data = uc_promo_load_code($code)) {

+    return FALSE;

+  }

+

+  // Validate the code based on its type.

+  switch ($data['promo_code_type']) {

+    // Unlimited use promotional code.

+    case 1:

+      // Nothing should cause these to fail.

+      break;

+

+    // Specified number of uses.

+    case 2:

+      // Return FALSE if the code has been used up; handle bulk codes in a way

+      // that accounts for redemptions of individual bulk generated codes.

+      if (drupal_substr($data['promo_code'], 0, 7) === 'BULKGEN') {

+        $count = db_result(db_query("SELECT COUNT(*) FROM {uc_promo_code_redemptions} WHERE promo_code = '%s' AND bulk_code = '%s'", $data['promo_code'], $code));

+        if ($count >= $data['max_redemptions']) {

+          return FALSE;

+        }

+      }

+      else {

+        if ($data['redemptions'] >= $data['max_redemptions']) {

+          return FALSE;

+        }

+      }

+      break;

+

+    // Expiration date.

+    case 3:

+      // Return FALSE if the code has expired.

+      if (time() >= $data['expiration']) {

+        return FALSE;

+      }

+      break;

+  }

+

+  // Return FALSE if the promotion is no longer available or is disabled.

+  $active = db_result(db_query("SELECT promo_id FROM {uc_promos} WHERE promo_id = %d AND promo_status = 1", $data['promo_id']));

+  if (!$active) {

+    return FALSE;

+  }

+

+  $redeem_once = db_result(db_query("SELECT redeem_once FROM {uc_promos} WHERE promo_id = %d", $data['promo_id']));

+  if ($redeem_once == 1) {

+    $redeemed = db_result(db_query("SELECT COUNT(redemption_id) FROM {uc_promo_code_redemptions} WHERE promo_id = %d AND uid = %d", $data['promo_id'], $user->uid));

+    if ($redeemed > 0) {

+      return FALSE;

+    }

+  }

+

+  return TRUE;

+}

+

+/**

+ * Redeems a promotional code for a user by adding a redemption in the database

+ *   and updating the promotional code's data as needed.

+ *

+ * @param $code

+ *   The promotional code to redeem.

+ * @param $user

+ *   The user account for which the code is being redeemed.

+ * @param $bulk_id

+ *   If the code is a special BULKGEN code, $bulk_code should contain the actual

+ *     unique code used to redeem the promotion.

+ */

+function uc_promo_redeem_code($code, $user, $bulk_code = NULL) {

+  // Load the appropriate promotion ID.

+  $promo_id = db_result(db_query("SELECT promo_id FROM {uc_promo_codes} WHERE promo_code = '%s'", $code));

+

+  // Insert the redemption into the database.

+  db_query("INSERT INTO {uc_promo_code_redemptions} (redemption_id, promo_code, bulk_code, promo_id, uid, redeemed) VALUES (%d, '%s', '%s', %d, %d, %d)",

+      db_last_insert_id('uc_promo_code_redemptions', 'redemption_id'), $code, $bulk_code, $promo_id, $user->uid, time());

+

+  // Update the promotion to reflect the redemption.

+  db_query("UPDATE {uc_promo_codes} SET redemptions = redemptions + 1, last_redemption = %d WHERE promo_code = '%s'", time(), $code);

+}

+

+// Downloads a file containing a bulk generated set of promotional codes.

+function uc_promo_code_bulkgen ($promo, $code) {

+  // Load the data directly from the database.

+  $result = db_query("SELECT * FROM {uc_promo_codes} WHERE UPPER(promo_code) = '%s'", drupal_strtoupper($code['promo_code']));

+

+  if ($data = db_fetch_array($result)) {

+    $filename = $code['promo_code'] .'.csv';

+

+    header('Content-Type: application/octet-stream');

+    header('Content-Disposition: attachment; filename="'. $filename .'";');

+

+    $parts = explode('-', $code['promo_code']);

+

+    $codes = '';

+    for ($i = 0; $i < $parts[2]; $i++) {

+      $codes .=

+        drupal_strtoupper(str_pad(dechex($i), 5, 'U'. md5($data['bulkgen_seed'] . $i))

+        . drupal_substr(md5($i . $promo_id . $data['bulkgen_seed']), 0, $parts[1] - 5)) .",\n";

+    }

+

+    echo rtrim($codes, ",\n");

+

+    exit();

+  }

+  else {

+    return drupal_not_found();

+  }

+}

+

+// Rules action callback and form functions.

+function uc_promo_user_add_download ($account, $settings) {

+  _user_table_action('allow', $settings['fid'], $account->uid, NULL);

+}

+

+/**

+ * Configuration field for file download action

+ */

+function uc_promo_user_add_download_form ($settings = array(), &$form) {

+  if (!is_dir(variable_get('uc_file_base_dir', NULL))) {

+    drupal_set_message(t('A file directory needs to be configured in <a href="!url">product feature settings</a> before a file can be selected.', array('!url' => url('admin/store/settings/products/edit/features'))), 'error');

+  }

+

+  $form['uc_file_filename'] = array(

+    '#type' => 'textfield',

+    '#title' => t('File download'),

+    '#default_value' => isset ($settings['uc_file_filename']) ? $settings['uc_file_filename'] : '',

+    '#autocomplete_path' => '_autocomplete_file',

+    '#description' => t('The file that can be downloaded when product is purchased (enter a path relative to the %dir directory).', array('%dir' => variable_get('uc_file_base_dir', NULL))),

+  );

+}

+

+/**

+ * Validator for file download action config

+ */

+function uc_promo_user_add_download_validate ($form, &$form_state) {

+  if (!db_result(db_query("SELECT fid FROM {uc_files} WHERE filename = '%s'", $form_state['values']['uc_file_filename']))) {

+    form_set_error(

+      'uc_file_filename',

+      t(

+        '%file is not a valid file or directory inside file download directory.',

+        array('%file' => $form_state['values']['uc_file_filename'])

+      )

+    );

+  }

+}

+

+/**

+ * Submit handler for file download action config

+ */

+function uc_promo_user_add_download_submit(&$settings, $form, $form_state) {

+  $settings['fid'] = db_result(db_query(

+    "SELECT fid FROM {uc_files} WHERE filename = '%s'",

+    $form_state['values']['uc_file_filename']

+  ));

+}

+

+// Rules action callback and form functions for role.

+function uc_promo_user_add_role($account, $settings) {

+  // Grant (or renew) role upon successful use of promo code

+	$existing_role = db_fetch_object(db_query("SELECT * FROM {uc_roles_expirations} WHERE uid = %d AND rid = %d", $account->uid, $settings['rid']));

+	if (!is_null($existing_role->expiration)) {

+	  uc_roles_renew($account, $settings['rid'], _uc_roles_get_expiration($settings['duration'], $settings['granularity'], $existing_role->expiration));

+	  $comment = t('Customer user role %role renewed.', array('%role' => _uc_roles_get_name($settings['rid'])));

+	}

+	else {

+	  uc_roles_grant($account, $settings['rid'], _uc_roles_get_expiration($settings['duration'], $settings['granularity'], $existing_role->expiration));

+	  $comment = t('Customer granted user role %role.', array('%role' => _uc_roles_get_name($settings['rid'])));

+	}

+

+	$new_expiration = db_fetch_object(db_query("SELECT * FROM {uc_roles_expirations} WHERE uid = %d AND rid = %d", $account->uid, $settings['rid']));

+//	_role_email_user($op, $account, $new_expiration);

+}

+

+function uc_promo_user_add_role_form($settings = array(), &$form) {

+  uc_add_js('$(document).ready(function() { if ($("#edit-uc-roles-granularity").val() == "never") {$("#edit-uc-roles-qty").attr("disabled", "disabled").val("");} });', 'inline');

+

+  $form['uc_roles_role'] = array (

+    '#type'           => 'select',

+    '#title'          => t('Role'),

+    '#default_value'  => isset ($settings['rid']) ? $settings['rid'] : '',

+    '#description'    => t('This is the role the customer will receive after using the promo code.'),

+    '#options'        => _uc_roles_get_choices (),

+  );

+

+  $form['uc_roles_qty'] = array (

+    '#type'           => 'textfield',

+    '#title'          => t('Time until expiration'),

+    '#default_value'  => isset ($settings['duration']) ? $settings['duration'] : '',

+    '#size'           => 4,

+    '#maxlength'      => 4,

+    '#prefix'         => '<div class="expiration">',

+    '#suffix'         => '</div>',

+  );

+

+  $form['uc_roles_granularity'] = array (

+    '#type'     => 'select',

+    '#options'  => array(

+      'never' => t('never'),

+      'day'   => t('day(s)'),

+      'week'  => t('week(s)'),

+      'month' => t('month(s)'),

+      'year'  => t('year(s)')

+    ),

+    '#default_value' => isset ($settings['granularity']) ? $settings['granularity'] : '',

+    '#attributes' => array (  //Javascript to disable qty on never select

+      'onchange' => 'if (this.value == "never") {$("#edit-uc-roles-qty").attr("disabled", "disabled").val("");} else {$("#edit-uc-roles-qty").removeAttr("disabled");}'

+    ),

+    '#description'  => t('This will set how long the specified role will last until it expires.'),

+    '#prefix'       => '<div class="expiration">',

+    '#suffix'       => '</div>',

+  );

+}

+

+function uc_promo_user_add_role_validate($form, &$form_state) {

+  if ($form_state['values']['uc_roles_granularity'] != 'never' && intval($form_state['values']['uc_roles_qty']) < 1) {

+    form_set_error('uc_roles_qty', t('The amount of time must be a positive integer.'));

+  }

+  if (empty($form_state['values']['uc_roles_role'])) {

+    form_set_error('uc_roles_role', t('You must have a role to assign. You may need to <a href="!role_url">create a new role</a> or perhaps <a href="!feature_url">set role assignment defaults</a>.', array('!role_url' => url('admin/user/roles'), '!feature_url' => url('admin/store/settings/products/edit/features'))));

+  }

+}

+

+function uc_promo_user_add_role_submit (&$settings, $form, &$form_state) {

+  $granularity = ($form_state['values']['uc_roles_granularity'] != 'never') ? $form_state['values']['uc_roles_granularity'] : NULL;

+  $duration = ($form_state['values']['uc_roles_granularity'] != 'never') ? $form_state['values']['uc_roles_qty'] : NULL;

+

+  $settings['duration'] = $duration;

+  $settings['granularity'] = $granularity;

+  $settings['rid'] = $form_state['values']['uc_roles_role'];

+}

diff -urpN uc_promo/uc_promo.redeem.inc uc_promo/uc_promo.redeem.inc
--- uc_promo/uc_promo.redeem.inc	1969-12-31 19:00:00.000000000 -0500
+++ uc_promo/uc_promo.redeem.inc	2009-04-10 11:08:44.000000000 -0400
@@ -0,0 +1,76 @@
+<?php

+// $Id$ uc_promo.redeem.inc,v 2.1 2009/04/09 11:06:00 dwkitchen Exp $

+

+// Displays a form for redeeming promotions.

+function uc_promo_redeem () {

+  // Check for redemption access.

+  if (!user_access('redeem uc promotions')) {

+    if (variable_get('uc_promo_login_redirect', TRUE)) {

+      // Redirect if specified in the promotions settings.

+      drupal_set_message(t('You must login or create an account before you can redeem your promotional code.'));

+      drupal_goto('user/login', 'destination=promo');

+    }

+    else {

+      drupal_access_denied();

+    }

+  }

+

+  return drupal_get_form('uc_promo_redeem_form');

+}

+

+// Form builder function for a promotional code redemption form.

+function uc_promo_redeem_form (&$form_state) {

+  $form = array();

+

+  $form['promo_code'] = array(

+    '#type' => 'textfield',

+    '#title' => t('Promotional code'),

+    '#description' => t('Enter the code for the promotion you would like to redeem.'),

+    '#required' => TRUE,

+  );

+

+  $form['submit'] = array(

+    '#type' => 'submit',

+    '#value' => t('Redeem'),

+  );

+

+  return $form;

+}

+

+function uc_promo_redeem_form_validate($form, &$form_state) {

+  global $user;

+

+  // Check to make sure the code is valid.

+  if (!uc_promo_validate_code($form_state['values']['promo_code'], $user)) {

+    form_set_error('promo_code', t('You have entered an invalid promotional code.  Make sure you have entered code correctly and try again.'));

+  }

+}

+

+function uc_promo_redeem_form_submit($form, &$form_state) {

+  global $user;

+

+  // Load the promo code data.

+  $code_data = uc_promo_load_code($form_state['values']['promo_code']);

+

+  // Check if this is a bulk generated code; if so, we redeem it differently.

+  if ($code_data['promo_code'] !== $form_state['values']['promo_code']) {

+    $bulk_code = $form_state['values']['promo_code'];

+  }

+  else {

+    $bulk_code = NULL;

+  }

+

+  // Make the appropriate database updates for the redemption.

+  uc_promo_redeem_code($code_data['promo_code'], $user, $bulk_code);

+

+  $promo_data = uc_promo_load_promotion($code_data['promo_id']);

+

+  // Trigger any configurations assigned to this promotion's trigger.

+  // document here: http://drupal.org/node/298549

+  rules_invoke_event ('uc_promo_'. $code_data['promo_id'], $user);

+

+  // Send the user to the promotion's landing page.

+  $path = token_replace($promo_data['landing_page'], 'global');

+

+  $form_state['redirect'] = $path;

+}

