This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /** * HELP! */ function rolecode_help($section) { switch ($section) { case 'admin/help#rolecode': return t('

This module allows you to generate random keys which can be "redeemed" by users (My account -> Upgrade) for assignment to a particular role.

Generate the codes in the user management section.

'); } } /** * Permissions set in this module. */ function rolecode_perm() { return array('administer codes', 'enter code'); } /** * Cron job run by this module. */ function rolecode_cron() { // clean up expired codes $now = time(); db_query("DELETE FROM {rolecode_codes} WHERE user_id IS NULL AND expires < %d", $now); } /** * Menu items created by this module. */ function rolecode_menu($may_cache) { $items = array(); global $user; $items[] = array( 'path' => 'admin/user/rolecode', 'title' => t('Role codes'), 'callback' => 'drupal_get_form', 'callback arguments' => 'rolecode_admin_generate_form', 'access' => user_access('administer role codes'), 'description' => t('Users who enter special codes are automatically assigned specific roles.') ); $items[] = array( 'path' => 'admin/user/rolecode/generate', 'title' => t('Generate'), 'description' => t('Generate codes'), 'type' => MENU_DEFAULT_LOCAL_TASK ); $items[] = array( 'path' => 'admin/user/rolecode/view', 'title' => t('Codes'), 'callback' => 'rolecode_admin_view', 'type' => MENU_LOCAL_TASK ); if ($user->uid) { $items[] = array( 'path' => 'user/' . $user->uid . '/upgrade-account', 'title' => t('Upgrade'), 'description' => t('Enter an upgrade code'), 'callback' => 'drupal_get_form', 'callback arguments' => 'rolecode_enter_code_form', 'access' => user_access('enter code'), 'type' => MENU_LOCAL_TASK, ); } return $items; } /** * The form users enter codes in. */ function rolecode_enter_code_form() { $form = array(); $form['rolecode_code'] = array( '#type' => 'textfield', '#title' => t('Enter code'), '#required' => TRUE, '#default_value' => '', '#description' => t('Enter an upgrade code'), ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Go'), ); return $form; } /** * Action to perform when the enter-code form is submitted. */ function rolecode_enter_code_form_submit($form_id, $form_values) { global $user; $code = $form_values['rolecode_code']; $code = _rolecode_clean_code($code); $res = db_query("SELECT role_id, user_id, expires FROM {rolecode_codes} WHERE code='%s'", $code); if ($row = db_fetch_array($res)) { if ($row['expires'] < time()) { $error = t('Expired code: ') . $code; } elseif ($row['user_id'] !== NULL) { $error = t('Recycled code: ') . $code; } else { _rolecode_set_role($row['role_id']); db_query("UPDATE {rolecode_codes} SET user_id=%d WHERE code='%s'", $user->uid, $code); drupal_set_message(t('Code accepted.')); return; } } else { $error = t('Nonexistent code: ') . $code; } watchdog('rolecode', $error); drupal_set_message(t('Error! Invalid code.'), 'error'); } /** * Set a user role. */ function _rolecode_set_role($rid) { global $user; if (!isset($user->roles[$rid])) db_query("INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)", $user->uid, $rid); } /** * Clean code input and fix common errors. * see also */ function _rolecode_clean_code($code) { $code = strtoupper($code); $code = str_replace(array('I', 'L'), '1', $code); $code = str_replace('O', '0', $code); $code = preg_replace('#[^A-Z0-9-]+#', '', $code); return $code; } /** * The "view codes" form. */ function rolecode_admin_view() { $header = array( array( 'data' => t('Code'), 'field' => 'code', 'sort' => 'desc' ), array( 'data' => t('Role'), 'field' => 'role_id', 'sort' => 'desc' ), array( 'data' => t('Used by'), 'field' => 'user_id', 'sort' => 'desc' ), array( 'data' => t('Expires'), 'field' => 'expires', 'sort' => 'desc' ) ); $query = "SELECT code, role_id, user_id, expires FROM {rolecode_codes}"; $query .= tablesort_sql($header); $res = pager_query($query, 20); $roles = user_roles(true); $rows = array(); while ($dbrow = db_fetch_array($res)) { $code = $dbrow['code']; $user = user_load(array("uid" => $dbrow['user_id'])); $user = $user->uid ? theme('username', $user) : '-'; $expires = date('Y-m-d', $dbrow['expires']); $role = $roles[$dbrow['role_id']]; $rows[] = array($code, $role, $user, $expires); } $output = ''; if (count($rows)) { $output .= theme('table', $header, $rows); } else { $output .= '

' . t('No codes have been used.') . '

'; } if ($pager = theme('pager', NULL, 50, 0, $request)) { $output .= $pager; } return $output; } /** * The "generate new codes" form. */ function rolecode_admin_generate_form($form_values=NULL) { $form = array(); $form['#redirect'] = false; $form['#multistep'] = true; if (!$form_values) { $form['role'] = array( '#type' => 'select', '#title' => t('Role'), '#options' => user_roles(true), '#description' => t('Codes assign this role'), ); $form['count'] = array( '#type' => 'textfield', '#default_value' => '50', '#title' => t('Quantity'), '#description' => t('Generate this many codes'), ); $form['expires'] = array( '#type' => 'textfield', '#default_value' => '7', '#title' => t('Expiry'), '#description' => t('Unused codes expire after this many days') ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Generate'), ); } else { $codes = _rolecode_generate_codes($form_values['role'], $form_values['count'], $form_values['expires']); $form['results'] = array( '#type' => 'item', '#title' => 'New codes', '#value' => '' ); } return $form; } /** * Generate a single code. Inefficient, but at least * the codes are random enough and very readable. */ function _rolecode_generate_code($cid) { //http://www.crockford.com/wrmg/base32.html $map = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; $code = array($cid); for ($i=0; $i<5; ++$i) { $part = ''; for ($j=0; $j<3; ++$j) { $k = rand(0, 31); $part .= $map[$k]; } $code[] = $part; } return join('-', $code); } /** * Generate many codes. */ function _rolecode_generate_codes($rid, $count, $expires) { $count = (int) $count; $rid = (int) $rid; $expires = (int) $expires; if (!$expires) $expires = "NULL"; else $expires = time() + ($expires * 24 * 60 * 60); // Maintain a ghetto auto-increment ID here to save // on queries. COUNT(*) should be optimized anyway. It's not really // necessary, but we prefix the ID to each code to ensure uniqueness. $res = db_query("SELECT COUNT(*) AS count FROM {rolecode_codes}"); if ($row = db_fetch_array($res)) $last = (int) $row['count']; else $last = 0; $upto = $last + $count; $query = "INSERT INTO {rolecode_codes} (code, role_id, user_id, expires) VALUES "; $q = array(); $qv = array(); $codes = array(); for ($i=$last+1; $i<=$upto; ++$i) { $code = _rolecode_generate_code($i); $codes[] = $code; $q[] = "('%s', %d, NULL, %d)"; array_push($qv, $code, $rid, $expires); } $query .= implode(", ", $q); db_query($query, $qv); return $codes; }