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

Contents of /contributions/modules/password_reset/password_reset.module

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


Revision 1.2 - (show annotations) (download) (as text)
Sun Oct 28 19:40:32 2007 UTC (2 years, 1 month ago) by karthik
Branch: MAIN
CVS Tags: HEAD
Changes since 1.1: +4 -1 lines
File MIME type: text/x-php
Add delete op to hook_user.
1 <?php
2 // $Id: password_reset.module,v 1.1 2007/10/23 19:57:57 karthik Exp $
3
4 /**
5 * @file
6 * The password_reset module allows for passwords to be reset without involving
7 * e-mail addresses through the use of security questions. This module would
8 * typically be used on sites that do not require users to enter their e-mail
9 * addresses - something that is possible only with a core hack, albeit a minor
10 * one.
11 *
12 * @todo
13 * - Implement flood control.
14 *
15 * @author Karthik Kumar ( http://drupal.org/user/21209 )
16 */
17
18 /**
19 * Implementation of hook_menu().
20 */
21 function password_reset_menu($may_cache) {
22 global $user;
23
24 $items = array();
25
26 if ($may_cache) {
27 $items[] = array(
28 'path' => 'admin/user/password_reset',
29 'title' => t('Password reset'),
30 'description' => t('Configure security questions for the password_reset module.'),
31 'callback' => 'password_reset_admin_questions',
32 'access' => user_access('administer site configuration')
33 );
34 $items[] = array(
35 'path' => 'admin/user/password_reset/list',
36 'title' => t('List'),
37 'callback' => 'password_reset_admin_questions',
38 'access' => user_access('administer site configuration'),
39 'type' => MENU_DEFAULT_LOCAL_TASK
40 );
41 $items[] = array(
42 'path' => 'admin/user/password_reset/add',
43 'title' => t('Add question'),
44 'callback' => 'drupal_get_form',
45 'callback arguments' => array('password_reset_admin_edit'),
46 'access' => user_access('administer site configuration'),
47 'type' => MENU_LOCAL_TASK,
48 'weight' => 1
49 );
50 $items[] = array(
51 'path' => 'admin/user/password_reset/edit',
52 'title' => t('Edit question'),
53 'callback' => 'drupal_get_form',
54 'callback arguments' => array('password_reset_admin_edit'),
55 'access' => user_access('administer site configuration'),
56 'type' => MENU_CALLBACK
57 );
58 $items[] = array(
59 'path' => 'admin/user/password_reset/delete',
60 'title' => t('Delete question'),
61 'callback' => 'drupal_get_form',
62 'callback arguments' => array('password_reset_admin_delete'),
63 'access' => user_access('administer site configuration'),
64 'type' => MENU_CALLBACK
65 );
66 }
67 else {
68 // Override the default password recovery page rather having to perform
69 // a string of form_alters and try overriding all the validation code
70 // of the standard form which require the presence of e-mail form fields and
71 // user fields.
72 $items[] = array(
73 'path' => 'user/password',
74 'title' => t('Reset password'),
75 'callback' => 'drupal_get_form',
76 'callback arguments' => array('password_reset_form'),
77 'access' => !$user->uid,
78 'type' => MENU_LOCAL_TASK
79 );
80 }
81
82 return $items;
83 }
84
85 /**
86 * Menu callback: A multi-step form to validate and reset the password for a
87 * user.
88 */
89 function password_reset_form($form_values = NULL) {
90 $form = array();
91
92 if (!isset($form_values)) {
93 $step = 1;
94 }
95 else {
96 $step = $form_values['step'] + 1;
97 // Forward essential values from step 1 to subsequent steps.
98 $form['operation'] = array('#type' => 'value', '#value' => $form_values['operation']);
99 }
100 $form['step'] = array('#type' => 'hidden', '#value' => $step);
101
102 switch ($step) {
103 case 1:
104 $form['username'] = array(
105 '#type' => 'textfield',
106 '#title' => t('Username'),
107 '#description' => t('Enter the username you use on this site.'),
108 '#required' => TRUE
109 );
110 $form['operation'] = array('#type' => 'submit', '#value' => t('Next'));
111 break;
112 case 2:
113 $uid = password_reset_uid_get($form_values['username']);
114 if ($question = password_reset_user_question_get($uid)) {
115 $form['username'] = array('#type' => 'hidden', '#value' => $form_values['username']);
116 $form['question'] = array('#value' => t('<strong>Security question:</strong> %question', array('%question' => $question->question)));
117 $form['answer'] = array(
118 '#type' => 'textfield',
119 '#title' => t('Answer'),
120 '#description' => t('Enter the answer to the above question.'),
121 '#required' => TRUE,
122 '#weight' => 2
123 );
124 $form['operation'] = array(
125 '#type' => 'submit',
126 '#value' => t('Next'),
127 '#weight' => 3
128 );
129 }
130 break;
131 case 3:
132 // This step should technically be handled in the submit stage of step 2.
133 // However, redirecting the user to the one-time login link was not
134 // working correctly, requiring the user to manually refresh the page.
135
136 // Use userload here as user_pass_reset_url requires the user object.
137 $account = user_load(array('name' => $form_values['username'], 'status' => 1));
138 $reset_url = user_pass_reset_url($account);
139
140 $form['content'] = array('#value' => t('A one-time login link has been created. <a href="!reset-url">Click here</a> to reset your password.', array('!reset-url' => $reset_url)));
141 break;
142 }
143
144 $form['#multistep'] = TRUE;
145 $form['#redirect'] = FALSE;
146
147 return $form;
148 }
149
150 /**
151 * Validate password_reset_form submissions.
152 */
153 function password_reset_form_validate($form_id, $form_values) {
154 switch ($form_values['step']) {
155 case 1:
156 $uid = password_reset_uid_get($form_values['username']);
157 if (!$uid) {
158 form_set_error('username', t('Username not found. Please check and try again.'));
159 }
160 break;
161 case 2:
162 $uid = password_reset_uid_get($form_values['username']);
163 $question = password_reset_user_question_get($uid);
164 if (!$question || trim(strtolower($question->answer)) != trim(strtolower($form_values['answer']))) {
165 form_set_error('answer', t('Answer incorrect. Please check and try again.'));
166 }
167 break;
168 }
169 }
170
171 /**
172 * Implementation of hook_form_alter.
173 */
174 function password_reset_form_alter($form_id, &$form) {
175 switch ($form_id) {
176 case 'user_pass':
177 $form['name']['#title'] = t('Username');
178 $form['submit']['#value'] = t('Next');
179 break;
180 case 'user_edit':
181 // Fall through.
182 case 'user_register':
183 $form['password_reset'] = array(
184 '#type' => 'fieldset',
185 '#title' => t('Security question'),
186 '#description' => t('To reset your password in case you have forgotten it, or for other administrative tasks, a security question will be asked to verify your identity.'),
187 '#tree' => TRUE,
188 '#collapsible' => TRUE
189 );
190 $form['password_reset']['question'] = array(
191 '#type' => 'select',
192 '#title' => t('Question'),
193 '#description' => t('Choose the question that you would like asked.'),
194 '#options' => password_reset_questions_get(),
195 '#required' => TRUE
196 );
197 $form['password_reset']['answer'] = array(
198 '#type' => 'textfield',
199 '#title' => t('Answer'),
200 '#description' => t('Type the answer to the question you have chosen.'),
201 '#maxlength' => 255,
202 '#required' => TRUE
203 );
204
205 // Set defaults only if this is the edit form.
206 if ($form_id == 'user_edit') {
207 $uid = arg(1);
208 $question = password_reset_user_question_get($uid);
209 $form['password_reset']['question']['#default_value'] = $question->qid;
210 $form['password_reset']['answer']['#default_value'] = $question->answer;
211 }
212 break;
213 }
214 }
215
216 /**
217 * Implementation of hook_user().
218 */
219 function password_reset_user($op, &$edit, &$account, $category = NULL) {
220 switch ($op) {
221 case 'update':
222 $qid = $edit['password_reset']['question'];
223 $answer = trim($edit['password_reset']['answer']);
224 unset($edit['password_reset']);
225 // MySQL-only query.
226 db_query("REPLACE INTO {password_reset_users} (uid, qid, answer) VALUES (%d, %d, '%s')", $account->uid, $qid, $answer);
227 break;
228 case 'insert':
229 $qid = $edit['password_reset']['question'];
230 $answer = trim($edit['password_reset']['answer']);
231 unset($edit['password_reset']);
232 db_query("INSERT INTO {password_reset_users} (uid, qid, answer) VALUES (%d, %d, '%s')", $account->uid, $qid, $answer);
233 break;
234 case 'delete':
235 db_query('DELETE FROM {password_reset_users} WHERE uid = %d', $account->uid);
236 break;
237 }
238 }
239
240 /**
241 * Menu callback: List the security questions.
242 */
243 function password_reset_admin_questions() {
244 $result = db_query('SELECT pr.qid, pr.question, COUNT(pru.qid) as total FROM {password_reset} pr LEFT JOIN {password_reset_users} pru ON (pr.qid = pru.qid) GROUP BY pru.qid ORDER BY question');
245
246 $rows = array();
247 while ($question = db_fetch_object($result)) {
248 $rows[] = array($question->question, $question->total, l(t('Edit'), 'admin/user/password_reset/edit/'. $question->qid), l(t('Delete'), 'admin/user/password_reset/delete/'. $question->qid));
249 }
250
251 if (empty($rows)) {
252 $rows[] = array(array('data' => t('No questions found.'), 'colspan' => 4));
253 }
254
255 $header = array(t('Question'), t('Use count'), array('data' => t('Operations'), 'colspan' => 2));
256
257 return theme('table', $header, $rows);
258 }
259
260 /**
261 * Menu callback: Question edit page.
262 */
263 function password_reset_admin_edit($qid = NULL) {
264 $form['question'] = array(
265 '#type' => 'textfield',
266 '#title' => t('Security question'),
267 '#maxlength' => 255,
268 '#default_value' => $question['question'],
269 '#description' => t("Example: 'What is your library card number?' or 'What is your mother's maiden name?'."),
270 '#required' => TRUE
271 );
272
273 $form['submit'] = array('#type' => 'submit', '#value' => t('Add question'));
274
275 if (arg(3) == "edit" && isset($qid)) {
276 $question = db_fetch_array(db_query("SELECT * FROM {password_reset} WHERE qid = %d", $qid));
277 $form['question']['#default_value'] = $question['question'];
278 $form['qid'] = array('#type' => 'value', '#value' => $question['qid']);
279 $form['submit']['#value'] = t('Update question');
280 }
281
282 return $form;
283 }
284
285 /**
286 * Process the password_reset question edit page form submission.
287 */
288 function password_reset_admin_edit_submit($form_id, $form_values) {
289 if (arg(3) == 'add') {
290 db_query("INSERT INTO {password_reset} (question) VALUES ('%s')", $form_values['question']);
291 drupal_set_message(t('Security question %question has been added.', array('%question' => $form_values['question'])));
292 watchdog('security', t('password_reset form: question %question added.', array('%question' => $form_values['question'])), WATCHDOG_NOTICE, l(t('view'), 'admin/user/password_reset'));
293 }
294 else {
295 db_query("UPDATE {password_reset} SET question = '%s' WHERE qid = %d", $form_values['question'], $form_values['qid']);
296 drupal_set_message(t('Security question %question has been updated.', array('%question' => $form_values['question'])));
297 watchdog('security', t('password_reset form: question %question updated.', array('%question' => $form_values['question'])), WATCHDOG_NOTICE, l(t('view'), 'admin/user/password_reset'));
298 }
299
300 return 'admin/user/password_reset';
301 }
302
303 /**
304 * Menu callback: Question delete page.
305 */
306 function password_reset_admin_delete($qid = NULL) {
307 if ($info = db_fetch_object(db_query("SELECT * FROM {password_reset} WHERE qid = %d", $qid))) {
308 $form['question'] = array('#type' => 'value', '#value' => $info->question);
309 $form['qid'] = array('#type' => 'value', '#value' => $info->qid);
310
311 return confirm_form($form, t('Are you sure you want to delete %question?', array('%question' => $info->question)), 'admin/user/password_reset', t('This action cannot be undone.'), t('Delete'), t('Cancel'));
312 }
313 else {
314 drupal_set_message(t('Question not found.'), 'error');
315 drupal_goto('admin/user/password_reset');
316 }
317 }
318
319 /**
320 * Process question delete form submission.
321 */
322 function password_reset_admin_delete_submit($form_id, $form_values) {
323 db_query("DELETE FROM {password_reset} WHERE qid = %d", $form_values['qid']);
324 drupal_set_message(t('Security question %question has been deleted.', array('%question' => $form_values['question'])));
325 watchdog('security', t('password_reset form: Security question %question deleted.', array('%question' => $form_values['question'])), WATCHDOG_NOTICE);
326
327 return 'admin/user/password_reset';
328 }
329
330 /**
331 * Retrieve all available questions.
332 */
333 function password_reset_questions_get() {
334 $result = db_query('SELECT pr.qid, pr.question FROM {password_reset} pr ORDER BY question');
335
336 $questions = array();
337 while ($question = db_fetch_object($result)) {
338 $questions[$question->qid] = $question->question;
339 }
340
341 return $questions;
342 }
343
344 /**
345 * Retrieve question and answer specific to a user.
346 */
347 function password_reset_user_question_get($uid) {
348 $result = db_query('SELECT pr.qid, pr.question, pru.answer FROM {password_reset_users} pru INNER JOIN {password_reset} pr ON (pru.qid = pr.qid) WHERE pru.uid = %d', $uid);
349
350 return $result ? db_fetch_object($result) : FALSE;
351 }
352
353 /**
354 * Retrieve the uid of a user based on the username. This avoids expensive
355 * user_load calls and / or JOINs on each step.
356 */
357 function password_reset_uid_get($username) {
358 $uid = db_result(db_query("SELECT u.uid FROM {users} u WHERE u.status = 1 AND LOWER(u.name) = '%s'", trim(strtolower($username))));
359
360 return $uid;
361 }

  ViewVC Help
Powered by ViewVC 1.1.2