? fpc_6_with_tests.patch
Index: force_password_change.info
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/force_password_change/force_password_change.info,v
retrieving revision 1.1
diff -u -p -r1.1 force_password_change.info
--- force_password_change.info	23 Jan 2010 07:00:11 -0000	1.1
+++ force_password_change.info	24 Mar 2010 15:36:26 -0000
@@ -1,4 +1,4 @@
-; $Id: force_password_change.info,v 1.1 2010/01/23 07:00:11 hakulicious Exp $
+; $Id$
 name = Force Password Change
-description = Creates the option to force users to create a new password, either by role, or by all users
-core = 6.x
\ No newline at end of file
+description = Creates the option to force users to create a new password by role or by user.
+core = 6.x
Index: force_password_change.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/force_password_change/force_password_change.install,v
retrieving revision 1.1
diff -u -p -r1.1 force_password_change.install
--- force_password_change.install	23 Jan 2010 07:00:11 -0000	1.1
+++ force_password_change.install	24 Mar 2010 15:36:26 -0000
@@ -1,44 +1,44 @@
 <?php
+// $Id$
 
-function force_password_change_install()
-{
-	$ret = array();
-	db_add_field
-	(
-		$ret,
-		'users',
-		'force_password_change',
-		array
-		(
-			'type' => 'int',
-			'length' => 1,
-			'default' => 0,
-		),
-		array
-		(
-			'indexes' => array
-			(
-				'user_force_password' => array('uid', 'force_password_change'),
-			),
-		)
-	);
-	return $ret;
+/**
+ * @file
+ * Force password change module install file.
+ *
+ * @sponsored by Classic Graphics.
+ */
+
+/**
+ * Implementation of hook_schema().
+ */
+function force_password_change_schema() {
+  $schema['force_password_change'] = array(
+    'description' => t(''),
+    'fields' => array(
+      'uid' => array('type' => 'int', 'not null' => TRUE),
+      'force_change' => array('type' => 'int', 'default' => 0),
+    ),
+    'indexes' => array('uid' => array('uid')),
+  );
+  return $schema;
+}
+
+/**
+ * Implementation of hook_install().
+ */
+function force_password_change_install() {
+  drupal_install_schema('force_password_change');
+  // We want to avoid an avalanche of inserts
+  // so instead we grab the current uid list at install
+  // then update when users are added/deleted.
+  // Ignore user 0 since anon. user can't log in
+  db_query('INSERT INTO {force_password_change} (uid) SELECT DISTINCT uid FROM {users} WHERE uid > 0;
+');
 }
 
-function force_password_change_uninstall()
-{
-	$ret = array();
-	db_drop_index
-	(
-		$ret,
-		'users',
-		'user_force_password'
-	);
-	db_drop_field
-	(
-		$ret,
-		'users',
-		'force_password_change'
-	);
-	return $ret;
-}
\ No newline at end of file
+/**
+ * Implementation of hook_uninstall().
+ */
+function force_password_change_uninstall() {
+  drupal_uninstall_schema('force_password_change');
+}
Index: force_password_change.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/force_password_change/force_password_change.module,v
retrieving revision 1.4
diff -u -p -r1.4 force_password_change.module
--- force_password_change.module	3 Feb 2010 02:19:23 -0000	1.4
+++ force_password_change.module	24 Mar 2010 15:36:27 -0000
@@ -1,163 +1,196 @@
 <?php
+// $Id$
 
-function force_password_change_perm()
-{
-	return array
-	(
-		'Administer force password change',
-	);
-}
-
-function force_password_change_menu()
-{
-	$menu['admin/user/force_password_change'] = array
-	(
-		'title' => 'Force password change',
-		'page callback' => 'drupal_get_form',
-		'page arguments' => array('force_password_change_settings'),
-		'access arguments' => array('Administer force password change'),
-	);
-	return $menu;
-}
-
-function force_password_change_init()
-{
-	global $user;
-	$change_password_url = preg_replace('/!uid/', $user->uid, variable_get('change_password_url', 'user/!uid/edit'));
-	if($user->force_password_change && $_GET['q'] != $change_password_url)
-	{
-		drupal_set_message(t('An administrator has required that you change your password. You must change your password to proceed on the site.'), 'error');
-		drupal_goto($change_password_url, 'destination=' . $_GET['q']);
-	}
-}
-
-function force_password_change_user($op, &$edit, &$account, $category = NULL)
-{
-	if($op == 'validate' && $account->force_password_change)
-	{
-		if($edit['pass'] == '')
-		{
-			form_set_error('password', t('You must choose a new password'));
-		}
-		else
-		{
-			$password_same = db_result
-			(
-				db_query
-				(
-					'SELECT 1 ' .
-					'FROM {users} ' .
-					'WHERE uid = %d AND pass = "%s"',
-					$account->uid,
-					md5($edit['pass'])
-				)
-			);
-			if($password_same)
-			{
-				form_set_error('password', t('You cannot use your current password. Please choose something different.'));
-			}
-		}
-	}
-	if($op == 'update' && $account->force_password_change)
-	{
-		db_query
-		(
-			'UPDATE {users} SET force_password_change = 0 WHERE uid = %d',
-			$account->uid
-		);
-	}
-	if($op == 'insert' && variable_get('first_time_login_password_change', 0))
-	{
-		db_query('UPDATE {users} SET force_password_change = 1 WHERE uid = %d', $account->uid);
-	}
-}
-
-function force_password_change_settings($form_state)
-{
-	$form['first_time_login_password_change'] = array
-	(
-		'#type' => 'checkbox',
-		'#title' => t('Force password change on first-time login'),
-		'#default_value' => variable_get('first_time_login_password_change', 0),
-	);
-	$roles = user_roles(TRUE);
-	$form['roles'] = array
-	(
-		'#type' => 'checkboxes',
-		'#options' => $roles,
-		'#title' => t('Force users in the following roles to change their password.'),
-		'#description' => t('Users who are not signed in will be required to change their password immediately upon sign in. Users who are currently signed in will be required to change their password upon their next page click, but after changing their password will be redirected back to the page they were attempting to access.'),
-	);
-	$form['change_password_url'] = array
-	(
-		'#type' => 'textfield',
-		'#title' => t('Path to password change URL'),
-		'#description' => t('Only change this if you have implemented a module that changes the user password URL from user/[UID]/edit to something else. Use !uid in place of the user id') . '<br />' . t('WARNING: if you set this URL to an invalid URL, you could break your site, so backup your files first, and double check the path before setting it.'),
-		'#default_value' => variable_get('change_password_url', 'user/!uid/edit'),
-	);
-
-	$form['submit'] = array
-	(
-		'#type' => 'submit',
-		'#value' => t('Submit'),
-	);
-	return $form;
-}
-
-function force_password_change_settings_submit($form, &$form_state)
-{
-	$selected_roles = array();
-	variable_set('change_password_url', $form_state['values']['change_password_url']);
-	variable_set('first_time_login_password_change', $form_state['values']['first_time_login_password_change']);
-	($form_state['values']['first_time_login_password_change']) ? drupal_set_message(t('New users will be required to change their password on first-time login')) : drupal_set_message(t('New users will not be required to change their pasword on first-time login'));
-	foreach($form_state['values']['roles'] as $role)
-	{
-		if($role > 2)
-		{
-			$uids = array();
-			$db_uids = db_query
-			(
-				'SELECT uid ' .
-				'FROM {users_roles} ' .
-				'WHERE rid = %d',
-				$role
-			);
-			while($uid = db_fetch_array($db_uids))
-			{
-				$uids[] = $uid['uid'];
-			}
-			db_query
-			(
-				'UPDATE {users} ' .
-				'SET force_password_change = 1 ' .
-				'WHERE uid IN (%s)',
-				implode(', ', $uids)
-			);
-			$selected_roles[] = $role;
-		}
-		elseif($role == 2)
-		{
-			db_query
-			(
-				'UPDATE {users} ' .
-				'SET force_password_change = 1 '
-			);
-			$selected_roles[] = $role;
-		}
-	}
-	if(count($selected_roles))
-	{
-		$roles = user_roles(TRUE);
-		$list = '<ul>';
-		foreach($selected_roles as $sr)
-		{
-			$list .= '<li>' . $roles[$sr] . '</li>';
-		}
-		$list .= '</ul>';
-		drupal_set_message(t('Users in the following roles will be required to immediately change their password: !list', array('!list' => $list)), 'status');
-	}
-	else
-	{
-		drupal_set_message(t('No roles were selected.'));
-	}
-}
\ No newline at end of file
+/**
+ * @file
+ * Force users to change their password.
+ *
+ * @Sponsored by Classic Graphics.
+ */
+
+/**
+ * Implementation of hook_init().
+ *
+ * Dumps user on their account edit page until they change their password.
+ */
+function force_password_change_init() {
+  global $user;
+  // Timing issues require reloading the user object
+  // to get the force_password_change property set.
+  $user = user_load($user->uid);
+  $change_password_url = str_replace('!uid', $user->uid, variable_get('change_password_url', 'user/!uid/edit'));
+  if ($user->force_password_change && $_GET['q'] != $change_password_url) {
+    // let users log out
+    if (arg(0) != 'logout') {
+      drupal_set_message(t('An administrator has required that you change your password. You must change your password to proceed on the site.'), 'error', FALSE);
+      drupal_goto($change_password_url, 'destination=' . $_GET['q']);
+    }
+  }
+}
+
+/**
+ * Implementation of hook_perm().
+ */
+function force_password_change_perm() {
+  return array('administer force password change');
+}
+
+/**
+ * Implementation of hook_menu().
+ */
+function force_password_change_menu() {
+  $menu['admin/user/force_password_change'] = array(
+    'title' => 'Force password change',
+    'description' => 'Force users to change their password',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('force_password_change_settings'),
+    'access arguments' => array('administer force password change'),
+  );
+  return $menu;
+}
+
+/**
+ * Implementation of hook_user().
+ */
+function force_password_change_user($op, &$edit, &$account, $category = NULL) {
+  switch ($op) {
+    case 'load':
+      $account->force_password_change = db_result(db_query('SELECT force_change FROM {force_password_change} WHERE uid=%d', $account->uid));
+      break;
+    case 'insert':
+      $force = variable_get('first_time_login_password_change', 0);
+      db_query('INSERT INTO {force_password_change} VALUES(%d, %d)', $account->uid, $force);
+      break;
+    case 'delete':
+      db_query('DELETE FROM {force_password_change} WHERE uid=%d', $account->uid);
+      break;
+    case 'validate':
+      global $user;
+      if (isset($account->uid) && $account->force_password_change == 1) {
+        // we want admins to be able to edit accounts without having to
+        // reset passwords.
+        if ($edit['pass'] == '' && $user->name == $account->name) {
+          form_set_error('password', t('You must choose a new password.'));
+        }
+        else {
+          $password_same = db_result(db_query('SELECT 1 FROM {users} WHERE uid = %d AND pass ="%s"', $account->uid, md5($edit['pass'])));
+          if ($password_same) {
+            form_set_error('password', t('You cannot use your current password. Please choose something different.'));
+          }
+        }
+      }
+      break;
+    case 'update':
+      if ($op == 'update' && $account->force_password_change) {
+        db_query('UPDATE {force_password_change} SET force_change = 0 WHERE uid = %d', $account->uid);
+      }
+      break;
+  }
+}
+
+/**
+ * Admin settings form.
+ */
+function force_password_change_settings() {
+  $form['first_time_login_password_change'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Force password change on first-time login'),
+    '#default_value' => variable_get('first_time_login_password_change', 0),
+  );
+  $roles = user_roles(TRUE);
+  $form['roles'] = array(
+    '#type' => 'checkboxes',
+    '#options' => $roles,
+    '#title' => t('Force users in the following roles to change their password'),
+    '#description' => t('Users who are not signed in will be required to change their password immediately upon sign in. Users who are currently signed in will be required to change their password upon their next page click, but after changing their password will be redirected back to the page they were attempting to access.'),
+  );
+  $form['change_password_url'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Path to password change URL'),
+    '#description' => t('Only change this if you have implemented a module that changes the user password URL from user/[UID]/edit to something else. Use !uid in place of the user id') . '<br />' . t('WARNING: if you set this URL to an invalid URL, you could break your site, so backup your files first, and double check the path before setting it.'),
+    '#default_value' => 'user/!uid/edit',
+  );
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Submit'),
+  );
+  return $form;
+}
+
+/**
+ * Submit handler for the admin settings form.
+ */
+function force_password_change_settings_submit($form, &$form_state) {
+  $selected_roles = array();
+  variable_set('change_password_url', $form_state['values']['change_password_url']);
+  variable_set('first_time_login_password_change', $form_state['values']['first_time_login_password_change']);
+  ($form_state['values']['first_time_login_password_change']) ? drupal_set_message(t('New users will be required to change their password on first-time login.')) : drupal_set_message(t('New users will not be required to change their pasword on first-time login.'));
+  foreach ($form_state['values']['roles'] as $role) {
+    if ($role > 2) {
+      $uids = array();
+      $db_uids = db_query('SELECT uid FROM {users_roles} WHERE rid = %d', $role);
+      while ($uid = db_fetch_array($db_uids)) {
+        $uids[] = $uid['uid'];
+      }
+      db_query("UPDATE {force_password_change} SET force_change = 1 WHERE uid IN ('%s')", implode(', ', $uids));
+      $selected_roles[] = $role;
+    }
+    elseif ($role == 2) {
+      db_query('UPDATE {force_password_change} SET force_change = 1');
+      $selected_roles[] = $role;
+    }
+  }
+  if (count($selected_roles)) {
+    $roles = user_roles(TRUE);
+    $list = array();
+    foreach ($selected_roles as $sr) {
+      $list[] = $roles[$sr];
+    }
+    $list = implode(', ', $list);
+    drupal_set_message(t('Users in the following roles will be required to immediately change their password: %list', array('%list' => $list)), 'status');
+  }
+  else {
+    drupal_set_message(t('No roles were selected.'));
+  }
+}
+
+/**
+ * Implementation of hook_form_alter().
+ */
+function force_password_change_form_alter(&$form, $form_state, $form_id) {
+  if (($form_id == 'user_register' || $form_id == 'user_profile_form') && user_access('administer force password change')) {
+    $form['password'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Password settings'),
+    );
+    $form['password']['force_password_change'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Force password change on next login'),
+    );
+    $form['#submit'][] = 'force_password_change_user_submit';
+  }
+}
+
+/**
+ * Submit handler for user profile checkbox
+ */
+function force_password_change_user_submit($form, $form_state) {
+  global $user;
+  $force_change = $form_state['values']['force_password_change'];
+  $name = $form_state['values']['name'];
+  $uid = $form['#uid'];
+  if ($force_change && $user->name != $name) {
+    drupal_set_message(t('!user will be required to change their password the next time they log in.', array('!user' => $name)));
+  }
+  if (is_numeric($uid) && $force_change == 1) {
+    // existing account
+    db_query('UPDATE {force_password_change} SET force_change=%d WHERE uid=%d', $force_change, $form['#uid']);
+  }
+  else {
+    // new account
+    if (variable_get('first_time_login_password_change', 0)) {
+      $force_change = 1;
+    }
+    db_query("UPDATE {force_password_change} AS f INNER JOIN {users} u ON u.uid = f.uid SET force_change=%d WHERE name='%s'", $force_change, $name);
+  }
+}
Index: force_password_change.test
===================================================================
RCS file: force_password_change.test
diff -N force_password_change.test
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ force_password_change.test	24 Mar 2010 15:36:27 -0000
@@ -0,0 +1,191 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Test suite for Force Password Change module.
+ */
+
+class ForcepasswordchangeTestCase extends DrupalWebTestCase {
+  protected $admin_user;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Force password reset test',
+      'description' => 'Tests forced password reset for single user, role and all new users.',
+      'group' => 'Force Password Change',
+    );
+  }
+
+  public function setUp() {
+    parent::setUp('force_password_change');
+  }
+
+  // test permissions
+  public function testPerms() {
+    // no perms
+    $user = $this->drupalCreateUser();
+    $this->drupalLogin($user);
+    $this->drupalGet('admin/user/force_password_change');
+    $this->assertResponse('403', t('Acces should be denied'));
+    $this->drupalLogout();
+
+    // with perms
+    $user = $this->drupalCreateUser(array('administer force password change'));
+    $this->drupalLogin($user);
+    $this->drupalGet('admin/user/force_password_change');
+    $this->assertResponse('200', t('Access should be granted'));
+    $this->drupalLogout();
+  }
+
+  // test form elements
+  public function testForms() {
+    // test admin form
+    $user = $this->drupalCreateUser(array('administer force password change', 'administer users'));
+    $this->drupalLogin($user);
+    $this->drupalGet('admin/user/force_password_change');
+    $this->assertFieldByName('first_time_login_password_change', '', t('Found first time login change checkbox.'));
+    $this->assertFieldByName('roles[2]', '', t('Found roles checkboxes.'));
+    $this->assertFieldByName('change_password_url', $value='user/!uid/edit', t('Found change password url textfield.'));
+    $this->assertFieldById('edit-submit', '', t('Found submit button'));
+
+    // test user edit form with perms
+    $this->drupalGet("user/$user->uid/edit");
+    $this->assertFieldByName('force_password_change', '', 'Force password change checkbox is visible to admin.');
+    $this->drupalLogout();
+
+    // test user edit form without perms
+    $user = $this->drupalCreateUser();
+    $this->drupalLogin($user);
+    $this->drupalGet("user/$user->uid/edit");
+    $this->assertNoFieldByName('force_password_change', '', 'Force password change checkbox is hidden for normal users.');
+    $this->drupalLogout();
+  }
+
+  // test single user password change
+  public function testSingleUser() {
+    $admin = $this->drupalCreateUser(array('administer force password change', 'administer users'));
+    $user = $this->drupalCreateUser();
+    $this->drupalLogin($admin);
+    $edit = array(
+      'force_password_change' => 1,
+    );
+    $this->drupalPost("user/$user->uid/edit", $edit, t('Save'));
+    $this->assertRaw(t('!user will be required to change their password the next time they log in.', array('!user' => $user->name)), t('Settings successful.'));
+    $force_change = db_result(db_query('SELECT force_change FROM {force_password_change} WHERE uid=%d', $user->uid));
+    $this->assertTRUE($force_change == 1, t('Force change flag set to %d for %s', array('%d' => $force_change, '%s' => $user->name)));
+
+    //confirm admin can edit user acct without changing password
+    $edit = array(
+      'name' => $user->name,
+      'mail' => $user->mail,
+      'force_password_change' => 1,
+    );
+    $this->drupalPost("user/$user->uid/edit", $edit, t('Save'));
+    $force_change = db_result(db_query('SELECT force_change FROM {force_password_change} WHERE uid=%d', $user->uid));
+    $this->assertTrue($force_change == 1, t('User force change flag set in database:' . $force_change));
+    $this->assertNoRaw(t('An administrator has required that you change your password. You must change your password to proceed on the site.'), t('Admin can edit user account without changing password.'));
+    $this->drupalLogout();
+
+    // verify user is forced to change password
+    $this->drupalLogin($user);
+    $this->assertFieldByName('mail', '', t('User redirected correctly.')); //marginal
+    $this->assertRaw(t('An administrator has required that you change your password. You must change your password to proceed on the site.'), t('User presented with error instructing them to change their password'));
+
+    // attempt to submit form with null password fields
+    $edit = array(
+      'mail' => $user->mail,
+    );
+    $this->drupalPost('user/$user->uid/edit', $edit, t('Save'));
+    $this->assertRaw(t('You must choose a new password.'), t('Null password not permitted.'));
+
+    // attempt to submit form with current password
+    $edit = array(
+      'mail' => $user->mail,
+      'pass[pass1]' => $user->pass_raw,
+      'pass[pass2]' => $user->pass_raw,
+    );
+    $this->drupalPost('user/$user->uid/edit', $edit, t('Save'));
+    $this->assertRaw(t('You cannot use your current password. Please choose something different.'), t('Old password not permitted.'));
+
+    // attempt to change password
+    $edit = array(
+      'mail' => $user->mail,
+      'pass[pass1]' => 'random_string',
+      'pass[pass2]' => 'random_string',
+    );
+    $this->drupalPost('user/$user->uid/edit', $edit, t('Save'));
+    $this->assertRaw(t('The changes have been saved.'), t('Password change successful.'));
+
+    // verify user not prompted to change password a 2nd time
+    $this->drupalGet('node');
+    $this->assertNoFieldByName('mail', '', t('User not forced to change password a 2nd time.'));
+    $this->drupalLogout();
+  }
+
+  // test role-based password change
+  public function testRoleChange() {
+    $admin = $this->drupalCreateUser(array('administer users', 'administer force password change'));
+    $user1 = $this->drupalCreateUser();
+    $user2 = $this->drupalCreateUser();
+    $this->drupalLogin($admin);
+    $edit = array(
+      'roles[2]' => 2,
+    );
+    $this->drupalPost('admin/user/force_password_change', $edit, t('Submit'));
+    $this->assertText(t('Users in the following roles will be required to immediately change their password: authenticated user'), t('Authenticated users role selected.'));
+    $this->assertRaw(t('An administrator has required that you change your password. You must change your password to proceed on the site.'), t('Admin forced to change password due to role selection.'));
+    $this->drupalLogout();
+
+    //test individual users
+    $this->drupalLogin($user1);
+    $this->assertRaw(t('An administrator has required that you change your password. You must change your password to proceed on the site.'), t('First test user forced to change password.'));
+    $this->drupalLogout();
+
+    //test 2nd user
+    $this->drupalLogin($user2);
+    $this->assertRaw(t('An administrator has required that you change your password. You must change your password to proceed on the site.'), t('Second test user forced to change password.'));
+    $this->drupalLogout();
+
+  }
+
+  //test new user change
+  public function testNewUserChange() {
+    $admin = $this->drupalCreateUser(array('administer users', 'administer force password change'));
+    $this->drupalLogin($admin);
+    $edit = array(
+      'first_time_login_password_change' => 1,
+    );
+    $this->drupalPost('admin/user/force_password_change', $edit, t('Submit'));
+    $this->assertRaw(t('New users will be required to change their password on first-time login.'), t('New users required to change password on 1st login.'));
+    $this->drupalLogout();
+    $user = $this->drupalCreateUser();
+    $this->drupalLogin($user);
+    $this->drupalGet('node');
+    $this->assertRaw(t('An administrator has required that you change your password. You must change your password to proceed on the site.'), t('New user forced to change password.'));
+  }
+
+  //test admin forcing their own account to reset
+  public function testSelfChange() {
+    $admin = $this->drupalCreateUser(array('administer users', 'administer force password change'));
+    $this->drupalLogin($admin);
+    $this->assertNoRaw(t('An administrator has required that you change your password. You must change your password to proceed on the site.'), t('Admin should not be prompted to change password yet.'));
+    $edit = array(
+      'force_password_change' => 1,
+    );
+    $this->drupalPost("user/$admin->uid/edit", $edit, t('Save'));
+    $this->assertRaw(t('The changes have been saved.'), t('Admin has queued account for password change.'));
+    $this->assertNoRaw(t('An administrator has required that you change your pasword. You must change your password to proceed on the site.'), t('Admin not initially prompted to change password.'));
+    $this->drupalGet('node');
+    $this->assertRaw(t('An administrator has required that you change your password. You must change your password to proceed on the site.'), t('Admin forced to change password once they try to leave account page.'));
+    $edit = array(
+      'mail' => $admin->mail,
+      'pass[pass1]' => 'fpcR@nd0m!',
+      'pass[pass2]' => 'fpcR@nd0m!',
+    );
+    $this->drupalPost("user/$admin->uid/edit", $edit, t('Save'));
+    $this->assertRaw(t('The changes have been saved.'), t('Admin changed password.'));
+    $this->drupalGet('node');
+    $this->assertNoRaw(t('An administrator has required that you change your password. You must change your password to proceed on the site.'), t('Not prompted to change password a 2nd time'));
+  }
+}
