From 96a4e911554b05c411f4870551b388c50347db06 Mon Sep 17 00:00:00 2001
From: Sheldon Chang <sheldon.chang@hyperlinked.com>
Date: Mon, 12 May 2014 08:50:27 -0700
Subject: [PATCH 1/2] Issues 1716150,182217,1979620,2178513,2265509 and
 addition of a module configure page

---
 access_code.module | 253 ++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 233 insertions(+), 20 deletions(-)

diff --git a/access_code.module b/access_code.module
index dbb8b53..6f050a5 100644
--- a/access_code.module
+++ b/access_code.module
@@ -30,6 +30,14 @@ function access_code_menu() {
     'file' => 'access_code.pages.inc',
   );
 
+  $items['admin/config/access_code'] = array(
+  		'title' => 'Access Code Settings',
+  		'description' => 'Configure settings for access codes.',
+  		'page callback' => 'drupal_get_form',
+  		'page arguments' => array('access_code_admin_settings'),
+  		'access arguments' => array('administer users'),
+  		'type' => MENU_NORMAL_ITEM,
+  );  
 
   return $items;
 }
@@ -95,17 +103,44 @@ function access_code_add_form_fields(&$form) {
     '#default_value' => (($register || empty($form['#user']->access_code)) ? '' : $form['#user']->access_code),
     '#access' => $access,
     '#weight' => 10,
+  	'#prefix' => '<div id="access_code-div">',
+  	'#suffix' => '</div>'
   );
 
+  $form['account']['access_code_settings']['generate_code'] = array(
+  		'#type' => 'button',
+  		'#description' => t("Automatically generates a unique access code."),
+  		'#value' => t('Generate Access Code'),
+  		'#access' => $access,  		
+  		'#weight' => 11,
+  		'#limit_validation_errors' => array(),
+  		'#ajax' => array(
+  						'callback' => 'access_code_form_code_generate',
+  						'wrapper' => 'access_code-div',
+  					  'method'  => 'replace'
+  				),  		
+  );
+  
+  if ($register || empty($form['#user']->access_code_expiration)) {
+  	$expiration_default = variable_get('expiration_default','1 month');
+  	
+  	$date = new DateTime('now');  	
+  	$date->modify('+'.$expiration_default);
+  	$expires = array('year' => $date->format('Y'), 'month' => $date->format('n'), 'day' => $date->format('j'));
+  }
+  else {
+  	$expires = array(
+  		'year' => date('Y', $form['#user']->access_code_expiration),
+  		'month' => date('n', $form['#user']->access_code_expiration),
+  		'day' => date('j', $form['#user']->access_code_expiration),
+  	); 
+  }
+  
   $form['account']['access_code_settings']['access_code_expiration'] = array(
     '#type' => 'date',
     '#title' => t('Expiration'),
     '#description' => t('Date after which the code cannot be used.'),
-    '#default_value' => ($register || empty($form['#user']->access_code_expiration) ? NULL : array(
-      'year' => date('Y', $form['#user']->access_code_expiration),
-      'month' => date('n', $form['#user']->access_code_expiration),
-      'day' => date('j', $form['#user']->access_code_expiration),
-    )),
+    '#default_value' => $expires,
     '#access' => ($register || ($user->uid == $form['#user']->uid && user_access('change own access code')) || user_access('administer users')),
     '#access' => $access,
     '#weight' => 20,
@@ -116,9 +151,8 @@ function access_code_add_form_fields(&$form) {
       '#markup' => t('Current access link:') . ' ' . l(url('ac/' . $form['#user']->access_code, array('absolute' => TRUE)), 'ac/' . $form['#user']->access_code, array('absolute' => TRUE)),
       '#weight' => 30,
     );
-
   }
-
+  
   $form['#validate'][] = 'access_code_user_profile_form_validate';
 }
 
@@ -126,20 +160,35 @@ function access_code_add_form_fields(&$form) {
  * Validation function for the user profile form.
  */
 function access_code_user_profile_form_validate($form, &$form_state) {
-  $invalid_chars = preg_replace('@[a-zA-Z0-9]@', '', $form_state['values']['access_code']);
+
+	$invalid_chars = preg_replace('@[a-zA-Z0-9]@', '', $form_state['values']['access_code']);
   if ($invalid_chars) {
-    form_set_error('user_access_code', t('The access code can contain alphanumerical characters only.'));
+    form_set_error('access_code', t('The access code can contain alphanumerical characters only.'));
   }
 
   if (drupal_strlen($form_state['values']['access_code']) > 20) {
-    form_set_error('user_access_code', t('The access code can not be longer than 20 characters.'));
+    form_set_error('access_code', t('The access code can not be longer than 20 characters.'));
   }
 
   if (!empty($form_state['values']['access_code']) && drupal_strlen($form_state['values']['access_code']) < 4) {
-    form_set_error('user_access_code', t('Please choose an access code that is at least 4 characters long. A short access code poses security risk.'));
+    form_set_error('access_code', t('Please choose an access code that is at least 4 characters long. A short access code poses security risk.'));
   }
-
+  
   $form_state['values']['access_code_expiration'] = mktime(23, 59, 59, $form_state['values']['access_code_expiration']['month'], $form_state['values']['access_code_expiration']['day'], $form_state['values']['access_code_expiration']['year']);
+  
+  if (date('Y', $form_state['values']['access_code_expiration']) > 2037) {
+  	form_set_error('access_code_expiration', t('Expirations cannot be set beyond year 2037.'));  	 
+  }
+  
+  if (!empty($form_state['values']['access_code'])) {
+	  $check = _access_code_check_unique_code($form_state['values']['access_code']);
+	  
+	  if (!$check) {
+	  	form_set_error('access_code', t('This access code has already been used. You cannot reuse access codes. Please enter a different code.'));
+	  }
+  }
+  
+  
 }
 
 /**
@@ -205,14 +254,17 @@ function access_code_user_update(&$edit, $account, $category) {
  * Implements hook_user_insert().
  */
 function access_code_user_insert(&$edit, $account, $category) {
-  db_insert('access_code')
-    ->fields(array(
-      'uid' => $account->uid,
-      'code' => $edit['access_code'],
-      'expiration' => $edit['access_code_expiration'],
-      )
-    )
-    ->execute();
+
+	if(!empty($account->access_code)){
+	  db_insert('access_code')
+	    ->fields(array(
+	      'uid' => $account->uid,
+	      'code' => $edit['access_code'],
+	      'expiration' => $edit['access_code_expiration'],
+	      )
+	    )
+	    ->execute();
+	}
 }
 
 /**
@@ -257,3 +309,164 @@ function access_code_validate_code($code) {
   }
 }
 
+/**
+ * Form constructor for access code settings.
+ *
+ * @see system_settings_form()
+ * @ingroup forms
+ */
+function access_code_admin_settings($form) {
+	
+	$form['access_code']['auto_code_length'] = array(
+			'#type' => 'select',
+			'#options' => array(4 => 4, 5 => 5, 6 => 6, 7 => 7, 8 => 8, 9 => 9, 10 => 10, 11 => 11, 12 => 12),
+			'#title' => t('Length of Auto Generated Code'),
+			'#default_value' => variable_get('auto_code_length', 8),
+			'#description' => t('Total length of access code EXCLUDING the prefix.')
+	);
+	
+	$form['access_code']['auto_code_prefix'] = array(
+			'#type' => 'textfield',
+    	'#size' => 20,
+			'#title' => t('Code Prefix'),
+			'#default_value' => variable_get('auto_code_prefix'),
+			'#description' => t('Optional string of characters to append to beginning of access code.')
+	);
+
+	$form['access_code']['auto_code_format'] = array(
+			'#type' => 'radios',
+			'#title' => t('Format of Code'),
+			'#options' => array('alpha' => t('Random Alphanumeric'), 'numbers' => t('Numbers'), 'letters' => t('Letters')),			
+			'#default_value' => variable_get('auto_code_format', 'alpha'),
+	);
+
+	$form['access_code']['expiration_default'] = array(
+			'#type' => 'select',
+			'#title' => t('Default expiration period'),
+			'#options' => array('none' => t('None'), '1 day' => t('One Day'), '1 week' => t('One Week'), '1 month' => t('One Month'), '3 months' => t('Three Months'), '6 months' => t('Six Months'), '1 year' => t('One Year'), '2 years' => t('Two Years'), '3 years' => t('Three Years'), '5 years' => t('Five Years'), '10 years' => t('Ten Years')),
+			'#default_value' => variable_get('expiration_default', '1 month'),
+			'#description' => t('Automatically sets the expiration date a set period into the future.')
+	);
+	
+	return system_settings_form($form);
+}
+
+/*
+ * AJAX callback to return automatically generated code to use
+ */
+function access_code_form_code_generate($form, $form_state) {
+	$access_code = _access_code_generate_random_code();
+	$form['account']['access_code_settings']['access_code']['#value'] = $access_code;
+
+	return $form['account']['access_code_settings']['access_code'];	
+}
+
+function _access_code_check_unique_code($code) {
+
+	$result = db_query("SELECT * FROM {access_code} WHERE code=:code", array(':code' => $code ))->rowCount();	
+
+	if ($result == 0) {
+		return TRUE;
+	}
+}
+
+function _access_code_generate_random_code() {
+	global $passes; 
+	
+	$passes++;
+	
+	$prefix = variable_get('auto_code_prefix');
+	$format = variable_get('auto_code_format', 'alpha');
+	$length = variable_get('auto_code_length', 8);
+	
+	// Length must be minimum of 4 characters.
+	if ($length < 4) {
+		$length = 4;
+	}
+	
+	switch ($format) {
+	  case 'numbers':
+	  	$chars = '0123456789';
+	  	break;
+	  case 'letters':
+	  	$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+	  	break;
+	  case 'alpha':
+	  default:
+	  	$chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+	  	break;
+	}
+	
+	$random = '';
+	
+	for($i=0; $i<$length; $i++) {
+		$random .= substr(str_shuffle($chars), 0,1);
+	}
+	
+	$access_code = $prefix.$random;
+	
+	$check = _access_code_check_unique_code($access_code);
+	
+	if ($check) {
+		return $access_code;
+	}
+	else {
+		// If we don't have a unique code after 10 tries, we might need to change our settings. Abort.
+		if ($passes > 10) {
+			$link = l('Settings', 'admin/config/access_code');		
+			watchdog('Access Code','Unique could not be generated. Please check Access Code settings.', NULL, WATCHDOG_ERROR, $link);				
+			exit;
+		}
+		// If the code is not unique generate another one.
+		_access_code_generate_random_code();
+	}
+}
+
+function access_code_tokens($type, $tokens, $data) {
+
+	if ($type == 'user' && !empty($data['user'])) {
+
+		$replacements = array();
+		
+		foreach ($tokens as $name => $original) {
+
+			if (!in_array($name, array('access_code','access_code_expiration'))) {
+				continue;
+			}		
+			
+			if ($name == 'access_code_expiration') {
+				$datetime = date('Y-m-d H:i:s', filter_xss($data['user']->access_code_expiration));
+				$date = new DateTime($datetime, new DateTimeZone('GMT'));
+				$date->setTimezone(new DateTimeZone($data['user']->timezone));				
+				$replacement_val = $date->format('F jS, Y');								
+			} 
+			else if ($name == 'access_code') {
+				$replacement_val = filter_xss($data['user']->access_code);				
+			}
+			
+			if (isset($replacement_val)) {
+				$replacements[$original] = $replacement_val;				
+			}
+		}		
+		return $replacements;
+	}
+
+}
+
+
+/**
+ * Implements hook_token_info().
+ */
+function access_code_token_info() {
+
+	$info['tokens']['user']['access_code'] = array(
+			'name' => t('Access Code'),
+			'description' => t('Login access code.'),
+	);
+	$info['tokens']['user']['access_code_expiration'] = array(
+			'name' => t('Access Code Expiration'),
+			'description' => t('Date that the access code will stop working.'),
+	);
+	
+	return $info;
+}
\ No newline at end of file
-- 
1.8.5.2 (Apple Git-48)


From ce541747037a10a09b098380bfc83eb5b1e226b1 Mon Sep 17 00:00:00 2001
From: Sheldon Chang <sheldon.chang@hyperlinked.com>
Date: Mon, 12 May 2014 09:20:22 -0700
Subject: [PATCH 2/2] Issues 1716150,182217,1979620,2178513,2265509 and
 addition of a module configure page

---
 access_code.module | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/access_code.module b/access_code.module
index 6f050a5..da8bf3b 100644
--- a/access_code.module
+++ b/access_code.module
@@ -181,7 +181,7 @@ function access_code_user_profile_form_validate($form, &$form_state) {
   }
   
   if (!empty($form_state['values']['access_code'])) {
-	  $check = _access_code_check_unique_code($form_state['values']['access_code']);
+	  $check = _access_code_check_unique_code($form_state['values']['access_code'], $form_state['user']->uid);
 	  
 	  if (!$check) {
 	  	form_set_error('access_code', t('This access code has already been used. You cannot reuse access codes. Please enter a different code.'));
@@ -361,10 +361,17 @@ function access_code_form_code_generate($form, $form_state) {
 	return $form['account']['access_code_settings']['access_code'];	
 }
 
-function _access_code_check_unique_code($code) {
+function _access_code_check_unique_code($code, $uid=NULL) {
 
-	$result = db_query("SELECT * FROM {access_code} WHERE code=:code", array(':code' => $code ))->rowCount();	
+	if (isset($uid)) {
+		$query = "SELECT * FROM {access_code} WHERE code=:code AND uid !=:uid";
+	}
+	else {
+		$query = "SELECT * FROM {access_code} WHERE code=:code";
+	}
 
+	$result = db_query($query, array(':code' => $code, ':uid' => $uid ))->rowCount();
+	
 	if ($result == 0) {
 		return TRUE;
 	}
-- 
1.8.5.2 (Apple Git-48)

