Index: promotion.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/promotion/promotion.module,v retrieving revision 1.12 diff -u -F^f -r1.12 promotion.module --- promotion.module 11 Jul 2007 15:36:46 -0000 1.12 +++ promotion.module 15 Aug 2007 09:59:13 -0000 @@ -1,5 +1,5 @@ user_access('manage promotional codes'), 'type' => MENU_CALLBACK, ); + $items[] = array( + 'path' => 'admin/user/promotion/' . $pid . '/download', + 'title' => t('Download'), + 'callback' => 'promotion_autogen_download', + 'callback arguments' => array($pid), + 'access' => user_access('manage promotional codes'), + 'type' => MENU_CALLBACK, + ); } if (arg(0) == 'admin' && arg(1) == 'logs' && arg(2) == 'promotion' && is_numeric(arg(3))) { @@ -174,15 +183,20 @@ function promotion_list() { array('data' => t('Expiration'), 'field' => 'expiration_date'), ); - $sql = 'SELECT pid, name, active_promotion, simple_code, start_date, end_date, expiration_date FROM {promotion_codes}'; + $sql = 'SELECT pid, code_type, name, active_promotion, simple_code, start_date, end_date, expiration_date FROM {promotion_codes}'; $sql .= tablesort_sql($header); $result = pager_query($sql, PROMO_CODES_PER_PAGE); while ($promo_code = db_fetch_object($result)) { + if ($promo_code->code_type == PROMO_TYPE_AUTOGEN) { + $simple_code = l($promo_code->simple_code,'admin/user/promotion/'.$promo_code->pid.'/download',array("title"=>"Download promotion codes.")); + } else { + $simple_code = $promo_code->simple_code; + } $rows[] = array( l($promo_code->name, 'admin/user/promotion/'.$promo_code->pid), $promo_code->active_promotion ? 'yes' : 'no', - $promo_code->simple_code, + $simple_code, $promo_code->start_date ? format_date($promo_code->start_date) : 'none', $promo_code->end_date ? format_date($promo_code->end_date) : 'none', $promo_code->expiration_date ? format_date($promo_code->expiration_date) : 'none', @@ -249,20 +263,22 @@ function promotion_form($promotion, $for 1 => t('Exact code. Use when you want all registrants to use the exact same code.'), 2 => t('Simple format. Use when you have a range of codes with numbers or letters. For example, if you want to accept codes %promo_000 through %promo_999, you could use the format %promo_123.', array('%promo_000' => 'PROMO-000', '%promo_999' => 'PROMO-999', '%promo_123' => 'PROMO-[123]')), # 3 => t('Advanced. Use custom PHP code to validate the code. Only do this if you have a complex code you want to use and know how to write PHP code.'), + 4 => t('Auto-generated codes. Use when you want a range of unique auto-generated 10-30 character codes. A .CSV file containing the generated codes can be downloaded from the link in the code field on the Promotional Codes list view page.'), ), '#required' => TRUE, ); $form['code']['simple_code'] = array( '#type' => 'textfield', - '#title' => t('Exact or Simple code'), - '#description' => t('If you selected Exact code above, enter that code here. If you chose Simple format, you may use [] containing letters or numbers to indicate that you accept any letter or number at that position in the code. All other letters, numbers, and symbols will be matched exactly. For example, "PROMO-[000]" would accept "PROMO-123" and "promo-123," but not "PRO-123" and not "PROMO-ABC." Promotional codes are not case sensitive.'), + '#title' => t('Exact, Simple, or Auto-generated codes'), + '#description' => t('If you selected Exact code above, enter that code here. If you chose Simple format, you may use [] containing letters or numbers to indicate that you accept any letter or number at that position in the code. All other letters, numbers, and symbols will be matched exactly. For example, "PROMO-[000]" would accept "PROMO-123" and "promo-123," but not "PRO-123" and not "PROMO-ABC." If you chose Auto-generated codes, enter the length of the code (a number between 10 and 30), followed by a "-", followed by the number of codes to be generated (maximum 100000). For example, "10-1000" will generate 1000 unique 10 character codes. Promotional codes are not case sensitive.'), '#default_value' => $promotion->simple_code, '#size' => 30, '#maxlength' => 30, '#required' => TRUE, ); + # $form['code']['advanced_validator'] = array( # '#type' => 'textarea', # '#title' => t('Advanced Validation Code'), @@ -477,6 +493,12 @@ function promotion_form_validate($form_i if (!preg_match('/^(?:[^\[\]]+|\[[0-9a-zA-Z]*\])+$/', $form_values['simple_code'])) { form_set_error('simple_code', t('The simple code format given is not valid. Make sure you do not have an umatched "[" or "]" and that any brackets contain only letters and numbers.')); } + } + + else if ($form_values['code_type'] == PROMO_TYPE_AUTOGEN) { + if (!preg_match('/^(1\d|2\d|30)-(10{5}|[1-9]\d{0,4})$/', $form_values['simple_code'])) { + form_set_error('simple_code', t('The auto-generated code format given is not valid. Make sure you use the format "X-Y", where X is a two digit number between 10 and 30 (inclusive), and Y is any number between 1 and 100000. For example, "10-1000" will generate 1000 unique 10 character codes.')); + } } // Tests for delayed activation @@ -659,8 +681,8 @@ function promotion_form_submit($form_id, INSERT INTO {promotion_codes} (pid, name, description, active_promotion, start_date, end_date, code_type, simple_code, max_use_count, accounts_expire, expiration_date, expiration_action, activate_when, - activation_date) - VALUES (%d, '%s', '%s', %d, %d, %d, %d, '%s', %d, %d, %d, %d, %d, %d)", + activation_date,autogen_code_seed) + VALUES (%d, '%s', '%s', %d, %d, %d, %d, '%s', %d, %d, %d, %d, %d, %d,'%s')", $pid, $form_values['name'], $form_values['description'], @@ -674,7 +696,8 @@ function promotion_form_submit($form_id, $form_values['expiration_date'], $form_values['expiration_action'], $form_values['activate_when'], - $form_values['activation_date'] + $form_values['activation_date'], + sha1(mktime().rand()) ); _promotion_update_roles($pid, $form_values['promotion_roles'], 1); @@ -711,6 +734,31 @@ function promotion_edit($pid) { } } +function promotion_autogen_download($pid) { + + // Load the promotion, and generate the codes for download. + if ($promotion = promotion_get_promotion($pid)) { + $filename = t($promotion->name.'.csv'); + header('Content-Type: application/octet-stream'); + header('Content-Disposition: attachment; filename="' . $filename . '";'); + + $simple_code = explode("-",$promotion->simple_code); + $codes = ''; + for ($i = 0; $i < $simple_code[1]; $i++) { + + $codes .= strtoupper(str_pad(dechex($i),5,"U".md5($promotion->autogen_code_seed.$i)).substr(md5($i.$pid.$promotion->autogen_code_seed),0, $simple_code[0] - 5)).",\n"; + } + + echo rtrim($codes,",\n"); + exit; + } + + else { + return drupal_not_found(); + } + +} + function promotion_delete_confirm($pid) { $promotion = promotion_get_promotion($pid); @@ -1017,6 +1065,34 @@ function promotion_match($promotion, $co return preg_match("/^$simple_match\$/i", $code); } + // Compare auto-generated codes + elseif ($promotion->code_type == PROMO_TYPE_AUTOGEN) { + + // Parse the promotion code + $simple_code = explode('-', $promotion->simple_code); + + // First check that the code is the correct length. + if (strlen($code) != $simple_code[0]) { + return false; + } + + // 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). + $code_sequence = explode("U",substr(strtoupper($code),0,5)); + if (preg_match("/^[0-9ABCDEF]{1,5}$/", $code_sequence[0])) { + $code_sequence = hexdec($code_sequence[0]); + if ($code_sequence >= $simple_code[1]) { + return false; + } + } else { + return false; + } + // Last, check that the MD5 hash part of code is valid. + $code_hash = substr(md5($code_sequence.$promotion->pid.$promotion->autogen_code_seed), 0, ($simple_code[0] - 5)); + return strcasecmp($code_hash, substr($code,5)) == 0; + + } + // Shouldn't happen else { watchdog('promotion',