/[drupal]/contributions/modules/uc_promo/uc_promo.module
ViewVC logotype

Contents of /contributions/modules/uc_promo/uc_promo.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.1 - (show annotations) (download) (as text)
Wed Dec 10 16:11:12 2008 UTC (11 months, 2 weeks ago) by rszrama
Branch: MAIN
CVS Tags: DRUPAL-5--1-0, HEAD
Branch point for: DRUPAL-5
File MIME type: text/x-php
Initial commit of D5 version.
1 <?php
2
3 // $Id$
4
5 /**
6 * @file
7 * Create promotions with redeemable promotional codes that users can redeem to
8 * perform special actions in your store.
9 */
10
11
12 /**
13 * Implementation of hook_menu().
14 */
15 function uc_promo_menu($may_cache) {
16 $items = array();
17
18 if ($may_cache) {
19 $items[] = array(
20 'path' => 'promo',
21 'title' => t('Redeem a promotion'),
22 'description' => t('Form to redeem a promotional code.'),
23 'callback' => 'uc_promo_redeem',
24 'callback arguments' => array(''),
25 'access' => TRUE,
26 'type' => MENU_CALLBACK,
27 );
28 $items[] = array(
29 'path' => 'admin/store/settings/promo',
30 'title' => t('Promotions'),
31 'description' => t('Setup and administer your promotions.'),
32 'callback' => 'uc_promo_admin',
33 'access' => user_access('administer uc promotions'),
34 'type' => MENU_NORMAL_ITEM,
35 );
36 $items[] = array(
37 'path' => 'admin/store/settings/promo/overview',
38 'title' => t('Overview'),
39 'description' => t('Setup and administer your promotions.'),
40 'type' => MENU_DEFAULT_LOCAL_TASK,
41 'weight' => -10,
42 );
43 $items[] = array(
44 'path' => 'admin/store/settings/promo/add',
45 'title' => t('Add a promotion'),
46 'description' => t('Setup and administer your promotions.'),
47 'callback' => 'drupal_get_form',
48 'callback arguments' => array('uc_promo_promotion_form', 0),
49 'access' => user_access('administer uc promotions'),
50 'type' => MENU_LOCAL_TASK,
51 'weight' => 0,
52 );
53 $items[] = array(
54 'path' => 'admin/store/settings/promo/settings',
55 'title' => t('Settings'),
56 'description' => t('Adjust the general promotions settings.'),
57 'callback' => 'drupal_get_form',
58 'callback arguments' => array('uc_promo_admin_form'),
59 'access' => user_access('administer uc promotions'),
60 'type' => MENU_LOCAL_TASK,
61 'weight' => 10,
62 );
63 }
64 else {
65 if (arg(3) === 'promo' && intval(arg(4)) > 0) {
66 $items[] = array(
67 'path' => 'admin/store/settings/promo/'. arg(4) .'/edit',
68 'title' => t('Edit promotion'),
69 'description' => t('Delete an existing promotion.'),
70 'callback' => 'drupal_get_form',
71 'callback arguments' => array('uc_promo_promotion_form', arg(4)),
72 'access' => user_access('administer uc promotions'),
73 'type' => MENU_CALLBACK,
74 );
75 $items[] = array(
76 'path' => 'admin/store/settings/promo/'. arg(4) .'/delete',
77 'title' => t('Delete promotion'),
78 'description' => t('Delete a promotion.'),
79 'callback' => 'drupal_get_form',
80 'callback arguments' => array('uc_promo_delete_promotion_form', arg(4)),
81 'access' => user_access('administer uc promotions'),
82 'type' => MENU_CALLBACK,
83 );
84
85 if (arg(5) === 'codes') {
86 $items[] = array(
87 'path' => 'admin/store/settings/promo/'. arg(4) .'/codes',
88 'title' => t('Promotional codes'),
89 'description' => t('Administer promotional codes for a given promotion.'),
90 'callback' => 'uc_promo_codes',
91 'callback arguments' => array(arg(4)),
92 'access' => user_access('administer uc promotions'),
93 'type' => MENU_CALLBACK,
94 );
95 $items[] = array(
96 'path' => 'admin/store/settings/promo/'. arg(4) .'/codes/overview',
97 'title' => t('Overview'),
98 'description' => t('Administer promotional codes for a given promotion.'),
99 'type' => MENU_DEFAULT_LOCAL_TASK,
100 'weight' => -10,
101 );
102 $items[] = array(
103 'path' => 'admin/store/settings/promo/'. arg(4) .'/codes/add',
104 'title' => t('Add code'),
105 'description' => t('Add a promotional code for the promotion.'),
106 'callback' => 'drupal_get_form',
107 'callback arguments' => array('uc_promo_codes_form', arg(4), 0),
108 'access' => user_access('administer uc promotions'),
109 'type' => MENU_LOCAL_TASK,
110 'weight' => 0,
111 );
112 $items[] = array(
113 'path' => 'admin/store/settings/promo/'. arg(4) .'/codes/'. arg(6) .'/download',
114 'title' => t('Download bulk generated codes'),
115 'callback' => 'uc_promo_code_bulkgen',
116 'callback arguments' => array(arg(4), arg(6)),
117 'access' => user_access('administer uc promotions'),
118 'type' => MENU_CALLBACK,
119 );
120 $items[] = array(
121 'path' => 'admin/store/settings/promo/'. arg(4) .'/codes/'. arg(6) .'/edit',
122 'title' => t('Edit code'),
123 'description' => t('Edit a promotional code.'),
124 'callback' => 'drupal_get_form',
125 'callback arguments' => array('uc_promo_codes_form', arg(4), arg(6)),
126 'access' => user_access('administer uc promotions'),
127 'type' => MENU_CALLBACK,
128 );
129 $items[] = array(
130 'path' => 'admin/store/settings/promo/'. arg(4) .'/codes/'. arg(6) .'/delete',
131 'title' => t('Delete code'),
132 'description' => t('Delete a promotional code.'),
133 'callback' => 'drupal_get_form',
134 'callback arguments' => array('uc_promo_delete_code_form', arg(4), arg(6)),
135 'access' => user_access('administer uc promotions'),
136 'type' => MENU_CALLBACK,
137 );
138 $items[] = array(
139 'path' => 'admin/store/settings/promo/'. arg(4) .'/codes/'. arg(6) .'/redemptions',
140 'title' => t('Promotional code redemptions'),
141 'description' => t('A table of redemptions for a promotional code.'),
142 'callback' => 'uc_promo_redemptions',
143 'callback arguments' => array(arg(4), arg(6)),
144 'access' => user_access('administer uc promotions'),
145 'type' => MENU_CALLBACK,
146 );
147 $items[] = array(
148 'path' => 'admin/store/settings/promo/'. arg(4) .'/codes/back',
149 'title' => t('Back to promotions'),
150 'callback' => 'drupal_goto',
151 'callback arguments' => array('admin/store/settings/promo'),
152 'access' => user_access('administer uc promotions'),
153 'type' => MENU_LOCAL_TASK,
154 'weight' => 10,
155 );
156 }
157 }
158 }
159
160 return $items;
161 }
162
163 /**
164 * Implementation of hook_perm().
165 */
166 function uc_promo_perm() {
167 return array('administer uc promotions', 'redeem uc promotions');
168 }
169
170 /**
171 * Implementation of Workflow-ng hook_event_info().
172 */
173 function uc_promo_event_info() {
174 $events = array();
175
176 $result = db_query("SELECT promo_id, promo_name FROM {uc_promos}");
177 while ($promo = db_fetch_object($result)) {
178 $events['uc_promo_'. $promo->promo_id] = array(
179 '#label' => t('@name', array('@name' => $promo->promo_name)),
180 '#module' => 'Promotions',
181 '#arguments' => array(
182 'user' => array('#entity' => 'user', '#label' => t('User')),
183 ),
184 );
185 }
186
187 return $events;
188 }
189
190 /**
191 * Implementation of Workflow-ng hook_action_info().
192 */
193 function uc_promo_action_info() {
194 $actions = array();
195
196 // Provide integration for File Downloads module.
197 if (module_exists('uc_file')) {
198 // Add a file download to a user's account.
199 $actions['uc_promo_user_add_download'] = array(
200 '#label' => t('Give a user a file download.'),
201 '#arguments' => array(
202 'user' => array('#entity' => 'user', '#label' => t('User account')),
203 ),
204 '#module' => t('Promotions'),
205 );
206 }
207
208 return $actions;
209 }
210
211 // Displays a form for redeeming promotions.
212 function uc_promo_redeem() {
213 // Check for redemption access.
214 if (!user_access('redeem uc promotions')) {
215 if (variable_get('uc_promo_login_redirect', TRUE)) {
216 // Redirect if specified in the promotions settings.
217 drupal_set_message(t('You must login or create an account before you can redeem your promotional code.'));
218 drupal_goto('user/login', 'destination=promo');
219 }
220 else {
221 drupal_access_denied();
222 }
223 }
224
225 return drupal_get_form('uc_promo_redeem_form', $code);
226 }
227
228 // Form builder function for a promotional code redemption form.
229 function uc_promo_redeem_form() {
230 $form = array();
231
232 $form['promo_code'] = array(
233 '#type' => 'textfield',
234 '#title' => t('Promotional code'),
235 '#description' => t('Enter the code for the promotion you would like to redeem.'),
236 '#required' => TRUE,
237 );
238
239 $form['submit'] = array(
240 '#type' => 'submit',
241 '#value' => t('Submit'),
242 );
243
244 return $form;
245 }
246
247 function uc_promo_redeem_form_validate($form_id, $form_values) {
248 global $user;
249
250 // Check to make sure the code is valid.
251 if (!uc_promo_validate_code($form_values['promo_code'], $user)) {
252 form_set_error('promo_code', t('You have entered an invalid promotional code. Make sure you have entered code correctly and try again.'));
253 }
254 }
255
256 function uc_promo_redeem_form_submit($form_id, $form_values) {
257 global $user;
258
259 // Load the promo code data.
260 $code_data = uc_promo_load_code($form_values['promo_code']);
261
262 // Check if this is a bulk generated code; if so, we redeem it differently.
263 if ($code_data['promo_code'] !== $form_values['promo_code']) {
264 $bulk_code = $form_values['promo_code'];
265 }
266 else {
267 $bulk_code = NULL;
268 }
269
270 // Make the appropriate database updates for the redemption.
271 uc_promo_redeem_code($code_data['promo_code'], $user, $bulk_code);
272
273 $promo_data = uc_promo_load_promotion($code_data['promo_id']);
274
275 // Trigger any configurations assigned to this promotion's trigger.
276 workflow_ng_invoke_event('uc_promo_'. $code_data['promo_id'], $user);
277
278 // Send the user to the promotion's landing page.
279 $path = token_replace($promo_data['landing_page'], 'global');
280
281 return $path;
282 }
283
284 // Displays an admin table for existing promotions and a settings form.
285 function uc_promo_admin() {
286 $header = array(
287 array('data' => t('ID'), 'field' => 'promo_id'),
288 array('data' => t('Created'), 'field' => 'created', 'sort' => 'desc'),
289 array('data' => t('Name'), 'field' => 'promo_name'),
290 array('data' => t('Status'), 'field' => 'promo_status'),
291 t('Operations'),
292 );
293
294 $rows = array();
295
296 // Get all the promotions from the database.
297 $result = pager_query("SELECT * FROM {uc_promos}". tablesort_sql($header), 30);
298 while ($data = db_fetch_array($result)) {
299 $ops = array(
300 l(t('codes'), 'admin/store/settings/promo/'. $data['promo_id'] .'/codes'),
301 l(t('edit'), 'admin/store/settings/promo/'. $data['promo_id'] .'/edit'),
302 l(t('delete'), 'admin/store/settings/promo/'. $data['promo_id'] .'/delete'),
303 );
304
305 $row = array(
306 check_plain($data['promo_id']),
307 array('data' => format_date($data['created'], 'short'), 'style' => 'white-space: nowrap'),
308 check_plain($data['promo_name']),
309 $data['promo_status'] ? t('Active') : t('Disabled'),
310 array('data' => implode(' ', $ops), 'style' => 'white-space: nowrap;'),
311 );
312
313 $rows[] = $row;
314 }
315
316 if (count($rows) == 0) {
317 $rows[] = array(
318 array('data' => t('No promotions created yet.'), 'colspan' => 4),
319 );
320 }
321
322 return theme('table', $header, $rows) . theme('pager') . l(t('Add a promotion.'), 'admin/store/settings/promo/add');
323 }
324
325 // Form builder for the promo admin settings form.
326 function uc_promo_admin_form() {
327 $form = array();
328
329 $form['uc_promo_login_redirect'] = array(
330 '#type' => 'checkbox',
331 '#title' => t('Redirect users without access to redeem promotions to the login page.'),
332 '#description' => t('Check this setting if authenticated users has access but anonymous users do not.'),
333 '#default_value' => variable_get('uc_promo_login_redirect', TRUE),
334 );
335 $form['uc_promo_delete_behavior'] = array(
336 '#type' => 'radios',
337 '#title' => t('Promotion delete behavior'),
338 '#options' => array(
339 0 => t('Delete the promotion and any related promotional codes.'),
340 1 => t('Delete the promotion and any related unredeemed promotional codes.'),
341 2 => t('Delete the promotion.'),
342 ),
343 '#default_value' => variable_get('uc_promo_delete_behavior', 0),
344 );
345
346 return system_settings_form($form);
347 }
348
349 // Form builder for adding or editing a promotion.
350 function uc_promo_promotion_form($promo_id) {
351 $form = array();
352
353 // Load the specified promotion; will be FALSE if $promo_id == 0.
354 $data = uc_promo_load_promotion($promo_id);
355
356 $form['promo_id'] = array(
357 '#type' => 'value',
358 '#value' => $data === FALSE ? 0 : $promo_id,
359 );
360
361 $form['promo_name'] = array(
362 '#type' => 'textfield',
363 '#title' => t('Name'),
364 '#description' => t('Displayed on the admin overview and to the user when they redeem a code for this promotion.'),
365 '#max_length' => 255,
366 '#default_value' => $data['promo_name'],
367 '#required' => TRUE,
368 );
369
370 $form['landing_page'] = array(
371 '#type' => 'textfield',
372 '#title' => t('Landing page'),
373 '#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'))),
374 '#default_value' => $data['landing_page'],
375 '#size' => 32,
376 '#field_prefix' => url(NULL, NULL, NULL, TRUE) . (variable_get('clean_url', 0) ? '' : '?q='),
377 );
378
379 $form['redeem_once'] = array(
380 '#type' => 'checkbox',
381 '#title' => t('Users can only redeem this promotion once with any code.'),
382 '#description' => t('Use this to prevent users from taking advantage of unlimited use promotion codes.'),
383 '#default_value' => $data['redeem_once'],
384 );
385
386 $form['promo_status'] = array(
387 '#type' => 'radios',
388 '#title' => t('Status'),
389 '#options' => array(
390 0 => t('Disabled'),
391 1 => t('Active'),
392 ),
393 '#default_value' => $data === FALSE ? 1 : $data['promo_status'],
394 '#required' => TRUE,
395 );
396
397 $form['save'] = array(
398 '#type' => 'submit',
399 '#value' => t('Save'),
400 '#suffix' => l(t('Cancel'), 'admin/store/settings/promo'),
401 );
402
403 return $form;
404 }
405
406 function uc_promo_promotion_form_submit($form_id, $form_values) {
407 uc_promo_save_promotion($form_values);
408
409 drupal_set_message(t('Promotion saved.'));
410
411 return 'admin/store/settings/promo';
412 }
413
414 // Form buider for deleting a promotion.
415 function uc_promo_delete_promotion_form($promo_id) {
416 $data = uc_promo_load_promotion($promo_id);
417 $form = array();
418
419 // Fail if the promotion doesn't exist.
420 if (!$data) {
421 drupal_set_message(t("The specified promotion doesn't exist."), 'error');
422 drupal_goto('admin/store/settings/promo');
423 }
424
425 $form['promo_id'] = array(
426 '#type' => 'value',
427 '#value' => $promo_id,
428 );
429
430 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'));
431 }
432
433 function uc_promo_delete_promotion_form_submit($form_id, $form_values) {
434 uc_promo_delete_promotion($form_values['promo_id']);
435
436 drupal_set_message(t('The promotion has been deleted.'));
437
438 return 'admin/store/settings/promo';
439 }
440
441 /**
442 * Loads a promotion from the database.
443 *
444 * @param $promo_id
445 * The ID of the promotion to load.
446 * @return
447 * An array of data for the promotion.
448 */
449 function uc_promo_load_promotion($promo_id) {
450 $result = db_query("SELECT * FROM {uc_promos} WHERE promo_id = %d", $promo_id);
451
452 return db_fetch_array($result);
453 }
454
455 /**
456 * Saves or inserts a new promotion.
457 *
458 * @param $data
459 * An array of data representing the promotion.
460 */
461 function uc_promo_save_promotion($data) {
462 // First try to update an existing promotion.
463 if ($data['promo_id'] > 0) {
464 db_query("UPDATE {uc_promos} SET promo_name = '%s', landing_page = '%s', redeem_once = %d, promo_status = %d WHERE promo_id = %d",
465 $data['promo_name'], $data['landing_page'], $data['redeem_once'],
466 $data['promo_status'], $data['promo_id']);
467 }
468
469 // Otherwise insert a new promotion.
470 if ($data['promo_id'] === 0 || db_affected_rows() == 0) {
471 db_query("INSERT INTO {uc_promos} (promo_id, promo_name, landing_page, redeem_once, promo_status, created) VALUES (%d, '%s', '%s', %d, %d, %d)",
472 $data['promo_id'] == 0 ? db_next_id('{uc_promos}_promo_id') : $data['promo_id'],
473 $data['promo_name'], $data['landing_page'], $data['redeem_once'],
474 $data['promo_status'], time());
475 }
476 }
477
478 /**
479 * Delete a promotion from the database.
480 *
481 * @param $promo_id
482 * The ID of the promotion to delete.
483 */
484 function uc_promo_delete_promotion($promo_id) {
485 // Delete related codes per the user settings.
486 switch (variable_get('uc_promo_delete_behavior', 0)) {
487 case 0:
488 // Delete any related promotional codes.
489 db_query("DELETE FROM {uc_promo_code_redemptions} WHERE promo_id = %d", $promo_id);
490 db_query("DELETE FROM {uc_promo_codes} WHERE promo_id = %d", $promo_id);
491 break;
492
493 case 1:
494 // Delete related promotional codes that haven't been redeemed.
495 db_query("DELETE FROM {uc_promo_codes} WHERE promo_id = %d AND redemptions > 0", $promo_id);
496 break;
497 }
498
499 // Get the name for use in the log entry.
500 $name = db_result(db_query("SELECT promo_name FROM {uc_promos} WHERE promo_id = %d", $promo_id));
501
502 // Delete the promotion itself.
503 db_query("DELETE FROM {uc_promos} WHERE promo_id = %d", $promo_id);
504
505 watchdog('uc_promo', t('Promotion %name deleted.', array('%name' => !empty($name) ? $name : $promo_id)));
506 }
507
508 function uc_promo_codes($promo_id) {
509 // Load the specified promo code; will be FALSE if $code == 0.
510 $data = uc_promo_load_promotion($promo_id);
511
512 if ($data === FALSE) {
513 drupal_set_message(t('Specified promotion does not exist.'), 'error');
514 drupal_goto('admin/store/settings/promo');
515 }
516
517 $output = t('Promotion: %name', array('%name' => check_plain($data['promo_name'])));
518
519 $header = array(
520 array('data' => t('Code'), 'field' => 'promo_code'),
521 array('data' => t('Type'), 'field' => 'promo_code_type'),
522 array('data' => t('Redemptions'), 'field' => 'redemptions'),
523 array('data' => t('Last redemption'), 'field' => 'last_redemption', 'sort' => 'desc'),
524 t('Operations'),
525 );
526
527 $rows = array();
528
529 // Get all the promotions from the database.
530 $result = pager_query("SELECT * FROM {uc_promo_codes} WHERE promo_id = %d". tablesort_sql($header), 30, 0, NULL, $promo_id);
531 while ($data = db_fetch_array($result)) {
532 // Create a download link for bulk generated codes.
533 if (!empty($data['bulkgen_seed'])) {
534 $download = '<br />'. l(t('Download codes.'), 'admin/store/settings/promo/'. $data['promo_id'] .'/codes/'. $data['promo_code'] .'/download');
535 }
536 else {
537 $download = '';
538 }
539
540 // Create an array of operations for the code.
541 $ops = array(
542 l(t('redemptions'), 'admin/store/settings/promo/'. $data['promo_id'] .'/codes/'. $data['promo_code'] .'/redemptions'),
543 l(t('edit'), 'admin/store/settings/promo/'. $data['promo_id'] .'/codes/'. $data['promo_code'] .'/edit'),
544 l(t('delete'), 'admin/store/settings/promo/'. $data['promo_id'] .'/codes/'. $data['promo_code'] .'/delete'),
545 );
546
547 switch ($data['promo_code_type']) {
548 case 1:
549 $type = t('Unlimited');
550 break;
551 case 2:
552 $type = t('Limited: @number', array('@number' => $data['max_redemptions']));
553 break;
554 case 3:
555 $type = t('Expires @date', array('@date' => format_date($data['expiration'], 'short')));
556 break;
557 }
558
559 $row = array(
560 'data' => array(
561 check_plain($data['promo_code']) . $download,
562 array('data' => $type, 'style' => 'white-space: nowrap;'),
563 $data['redemptions'],
564 array('data' => $data['last_redemption'] > 0 ? format_date($data['last_redemption'], 'short') : '-', 'style' => 'white-space: nowrap;'),
565 array('data' => implode(' ', $ops), 'style' => 'white-space: nowrap;'),
566 ),
567 'valign' => 'top',
568 );
569
570 $rows[] = $row;
571 }
572
573 if (count($rows) == 0) {
574 $rows[] = array(
575 array('data' => t('No promotional codes created yet.'), 'colspan' => 5),
576 );
577 }
578
579 $output .= theme('table', $header, $rows) . theme('pager') . l(t('Add promotional codes.'), 'admin/store/settings/promo/'. $promo_id .'/codes/add');
580
581 return $output;
582 }
583
584 function uc_promo_codes_form($promo_id, $code) {
585 $form = array();
586
587 // Load the specified promotion; will be FALSE if $code == 0.
588 $data = uc_promo_load_promotion($promo_id);
589
590 if ($data === FALSE) {
591 drupal_set_message(t('Specified promotion does not exist.'), 'error');
592 drupal_goto('admin/store/settings/promo');
593 }
594
595 $form['promo_name'] = array(
596 '#value' => t('Promotion: %name', array('%name' => check_plain($data['promo_name']))),
597 );
598
599 $form['promo_id'] = array(
600 '#type' => 'value',
601 '#value' => $promo_id,
602 );
603
604 // Load the specified promo code; will be FALSE if $code == 0.
605 $data = uc_promo_load_code($code);
606
607 $code_disabled = FALSE;
608
609 if ($data !== FALSE) {
610 $form['promo_code_orig'] = array(
611 '#type' => 'value',
612 '#value' => $code,
613 );
614
615 // Disable editing the code if we're dealing with a bulkgen code.
616 if (!empty($data['bulkgen_seed'])) {
617 $code_disabled = TRUE;
618 }
619 else {
620 // Otherwise disable it if it's already been redeemed.
621 $count = db_result(db_query("SELECT COUNT(*) FROM {uc_promo_code_redemptions} WHERE promo_code = '%s'", $code));
622 if ($count > 0) {
623 $code_disabled = TRUE;
624 }
625 }
626 }
627
628
629 $form['promo_code'] = array(
630 '#type' => 'textfield',
631 '#title' => t('Promotional code'),
632 '#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".'),
633 '#default_value' => $data === FALSE ? '' : $data['promo_code'],
634 '#required' => $data === FALSE ? FALSE : TRUE,
635 '#disabled' => $code_disabled,
636 );
637
638 if ($code === 0) {
639 $form['bulk'] = array(
640 '#type' => 'fieldset',
641 '#title' => t('Bulk code generation'),
642 '#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.'),
643 '#collapsible' => TRUE,
644 '#collapsed' => TRUE,
645 );
646 $form['bulk']['bulk_generate'] = array(
647 '#type' => 'checkbox',
648 '#title' => t('Bulk generate codes for this promotion.'),
649 );
650 /*$form['bulk']['code_rule'] = array(
651 '#type' => 'radios',
652 '#title' => t('Code generation rule'),
653 '#options' => array(
654 'numeric' => t('Random numeric promotional codes.'),
655 'alpha_numeric' => t('Random alpha-numeric promotional codes.'),
656 ),
657 );*/
658 $form['bulk']['number_codes'] = array(
659 '#type' => 'textfield',
660 '#title' => t('Number of codes'),
661 '#description' => t('Enter in the number of codes you want to generate.'),
662 );
663 $form['bulk']['code_length'] = array(
664 '#type' => 'select',
665 '#title' => t('Code length'),
666 '#options' => drupal_map_assoc(range(10, 30)),
667 );
668 }
669
670 $form['promo_code_type'] = array(
671 '#type' => 'radios',
672 '#title' => t('Promotional code type'),
673 '#description' => t('Limited or expirable promotional codes should specify their settings below.'),
674 '#options' => array(
675 1 => t('Unlimited number of redemptions'),
676 2 => t('Limited number of redemptions'),
677 3 => t('Expires on a certain date'),
678 ),
679 '#default_value' => $data['promo_code_type'],
680 '#required' => TRUE,
681 );
682
683 $form['max_redemptions'] = array(
684 '#type' => 'textfield',
685 '#title' => t('Maximum number of redemptions'),
686 '#description' => t('Only applicable to limited promotional codes.'),
687 '#default_value' => $data['max_redemptions'],
688 );
689
690 $form['expiration'] = array(
691 '#type' => 'date',
692 '#title' => t('Expiration date'),
693 '#description' => t('The code will fail after 23:59 of the specified date.<br />Only applicable to expirable promotional codes.'),
694 '#default_value' => array(
695 'year' => date('Y', $data === FALSE ? time() : $data['expiration']),
696 'month' => date('n', $data === FALSE ? time() : $data['expiration']),
697 'day' => date('j', $data === FALSE ? time() : $data['expiration']),
698 ),
699 );
700
701 $form['save'] = array(
702 '#type' => 'submit',
703 '#value' => t('Save'),
704 );
705
706 $form['save_add'] = array(
707 '#type' => 'submit',
708 '#value' => t('Save and add another'),
709 '#suffix' => l(t('Cancel'), 'admin/store/settings/promo/'. $promo_id .'/codes'),
710 );
711
712 return $form;
713 }
714
715 function uc_promo_codes_form_validate($form_id, $form_values) {
716 // Fail if no promo code was entered and bulk generation was not selected.
717 if (!$form_values['bulk_generate'] && empty($form_values['promo_code'])) {
718 form_set_error('promo_code', t('You must specify a promotional code.'));
719 }
720
721 // Fail if the user entered an invalid bulk generated code length.
722 if ($form_values['bulk_generate'] && (intval($form_values['code_length']) < 10 || intval($form_values['code_length']) > 30)) {
723 form_set_error('code_length', t('You must specify a code length between 10 and 30.'));
724 }
725
726 // Fail if the user entered an invalid number of bulk generated codes.
727 if ($form_values['bulk_generate'] && intval($form_values['number_codes']) <= 0) {
728 form_set_error('number_codes', t('You must specify a number of codes greater than 0.'));
729 }
730
731 // Fail if the promotional code has invalid characters.
732 if (!empty($form_values['promo_code']) && !preg_match('!^[a-zA-Z0-9_-]+$!', $form_values['promo_code'])) {
733 form_set_error('promo_code', t('The promotional code can only consist of letters, numbers, underscores, and dashes.'));
734 }
735
736 // Fail if the promotional code entered is non-unique.
737 if (!isset($form_values['promo_code_orig']) || $form_values['promo_code_orig'] != $form_values['promo_code']) {
738 $exists = db_result(db_query("SELECT COUNT(*) FROM {uc_promo_codes} WHERE UPPER(promo_code) = '%s'", strtoupper($form_values['promo_code'])));
739 if ($exists > 0) {
740 form_set_error('promo_code', t('That promotional code has already been used.'));
741 }
742 }
743
744 // Fail if the user entered an invalid value for redemptions.
745 if ($form_values['promo_code_type'] == 2 && intval($form_values['max_redemptions']) <= 0) {
746 form_set_error('max_redemptions', t('You must specify a number of redemptions greater than 0.'));
747 }
748 }
749
750 function uc_promo_codes_form_submit($form_id, $form_values) {
751 // Convert the expiration date to a timestamp.
752 $form_values['expiration'] = gmmktime(23, 59, 59, $form_values['expiration']['month'], $form_values['expiration']['day'], $form_values['expiration']['year']);
753
754 // Special handling for bulk generated codes.
755 if ($form_values['bulk_generate']) {
756 $bulkgen_id = variable_get('uc_promo_bulkgen_id', 1);
757 variable_set('uc_promo_bulkgen_id', $bulkgen_id + 1);
758
759 // Manually set the promo code name.
760 $form_values['promo_code'] = 'BULKGEN'. $bulkgen_id .'-'. $form_values['code_length'] .'-'. $form_values['number_codes'];
761
762 // Setup the bulk generator seed.
763 $form_values['bulkgen_seed'] = sha1(mktime() . rand());
764 }
765
766 uc_promo_save_code($form_values);
767
768 drupal_set_message(t('Promotional code saved.'));
769
770 if ($form_values['op'] == t('Save')) {
771 return 'admin/store/settings/promo/'. $form_values['promo_id'] .'/codes';
772 }
773 }
774
775 // Form buider for deleting a promotional code.
776 function uc_promo_delete_code_form($promo_id, $code) {
777 $data = uc_promo_load_code($code);
778 $form = array();
779
780 // Fail if the promotional code doesn't exist.
781 if (!$data) {
782 drupal_set_message(t("The specified promotional code doesn't exist."), 'error');
783 drupal_goto('admin/store/settings/promo/'. $promo_id .'/codes');
784 }
785
786 $form['promo_id'] = array(
787 '#type' => 'value',
788 '#value' => $promo_id,
789 );
790
791 $form['promo_code'] = array(
792 '#type' => 'value',
793 '#value' => $code,
794 );
795
796 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'));
797 }
798
799 function uc_promo_delete_code_form_submit($form_id, $form_values) {
800 uc_promo_delete_code($form_values['promo_code']);
801
802 drupal_set_message(t('The promotional code has been deleted.'));
803
804 return 'admin/store/settings/promo/'. $form_values['promo_id'] .'/codes';
805 }
806
807 /**
808 * Loads a promotional code's data from the database.
809 *
810 * @param $code
811 * The promotional code to load.
812 * @return
813 * An array of data for the promotional code; FALSE if the code isn't found.
814 */
815 function uc_promo_load_code($code) {
816 if (empty($code)) {
817 return FALSE;
818 }
819
820 // Check to make sure non-priveleged users don't match directly on the bulk
821 // generated special string.
822 if (substr($code, 0, 7) === 'BULKGEN' && !user_access('administer uc promotions')) {
823 return FALSE;
824 }
825
826 // Look for an exact match in the database.
827 $result = db_query("SELECT * FROM {uc_promo_codes} WHERE UPPER(promo_code) = '%s'", strtoupper($code));
828
829 // Return the data if an exact match was found.
830 if ($data = db_fetch_array($result)) {
831 return $data;
832 }
833
834 // Otherwise look for a bulk generated match in codes for active promotions
835 // that haven't passed their expiration date.
836 $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());
837 while ($data = db_fetch_array($result)) {
838 // Parse the promotion code
839 $parts = explode('-', $data['promo_code']);
840
841 // First check that the code is the correct length.
842 if (strlen($code) != $parts[1]) {
843 continue;
844 }
845
846 // Second check that the sequence part of the code is in the valid range.
847 // The code sequence is encoded in the first 5 characters of the code.
848 $sequence = explode("U", substr(strtoupper($code), 0, 5));
849 if (preg_match("/^[0-9ABCDEF]{1,5}$/", $sequence[0])) {
850 $sequence = hexdec($sequence[0]);
851 if ($sequence >= $parts[2]) {
852 continue;
853 }
854 }
855 else {
856 continue;
857 }
858
859 // Last, check that the MD5 hash part of code is valid.
860 $hash = substr(md5($sequence . $data['promo_id'] . $data['bulkgen_seed']), 0, ($parts[1] - 5));
861
862 if (strcasecmp($hash, substr($code, 5)) == 0) {
863 return $data;
864 }
865 }
866
867 return FALSE;
868 }
869
870 /**
871 * Saves or inserts a new promotional code.
872 *
873 * @param $data
874 * An array of data representing the promotional code.
875 */
876 function uc_promo_save_code($data) {
877 // First try to update an existing promotional code.
878 db_query("UPDATE {uc_promo_codes} SET promo_code_type = %d, max_redemptions = %d, expiration = %d WHERE UPPER(promo_code) = '%s'",
879 $data['promo_code_type'], $data['max_redemptions'], $data['expiration'], strtoupper($data['promo_code']));
880
881 // Otherwise insert a new promotional code.
882 if (db_affected_rows() == 0) {
883 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)",
884 strtoupper($data['promo_code']), $data['promo_id'], $data['promo_code_type'],
885 $data['bulkgen_seed'], 0, $data['max_redemptions'], $data['expiration'],
886 time(), 0);
887 }
888 }
889
890 /**
891 * Delete a promotional code from the database.
892 *
893 * @param $promo_code
894 * The ID of the promotional code to delete.
895 */
896 function uc_promo_delete_code($code) {
897 // Delete any related promotional code redemptions.
898 db_query("DELETE FROM {uc_promo_code_redemptions} WHERE promo_code = '%s'", $code);
899
900 // Delete the code itself.
901 db_query("DELETE FROM {uc_promo_codes} WHERE promo_code = '%s'", $code);
902
903 watchdog('uc_promo', t('Promotional code %code deleted.', array('%code' => $code)));
904 }
905
906 // Displays a table of redemptions for the specified promotional code.
907 function uc_promo_redemptions($promo_id, $code) {
908 // Load the specified promotion; will be FALSE if $code == 0.
909 $data = uc_promo_load_promotion($promo_id);
910
911 if ($data === FALSE) {
912 drupal_set_message(t('Specified promotion does not exist.'), 'error');
913 drupal_goto('admin/store/settings/promo');
914 }
915
916 $output = '<div>'. t('<strong>Promotion:</strong> @name', array('@name' => check_plain($data['promo_name']))) .'<br />';
917
918 // Load the specified promotional code; will be FALSE if $code == 0.
919 $data = uc_promo_load_code($code);
920
921 if ($data === FALSE) {
922 drupal_set_message(t('Specified promotional code does not exist.'), 'error');
923 drupal_goto('admin/store/settings/promo/'. $promo_id .'/codes');
924 }
925
926 $output .= t('<strong>Code:</strong> @code', array('@code' => check_plain($code))) .'</div>';
927
928 $output .= '<div>'. l(t('Return to promotional codes listing.'), 'admin/store/settings/promo/'. $promo_id .'/codes') .'</div>';
929
930 $header = array(
931 array('data' => t('Redeemed on'), 'field' => 'redeemed', 'sort' => 'desc'),
932 array('data' => t('User'), 'field' => 'uid'),
933 );
934
935 if (substr($code, 0, 7) === 'BULKGEN') {
936 $header[] = array('data' => t('Bulk code'), 'field' => 'bulk_code');
937 }
938
939 $rows = array();
940
941 $result = pager_query("SELECT * FROM {uc_promo_code_redemptions} WHERE promo_code = '%s'". tablesort_sql($header), 30, 0, NULL, $code);
942 while ($data = db_fetch_array($result)) {
943 $row = array(
944 array('data' => format_date($data['redeemed'], 'short'), 'style' => 'white-space: nowrap'),
945 array('data' => $data['uid'] > 0 ? l($data['uid'], 'user/'. $data['uid']) : '0', 'width' => substr($code, 0, 7) === 'BULKGEN' ? '33%' : '50%'),
946 );
947
948 if (substr($code, 0, 7) === 'BULKGEN') {
949 $row[] = $data['bulk_code'];
950 }
951
952 $rows[] = $row;
953 }
954
955 if (count($rows) == 0) {
956 $rows[] = array(
957 array('data' => t('This promotional code has not been redeemed.'), 'colspan' => 5),
958 );
959 }
960
961 $output .= theme('table', $header, $rows) . theme('pager');
962
963 return $output;
964 }
965
966 /**
967 * Checks the database to see if a code is valid.
968 *
969 * @param $code
970 * The promotional code to validate.
971 * @return
972 * TRUE or FALSE depending on whether the code is valid or not.
973 */
974 function uc_promo_validate_code($code, $user) {
975 // Return FALSE if the code is not found.
976 if (!$data = uc_promo_load_code($code)) {
977 return FALSE;
978 }
979
980 // Validate the code based on its type.
981 switch ($data['promo_code_type']) {
982 // Unlimited use promotional code.
983 case 1:
984 // Nothing should cause these to fail.
985 break;
986
987 // Specified number of uses.
988 case 2:
989 // Return FALSE if the code has been used up; handle bulk codes in a way
990 // that accounts for redemptions of individual bulk generated codes.
991 if (substr($data['promo_code'], 0, 7) === 'BULKGEN') {
992 $count = db_result(db_query("SELECT COUNT(*) FROM {uc_promo_code_redemptions} WHERE promo_code = '%s' AND bulk_code = '%s'", $data['promo_code'], $code));
993 if ($count >= $data['max_redemptions']) {
994 return FALSE;
995 }
996 }
997 else {
998 if ($data['redemptions'] >= $data['max_redemptions']) {
999 return FALSE;
1000 }
1001 }
1002 break;
1003
1004 // Expiration date.
1005 case 3:
1006 // Return FALSE if the code has expired.
1007 if (time() >= $data['expiration']) {
1008 return FALSE;
1009 }
1010 break;
1011 }
1012
1013 // Return FALSE if the promotion is no longer available or is disabled.
1014 $active = db_result(db_query("SELECT promo_id FROM {uc_promos} WHERE promo_id = %d AND promo_status = 1", $data['promo_id']));
1015 if (!$active) {
1016 return FALSE;
1017 }
1018
1019 $redeem_once = db_result(db_query("SELECT redeem_once FROM {uc_promos} WHERE promo_id = %d", $data['promo_id']));
1020 if ($redeem_once == 1) {
1021 $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));
1022 if ($redeemed > 0) {
1023 return FALSE;
1024 }
1025 }
1026
1027 return TRUE;
1028 }
1029
1030 /**
1031 * Redeems a promotional code for a user by adding a redemption in the database
1032 * and updating the promotional code's data as needed.
1033 *
1034 * @param $code
1035 * The promotional code to redeem.
1036 * @param $user
1037 * The user account for which the code is being redeemed.
1038 * @param $bulk_id
1039 * If the code is a special BULKGEN code, $bulk_code should contain the actual
1040 * unique code used to redeem the promotion.
1041 */
1042 function uc_promo_redeem_code($code, $user, $bulk_code = NULL) {
1043 // Load the appropriate promotion ID.
1044 $promo_id = db_result(db_query("SELECT promo_id FROM {uc_promo_codes} WHERE promo_code = '%s'", $code));
1045
1046 // Insert the redemption into the database.
1047 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)",
1048 db_next_id('{uc_promo_code_redemptions}_redemption_id'), $code, $bulk_code,
1049 $promo_id, $user->uid, time());
1050
1051 // Update the promotion to reflect the redemption.
1052 db_query("UPDATE {uc_promo_codes} SET redemptions = redemptions + 1, last_redemption = %d WHERE promo_code = '%s'", time(), $code);
1053 }
1054
1055 // Downloads a file containing a bulk generated set of promotional codes.
1056 function uc_promo_code_bulkgen($promo_id, $code) {
1057 // Load the data directly from the database.
1058 $result = db_query("SELECT * FROM {uc_promo_codes} WHERE UPPER(promo_code) = '%s'", strtoupper($code));
1059
1060 if ($data = db_fetch_array($result)) {
1061 $filename = $code .'.csv';
1062
1063 header('Content-Type: application/octet-stream');
1064 header('Content-Disposition: attachment; filename="' . $filename . '";');
1065
1066 $parts = explode('-', $code);
1067
1068 $codes = '';
1069 for ($i = 0; $i < $parts[2]; $i++) {
1070 $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";
1071 }
1072
1073 echo rtrim($codes, ",\n");
1074
1075 exit();
1076 }
1077 else {
1078 return drupal_not_found();
1079 }
1080 }
1081
1082 // Workflow-ng action callback and form functions.
1083 function uc_promo_user_add_download($account, $settings) {
1084 _user_table_action('allow', $settings['fid'], $account->uid, NULL);
1085 }
1086
1087 function uc_promo_user_add_download_form($settings = array()) {
1088 if (!is_dir(variable_get('uc_file_base_dir', NULL))) {
1089 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');
1090 }
1091 $form['uc_file_filename'] = array(
1092 '#type' => 'textfield',
1093 '#title' => t('File download'),
1094 '#default_value' => $default_filename,
1095 '#autocomplete_path' => '_autocomplete_file',
1096 '#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))),
1097 );
1098
1099 return $form;
1100 }
1101
1102 function uc_promo_user_add_download_validate($form_id, $form_values) {
1103 if (!db_result(db_query("SELECT fid FROM {uc_files} WHERE filename = '%s'", $form_values['uc_file_filename']))) {
1104 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'])));
1105 }
1106 }
1107
1108 function uc_promo_user_add_download_submit($form_id, $form_values) {
1109 return array('fid' => db_result(db_query("SELECT fid FROM {uc_files} WHERE filename = '%s'", $form_values['uc_file_filename'])));
1110 }
1111

  ViewVC Help
Powered by ViewVC 1.1.2