Index: /branches/rel_ag_9_4_5/webui/proxy/new/inc/language/chinese.php
===================================================================
--- /branches/rel_ag_9_4_5/webui/proxy/new/inc/language/chinese.php	(revision 20493)
+++ /branches/rel_ag_9_4_5/webui/proxy/new/inc/language/chinese.php	(working copy)
@@ -4048,6 +4048,7 @@
 		'hours'                                    => '小时',
 		'xcc_rdn_sep_position_pre'                 => 'Pre（在RDN之前）',
 		'search'                                   => '搜索',
+		'send_otp'                                 => '发送 OTP 二维码',
 		'virtual_database'                         => '虚拟数据库',
 		'edit_custom_rewrite_rule'                 => '编辑自定义改写规则',
 		'action'                                   => '操作',
@@ -4906,6 +4907,7 @@
 		'login_token_length'                       => '登录页面：无效的令牌长度，有效长度为1到64字节。',
 		'format_options'                           => '格式选项',
 		'configuration_autolockoutidle'            => '配置用户帐号闲置超时锁定',
+		'send_qrcode_for_otp_to_email'             => '将第三方 OTP 密钥的二维码发送到电子邮件',
 		'userid_off'                               => '关闭',
 		'aaa_cert_config'                          => '证书服务器配置',
 		'tunnel_type'                              => '隧道类型',
Index: /branches/rel_ag_9_4_5/webui/proxy/new/inc/language/default.php
===================================================================
--- /branches/rel_ag_9_4_5/webui/proxy/new/inc/language/default.php	(revision 20493)
+++ /branches/rel_ag_9_4_5/webui/proxy/new/inc/language/default.php	(working copy)
@@ -2656,6 +2656,7 @@
 	'overwrite' => 'Overwrite',
 	'ignore' => 'Ignore',
 	'configuration_autolockoutidle' => 'Configure Login Idletime Lockout',
+	'send_qrcode_for_otp_to_email' => 'Send QR Code for Third-party OTP Secret to Email',
 	'enable_login_failure_lockout_idle' => 'Enable Login Idletime Lockout',
 	'number_of_idle_time' => 'Idletime',
 	'number_of_interval_time' => 'Duration time',
@@ -2679,6 +2680,7 @@
 	'otp_warning_message' => '* NOTE: Please ensure that NTP is running or system time is synchronized with UTC time.',
 	'nfs_user_id' => 'NFS Account ID',
 	'search' => 'Search',
+	'send_otp' => 'Send OTP QR Code',
 	'account_configuration' => 'Configure For ALL Local Accounts',
 	'apply_to_all' => 'Apply To All',
 	'accounts_configuration' => 'Configure All Local Accounts',
Index: /branches/rel_ag_9_4_5/webui/proxy/new/inc/language/japanese.php
===================================================================
--- /branches/rel_ag_9_4_5/webui/proxy/new/inc/language/japanese.php	(revision 20493)
+++ /branches/rel_ag_9_4_5/webui/proxy/new/inc/language/japanese.php	(working copy)
@@ -3678,6 +3678,7 @@
 	'last_ip_address'                          => '最後の IP アドレス',
 	'confirm_script_clear'                     => '設定スクリプト URL が消去されます。',
 	'search'                                   => '検索',
+	'send_otp'                                 => 'OTP QRコードをメールで送信',
 	'internal_group'                           => '内部グループ',
 	'selectexist'                              => '既存リソースの利用',
 	'role_cond_time'                           => 'ログイン時間',
@@ -4130,6 +4131,7 @@
 	'sub_rule_condition'                       => 'サブルール条件',
 	'binary'                                   => 'バイナリ',
 	'configuration_autolockoutidle'            => 'ログインアイドルタイムアウト設定',
+	'send_qrcode_for_otp_to_email'             => 'サードパーティのOTPシークレットのQRコードをメールで送信',
 	'sitename'                                 => 'サイト名',
 	'userid_off'                               => 'オフ',
 	'hostname'                                 => 'ホスト名',
Index: /branches/rel_ag_9_4_5/webui/proxy/new/inc/language/traditional_chinese.php
===================================================================
--- /branches/rel_ag_9_4_5/webui/proxy/new/inc/language/traditional_chinese.php	(revision 20493)
+++ /branches/rel_ag_9_4_5/webui/proxy/new/inc/language/traditional_chinese.php	(working copy)
@@ -4048,6 +4048,7 @@
 		'hours' => '小時',
 		'xcc_rdn_sep_position_pre' => 'Pre（在RDN之前）',
 		'search' => '搜尋',
+		'send_otp' => '寄送 OTP 用 QR 碼',
 		'virtual_database' => '虛擬資料庫',
 		'edit_custom_rewrite_rule' => '編輯自訂改寫規則',
 		'action' => '操作',
@@ -4906,6 +4907,7 @@
 		'login_token_length' => '登入頁面：無效的令牌長度，有效長度為1到64位元組。',
 		'format_options' => '格式選項',
 		'configuration_autolockoutidle' => '設定使用者帳號閒置超時鎖定',
+		'send_qrcode_for_otp_to_email' => '將第三方 OTP 密鑰的 QR Code 傳送至電子郵件',
 		'userid_off' => '關閉',
 		'aaa_cert_config' => '憑證伺服器設定',
 		'tunnel_type' => '隧道類型',
Index: /branches/rel_ag_9_4_5/webui/proxy/new/incVirtual/localdb/class.cliWrap_vLocalDBAccounts.php
===================================================================
--- /branches/rel_ag_9_4_5/webui/proxy/new/incVirtual/localdb/class.cliWrap_vLocalDBAccounts.php	(revision 20493)
+++ /branches/rel_ag_9_4_5/webui/proxy/new/incVirtual/localdb/class.cliWrap_vLocalDBAccounts.php	(working copy)
@@ -171,34 +171,6 @@
 					// configure all local accounts
 					//---------------------------------------
 					$configure_type =  $_POST[$this->classId . '_configure'];
-				/*	if ($configure_type == 2) {
-						if (isset($_POST[$this->classId . '_forcePasswordChange'])) {
-							$t_resp = cli::exec_direct(CMD_LOCALDB_PASSWDEXPIRE_NEXTLOGIN, "");
-						} else {
-							$t_resp = cli::exec_direct(CMD_CLEAR_LOCALDB_PASSWDEXPIRE_NEXTLOGIN);
-						}
-						if ($t_resp->result != 1) {
-								$t_errStr = cli::get_reason_info($t_resp);
-								$this->jsOnLoadEnd .= 'g_errStr += "' . (urldecode(str_replace('%0A', '\n', urlencode(addslashes($t_errStr))))) . '";';
-								break;
-						}
-					}
-					if ($configure_type == 3) {
-						if (!isset($_POST[$this->classId . '_passwdexpireage'])) {
-							$t_resp = cli::exec_direct(CMD_CLEAR_LOCALDB_PASSWDEXPIRE_AGE);
-						} else {
-							if (strcmp($_POST[$this->classId . '_mode'], "once") != 0) {
-								$t_resp = cli::exec_direct(CMD_LOCALDB_PASSWDEXPIRE_AGE, "" , $_POST[$this->classId . '_passwordexpire'], $_POST[$this->classId . '_mode']);
-							} else {
-								$t_resp = cli::exec_direct(CMD_LOCALDB_PASSWDEXPIRE_AGE, "", $_POST[$this->classId . '_passwordexpire'], "");
-							}
-						}
-						if ($t_resp->result != 1) {
-							$t_errStr = cli::get_reason_info($t_resp);
-							$this->jsOnLoadEnd .= 'g_errStr += "' . (urldecode(str_replace('%0A', '\n', urlencode(addslashes($t_errStr))))) . '";';
-							break;
-						}
-					} */
 					if ($configure_type == 7) {
 						if (!isset($_POST[$this->classId . '_lockout'])) {
 							$t_resp = cli::exec_direct(CMD_NO_LOCALDB_LOCKOUT_AUTO_LOGINFAILURE);
@@ -211,24 +183,6 @@
 							break;
 						}
 					}
-			/*		if ($configure_type == 5) {
-						if (!isset($_POST[$this->classId . '_lockmanual'])) {
-							cli::cmd("localdb lockout unlock");
-            				$t_errStr = cli::cmd("Yes");
-            				$t_errStr = trim($t_errStr);
-            				if (!empty($t_errStr)) {
-            					$this->jsOnLoadEnd .= 'g_errStr += "' . (urldecode(str_replace('%0A', '\n', urlencode(addslashes($t_errStr))))) . '";';
-								break;
-            				}
-						}else {
-							$t_resp = cli::exec_direct(CMD_LOCALDB_LOCKOUT_MANUAL, "", $_POST[$this->classId . '_expiration_duration']);
-							if ($t_resp->result != 1) {
-								$t_errStr = cli::get_reason_info($t_resp);
-								$this->jsOnLoadEnd .= 'g_errStr += "' . (urldecode(str_replace('%0A', '\n', urlencode(addslashes($t_errStr))))) . '";';
-								break;
-							}
-						}
-					} */
 					if ($configure_type == 8) {
 						if (!isset($_POST[$this->classId . '_lockout_idle'])) {
 							$t_resp = cli::exec_direct(CMD_NO_LOCALDB_LOCKOUT_AUTO_IDLETIME);
@@ -241,6 +195,25 @@
 							break;
 						}
 					}
+					if ($configure_type == 9) {
+						$t_cliResp = cli::exec_direct("show localdb account");
+						if ($t_cliResp->result != 1) {
+							$t_errStr = cli::get_reason_info($t_cliResp);
+							$this->jsOnLoadEnd .= 'g_errStr += "' . (urldecode(str_replace('%0A', '\n', urlencode(addslashes($t_errStr))))) . '";';
+							break;
+						}
+						$selectedAccounts = array();
+						$list = $t_cliResp->content[1]->list;
+						for ($i = 0, $cnt = count($list); $i < $cnt; $i++) {
+							$t_data = $list[$i];
+							$selectedAccounts[] = trim($t_data->user_name);
+						}
+						$error_msg = $this->callSendOTPCLIByAccounts($selectedAccounts);
+						if (!empty($error_msg)) {
+							$this->jsOnLoadEnd .= 'g_errStr += "' . $error_msg . '";';
+						}
+						break;
+					}
 					break;
 				case ($this->classId . '_vEnablePasswdChange'):
 					$t_resp = cli::exec_direct(CMD_LOCALDB_PASSWDEXPIRE_NEXTLOGIN, "");
@@ -280,6 +253,20 @@
 						break;
             		}
 					break;
+				case ($this->classId . '_sendOTP'):
+					if (empty($_POST[$this->classId . '_sendOTPArr'])) {
+						break;
+					}
+					$selectedAccounts = explode("\x01", $_POST[$this->classId . '_sendOTPArr']);
+					if (count($selectedAccounts) == 0) {
+						// No accounts selected
+						break;
+					}
+					$error_msg = $this->callSendOTPCLIByAccounts($selectedAccounts);
+					if (!empty($error_msg)) {
+						$this->jsOnLoadEnd .= 'g_errStr += "' . $error_msg . '";';
+					}
+					break;
 				default:
 					break;
 			}
@@ -303,10 +290,7 @@
 		// Pagination support
 		// ------------------------------------------------------------
 		pilot::setSessionData('rowMax', 100);
-//		pilot::setSessionData('pageIndex', 1);
-//		pilot::setSessionData('pageIndexStart', 1);
-//		pilot::setSessionData('pageIndexEnd', 1);
-		
+
 		// ------------------------------------------------------------
 		// CLI DATA:  get all localdb data
 		// ------------------------------------------------------------
@@ -377,37 +361,7 @@
 			$t_errStr = cli::get_reason_info($t_cliResp);
 			$this->jsOnLoadEnd .= 'g_errStr += "' . (urldecode(str_replace('%0A', '\n', urlencode(addslashes($t_errStr))))) . '";';
 		}
-		
-		
-		/*
-		$assigned_group = cli::exec_direct(CMD_SHOW_LOCALDB_MEMBER_ACCOUNT, $search_name);
-		if ($assigned_group->result != 1) {
-			$t_errStr = cli::get_reason_info($assigned_group);
-			//$this->jsOnLoadEnd .= 'g_errStr += "' . (urldecode(str_replace('%0A', '\n', urlencode(addslashes($t_errStr))))) . '";';
-		}
-		/*
-		$t_nextlogincli = cli::exec_direct(CMD_SHOW_LOCALDB_PASSWDEXPIRE_NEXTLOGIN, $search_name);
-		if ($t_nextlogincli->result != 1) {
-			$t_errStr = cli::get_reason_info($t_nextlogincli);
-			//$this->jsOnLoadEnd .= 'g_errStr += "' . (urldecode(str_replace('%0A', '\n', urlencode(addslashes($t_errStr))))) . '";';
-		}
-		$t_lockmanualcli = cli::exec_direct(CMD_LOCALDB_LOCKOUT_LIST, "MANUAL");
-		if ($t_lockmanualcli->result != 1) {
-			$t_errStr = cli::get_reason_info($t_lockmanualcli);
-			//$this->jsOnLoadEnd .= 'g_errStr += "' . (urldecode(str_replace('%0A', '\n', urlencode(addslashes($t_errStr))))) . '";';
-		}
-		
-		$t_maxpasswdagecli = cli::exec_direct(CMD_SHOW_LOCALDB_PASSWDEXPIRE_AGE, $search_name, "");
-		if ($t_maxpasswdagecli->result != 1) {
-			$t_errStr = cli::get_reason_info($t_maxpasswdagecli);
-			//$this->jsOnLoadEnd .= 'g_errStr += "' . (urldecode(str_replace('%0A', '\n', urlencode(addslashes($t_errStr))))) . '";';
-		}
-		$t_accountIP = cli::exec_direct(CMD_SHOW_LOCALDB_IP_ACCOUNT, $search_name);
-		if ($t_accountIP->result != 1) {
-			$t_errStr = cli::get_reason_info($t_accountIP);
-			//$this->jsOnLoadEnd .= 'g_errStr += "' . (urldecode(str_replace('%0A', '\n', urlencode(addslashes($t_errStr))))) . '";';
-		}
-	*/	
+
 		while(list($t_key, $t_data) = each($t_cliResp->content[1]->list)) {
 			$sso_account = "";
 			$sso_domain = "";
@@ -444,80 +398,14 @@
 						trim($sso_pw)
 						);
 		}
-		//	print_r($this->accountArr);
-		/*
-		// change password nextlogin
-		while(list($t_key, $t_data) = each($t_nextlogincli->content[0]->list)) {
-			if (isset($this->accountArr[$t_data->account_name])) {
-				$this->accountArr[$t_data->account_name][11] = 'Yes'; 	
-			}
-		}
-		// assigned group
-		while(list($t_key, $t_data) = each($assigned_group->content[0]->list)) {
-			if (isset($this->accountArr[$t_data->account_name])) {
-				if (empty($this->accountArr[$t_data->account_name][2])) {
-					$this->accountArr[$t_data->account_name][2] = $t_data->group_name; 
-				} else {
-					$this->accountArr[$t_data->account_name][2] .= ',' . $t_data->group_name; 
-				}
-			}
-		}
-		// lockout manual
-		while(list($t_key, $t_data) = each($t_lockmanualcli->content[0]->list)) {
-			if (isset($this->accountArr[$t_data->account_name])) {
-				$this->accountArr[$t_data->account_name][12] = 'Yes'; 
-				$this->accountArr[$t_data->account_name][15] = $t_data->expires_at;
-			}
-		}
-		// failed login
-		// remove
-		// password age
-		while(list($t_key, $t_data) = each($t_maxpasswdagecli->content[0]->list)) {
-			if (isset($this->accountArr[$t_data->account_name])) {
-					$this->accountArr[$t_data->account_name][14] = $t_data->password_expiration; 
-				$time = $t_data->interval_time;
-				$p = explode(':', $time);
-				if (count($p) > 0){
-					$hour = intval($p[0]);
-					$mint = intval($p[1]);
-					$sec = intval($p[2]);
-					$sec = $hour*3600 + $mint*60 + $sec;
-				} else {
-					$sec = '';
-				}
-				$this->accountArr[$t_data->account_name][17] = $sec ;
-			}
-		}		
-		// ip account
-		while(list($t_key, $t_data) = each($t_accountIP->content[0]->list)) {
-			if (isset($this->accountArr[$t_data->account_name])) {
-				$this->accountArr[$t_data->account_name][18] = cli::get_ip($t_data->ip_address);
-				$this->accountArr[$t_data->account_name][19] = cli::get_ip($t_data->netmask);
-			}
-		}
-		*/
-		// ------------------------------------------------------------
-		// CLI DATA:  internal ip address (IP Address will need later)
-		// ------------------------------------------------------------
-		/*$t_cliResp = cli::cmd_direct('show localdb ip account');
-		if (@ preg_match_all('/localdb ip account \"(' . ((pilot::getSessionData('searchByName') == '') ? '.*' : '.*' . pilot::getSessionData('searchByName') . '.*') . ')\" (.*) (.*)/', $t_cliResp, $t_matchResp) != FALSE) {
-			while (list($t_key, $t_data) = each($t_matchResp[1])) {
-				if (isset($this->accountArr[$t_data])) {
-					$this->accountArr[$t_data][4] = $t_matchResp[2][$t_key];
-					$this->accountArr[$t_data][5] = $t_matchResp[3][$t_key];
-				}
-			}
-		}
-	*/
+
 		// ------------------------------------------------------------
 		// CHECK VIEW MODE:  section header buttons
 		// ------------------------------------------------------------
 		switch ($this->viewMode) {
 			case CLI_CONFIG:
-				//$this->htmlSectionHeaderBtns = '<B><A href="javascript:formAction_setPage(\'vLocalDBImportAccounts\');">' . language::translate('import') . '</A>';
 				$this->htmlSectionHeaderBtns .= '<B><A href="javascript:deleteLocalDbAccount();">' . language::translate('delete') . '</A>';
 				$this->htmlSectionHeaderBtns .= ' | <A href="javascript:formAction_setPage(\'vLocalDBAccountAdd\');">' . language::translate('add') . '</A></B>';
-				//$this->htmlSectionHeaderBtns2 .='<B><A href="javascript:formAction_setPage(\'vLocalDBAccounts\');">' . language::translate('cancel') . '</A>';
 				$this->htmlSectionHeaderBtns2 .= ' <A href="javascript:sendForm(\'_apply\');">' . language::translate('apply_to_all') . '</A></B>';
 				break;
 			case CLI_ENABLE:
@@ -537,7 +425,13 @@
 				$t_restoreViewMode = $this->viewMode;
 				$this->viewMode = CLI_CONFIG;
 				$this->mainContent .= '<TR><TD nowrap>';
-				$this->mainContent .= anLib_htmlCode::greyBox ('footnoteSearch', $this, array(array('footnote', language::translate('search_localdbaccount_by_name') . anLib_htmlCode::textField('searchByName', $this, urldecode(pilot::getSessionData('searchByName')), $g_tabIndex++, '') . ' ' . anLib_htmlCode::regularBtn('_search_btn', $this, language::translate('search'), $g_tabIndex++, 'onclick="onClickSearch();"') . '')));
+				$send_otp_btn_html = "";
+				if ($t_restoreViewMode == CLI_CONFIG) {
+					$send_otp_btn_html = ' ' . anLib_htmlCode::regularBtn('_send_otp_btn', $this, language::translate('send_otp'), $g_tabIndex++, 'onclick="onSendOTP(event);"');
+					// Default is disabled until click checkboxes
+					$send_otp_btn_html = str_replace('>', ' disabled>', $send_otp_btn_html);
+				}
+				$this->mainContent .= anLib_htmlCode::greyBox ('footnoteSearch', $this, array(array('footnote', language::translate('search_localdbaccount_by_name') . anLib_htmlCode::textField('searchByName', $this, urldecode(pilot::getSessionData('searchByName')), $g_tabIndex++, '') . ' ' . anLib_htmlCode::regularBtn('_search_btn', $this, language::translate('search'), $g_tabIndex++, 'onclick="onClickSearch();"') . $send_otp_btn_html . '')));
 				$this->mainContent .= '</TD></TR>';
 				$this->viewMode = $t_restoreViewMode;
 				
@@ -590,10 +484,10 @@
 				// configuration for account
 				$this->mainContent .= anLib_htmlCode::pageSectionStart('account_configuration', 1, $this->htmlSectionHeaderBtns2);
 				$this->mainContent .= anLib_htmlCode::cliWrap_startTable();
-				
-				$configuration_value = array("1", "2", "3", "5", "7", "8");
-				$configuration_name = array(language::translate('configutation_choose'), language::translate('configuration_changepassword'), language::translate('configuration_passwdage') , language::translate('configuration_lockoutmanual') , language::translate('configuration_faillogin'), language::translate('configuration_autolockoutidle'));
-				
+
+				$configuration_value = array("1", "2", "3", "5", "7", "8", "9");
+				$configuration_name = array(language::translate('configutation_choose'), language::translate('configuration_changepassword'), language::translate('configuration_passwdage') , language::translate('configuration_lockoutmanual') , language::translate('configuration_faillogin'), language::translate('configuration_autolockoutidle'), language::translate('send_qrcode_for_otp_to_email'));
+
 				$this->mainContent .= anLib_htmlCode::cliWrap_rowPulldownMenu (
 											'_configure',
 											'accounts_configuration',
@@ -610,15 +504,7 @@
 				$t_HTML .= '<TD style="width:100%;">' . anLib_htmlCode::regularBtn('_forcePasswordChange_enable', $this, language::translate('vLocaldbAccountEnable'), $g_tabIndex++, 'onClick="EnablePasswdChange();"') . ' ' . anLib_htmlCode::regularBtn('_forcePasswordChange_disable', $this, language::translate('vLocaldbAccountDisable'), $g_tabIndex++, 'onClick="DisablePasswdChange();"') . '</TD>';
 				$t_HTML .= '</TR>';
 				$this->mainContent .= $t_HTML;
-		/*		$this->mainContent .= anLib_htmlCode::cliWrap_rowCheckBox (
-											'_forcePasswordChange',
-											'force_password_change', 
-											$this,
-											$this->forcePasswordChangeDefault,
-											$g_tabIndex++,
-											'',
-											'');
-			*/	
+
 				$this->mainContent .= anLib_htmlCode::cliWrap_rowCheckBox(
 											'_lockout', 
 											'enable_login_failure_lockout', 
@@ -644,15 +530,7 @@
 											$g_tabIndex++, 
 											'maxlength="32" size ="32" onkeyup="this.value = stripCharsNotInBag(this.value, \'1234567890\');" onmouseout="if (!isInteger(this.value)) { this.value = stripCharsNotInBag(this.value, \'1234567890\'); }"',
 											language::translate('interval_time_unit'));
-				
-		/*		$this->mainContent .= anLib_htmlCode::cliWrap_rowCheckBox(
-											'_passwdexpireage', 
-											'enable_passwd_expire', 
-											$this,
-											$this->PasswdexpireageDefault,
-											$g_tabIndex++, 
-											'onclick="setIsElementVisible(\'' . $this->classId . '_mode_row\', this.checked);setIsElementVisible(\'' . $this->classId . '_passwordexpire_row\', this.checked);"');
-			*/	
+
 				$this->mainContent .= anLib_htmlCode::cliWrap_rowPulldownMenu(
 											'_mode', 
 											'passwd_expire_mode',
@@ -678,15 +556,7 @@
 				$t_HTML .= '<TD style="width:100%;">' . anLib_htmlCode::regularBtn('_PasswordExpire_enable', $this, language::translate('vLocaldbAccountEnable'), $g_tabIndex++, 'onClick="EnablePasswordExpire();"') . ' ' . anLib_htmlCode::regularBtn('_PasswordExpire_disable', $this, language::translate('vLocaldbAccountDisable'), $g_tabIndex++, 'onClick="DisablePasswordExpire();"') . '</TD>';
 				$t_HTML .= '</TR>';
 				$this->mainContent .= $t_HTML;
-											
-		/*		$this->mainContent .= anLib_htmlCode::cliWrap_rowCheckBox(
-											'_lockmanual', 
-											'enable_lockout_manual', 
-											$this,
-											$this->LockoutmanualDefault,
-											$g_tabIndex++, 
-											'onclick="setIsElementVisible(\'' . $this->classId . '_expiration_duration_row\', this.checked);"');
-		*/		
+
 				$this->mainContent .= anLib_htmlCode::cliWrap_rowTextField(
 											'_expiration_duration', 
 											'lock_manual_interval_time', 
@@ -748,6 +618,7 @@
 	********************************************************************/
     function setFormContent () {
 		$this->formContent .= '<INPUT type="hidden" name="' . $this->classId . '_deleteAccountArr">';
+		$this->formContent .= '<INPUT type="hidden" name="' . $this->classId . '_sendOTPArr">';
 		$this->formContent .= '<INPUT type="hidden" name="' . $this->classId . '_editUserName">';
 		$this->formContent .= '<INPUT type="hidden" name="' . $this->classId . '_editPassword">';
 		$this->formContent .= '<INPUT type="hidden" name="' . $this->classId . '_editConfirmPassword">';
@@ -757,8 +628,6 @@
 		$this->formContent .= '<INPUT type="hidden" name="' . $this->classId . '_editlockoutmanualduration">';
 		$this->formContent .= '<INPUT type="hidden" name="' . $this->classId . '_editpasswdexpireage">';
 		$this->formContent .= '<INPUT type="hidden" name="' . $this->classId . '_editpasswdexpiration">';
-		//$this->formContent .= '<INPUT type="hidden" name="' . $this->classId . '_editpasswdexpireagemode">';
-		//$this->formContent .= '<INPUT type="hidden" name="' . $this->classId . '_editpasswdexpireageduration">';
 		$this->formContent .= '<INPUT type="hidden" name="' . $this->classId . '_editloginfailure">';
 		$this->formContent .= '<INPUT type="hidden" name="' . $this->classId . '_editloginfailureexpiration">';
 		$this->formContent .= '<INPUT type="hidden" name="' . $this->classId . '_editloginfailureattempts">';
@@ -834,13 +703,21 @@
 				"' . language::translate('app_sso_user_name') . '",
 				"' . language::translate('app_sso_domain') . '"
 			];';
-			
+		if ($this->viewMode == CLI_CONFIG) {
+			$checkboxAll = "<INPUT type='checkbox' id='grid_accounts_selector_all' onclick='onClickSelectAll(this);' title='" . language::translate('check_all') . "' />";
+			$this->jsAppend .= 'accountGridHeaders = ["' . $checkboxAll . '", ...accountGridHeaders];';
+		}
+
 		// Declare text (e.g. for HTML display) and value (e.g. for sorting or CLI parameters) arrays
 		$t_jsAppend = 'var accountDataValue = [';
 		$this->jsAppend .= 'var accountDataText = [';
 		$t_keyMin = (pilot::getSessionData('pageIndexStart') - 1) * pilot::getSessionData('rowMax');
 		while (list($t_key, $t_data) = each($this->accountArr)) {
-			$this->jsAppend .= '["' . helper::makeHTMLSafe($t_data[0]) . '", "' . $t_data[1] . '", "' . helper::makeHTMLSafe($t_data[2]) . '", "' . $t_data[3] . '", "' . $t_data[4] . '", "' . $t_data[5] . '", "' . $t_data[6] . '", "' . $t_data[7] . '", "' . $t_data[8] . '", "' . $t_data[9] . '", "' . $t_data[10] . '", "' . $t_data[11] . '", "' . $t_data[12] . '", "' . $t_data[13] . '", "' . $t_data[14] . '", "' . $t_data[15] . '", "' . $t_data[16] . '", "' . $t_data[17] . '", "' . $t_data[18] . '", "' . $t_data[19] . '","' . helper::makeHTMLSafe($t_data[20]) . '","' . helper::makeHTMLSafe($t_data[21]) . '","' . helper::makeHTMLSafe($t_data[22]) . '"],';
+			$checkboxEach = "";
+			if ($this->viewMode == CLI_CONFIG) {
+				$checkboxEach = "'" . '<INPUT type="checkbox" id="grid_accounts_selector_' . helper::makeHTMLSafe($t_data[0]) . '" value="' . helper::makeHTMLSafe($t_data[0]) . '" class="grid_accounts_selector" onclick="onClickSelect(this);" />' . "', ";
+			}
+			$this->jsAppend .= '[' . $checkboxEach . '"' . helper::makeHTMLSafe($t_data[0]) . '", "' . $t_data[1] . '", "' . helper::makeHTMLSafe($t_data[2]) . '", "' . $t_data[3] . '", "' . $t_data[4] . '", "' . $t_data[5] . '", "' . $t_data[6] . '", "' . $t_data[7] . '", "' . $t_data[8] . '", "' . $t_data[9] . '", "' . $t_data[10] . '", "' . $t_data[11] . '", "' . $t_data[12] . '", "' . $t_data[13] . '", "' . $t_data[14] . '", "' . $t_data[15] . '", "' . $t_data[16] . '", "' . $t_data[17] . '", "' . $t_data[18] . '", "' . $t_data[19] . '","' . helper::makeHTMLSafe($t_data[20]) . '","' . helper::makeHTMLSafe($t_data[21]) . '","' . helper::makeHTMLSafe($t_data[22]) . '"],';
 			$t_jsAppend .= '["' . helper::makeFormSafe($t_data[0]) . '", "' . $t_data[1] . '", "' . helper::makeFormSafe($t_data[2]) . '", "' . $t_data[3] . '", "' . $t_data[4] . '", "' . $t_data[5] . '", "' . $t_data[6] . '", "' . $t_data[7] . '", "' . $t_data[8] . '", "' . $t_data[9] . '", "' . $t_data[10] . '", "' . $t_data[11] . '", "' . $t_data[12] . '", "' . $t_data[13] . '", "' . $t_data[14] . '", "' . $t_data[15] . '", "' . $t_data[16] . '", "' . $t_data[17] . '", "' . $t_data[18] . '", "' . $t_data[19] . '","' . helper::makeFormSafe($t_data[20]) . '","' . helper::makeFormSafe($t_data[21]) . '","' . helper::makeFormSafe($t_data[22]) . '"],';
 		}
 		$this->jsAppend .= '];';
@@ -865,31 +742,33 @@
 			grid_accounts.setSelectorVisible(true);
 			grid_accounts.onHeaderClicked = function(event,index){ return true; };
 			grid_accounts.setSelectorVisible(true);';
-			//grid_accounts.setSelectorResizable(true);';
-			//grid_accounts.setSelectorWidth(' . (strlen(count($this->accountArr)) * 10) . ');
-			
-		$this->jsAppend .= 'grid_accounts.setColumnIndices([0,2,3,4,5,6,7,8,9,10,11,12,15,14,18,19,20,21]);';
+
+		$showColumn = '[0,2,3,4,5,6,7,8,9,10,11,12,15,14,18,19,20,21]';
+		if ($this->viewMode == CLI_CONFIG) {
+			$showColumn = '[0,1,4,5,6,7,8,9,10,11,12,13,16,15,19,20,21,22]';
+		}
+		$this->jsAppend .= 'grid_accounts.setColumnIndices('.$showColumn.');';
 		if ($this->viewMode == CLI_CONFIG) {
 			$this->jsAppend .= '
 				grid_accounts.setSelectionMode("multi-row");
 				grid_accounts.setSelectionMultiple(true);
 				grid_accounts.onCellMouseOver = function(event, column, row) { 
 				//Process the "account name" column here on mouse over.
-				if (column == 0) { 
+				if (column == 1) { 
 						window.status = "Cell " + column + "." + row + " clicked"; 
 						this.getCellTemplate(column, row).setStyle("cursor", "Hand !important"); 
 						this.getCellTemplate(column, row).refresh();
 					}
 				};
 				grid_accounts.onCellMouseOut = function(event, column, row) { 
-				    if (column == 0) { 
+				    if (column == 1) { 
 						window.status = "Cell " + column + "." + row + " clicked"; 
 						this.setCellTemplate(this.getCellTemplate((column + 1), row), column, row); 
 						this.getCellTemplate(column, row).refresh();
 					}
 				};
 				grid_accounts.onCellClicked = function(event, column, row){ 
-				if (column == 0) {
+				if (column == 1) {
 					editAccount(row);
 					return ture;
 					}
@@ -1006,6 +885,87 @@
 				setCookie("a_pageIndex", 1);
 				window.location.replace(window.location.href);
 			}
+			/*************************************************************************
+			 *
+			 * OnClick event for "Send OTP" button
+			 *
+			 * @param	VOID
+			 * @return	VOID
+			 *
+			 *************************************************************************/
+			function onSendOTP (event) {
+				if (event.detail && event.detail !== 1) {
+					return;
+				}
+				var btn = event.target || event.srcElement;
+				btn.disabled = true;
+				var checkboxes = document.getElementsByClassName("grid_accounts_selector");
+				var selectedAccounts = [];
+				for (var i = 0; i < checkboxes.length; i++) {
+					if (checkboxes[i].checked) {
+						selectedAccounts.push(checkboxes[i].value);
+					}
+				}
+				if (selectedAccounts.length === 0) {
+					window.alert("' . language::translate('warning_rowNotSelected') . '");
+					return;
+				}
+				if (document.forms["form_mainView"].actionStr.value == "") {
+					setCookie("currentPageId","vLocalDBAccounts");
+					document.forms["form_mainView"].target = "mainView";
+					document.forms["form_mainView"].actionStr.value = "' . $this->classId . '_sendOTP";
+					document.forms["form_mainView"].' . $this->classId . '_sendOTPArr.value = selectedAccounts.join("\u0001");
+					document.forms["form_mainView"].submit();
+				} else {
+					document.forms["form_mainView"].submit();
+				}
+			}
+			/*************************************************************************
+			 *
+			 * OnClick event for "Select All" checkbox
+			 *
+			 * @param	VOID
+			 * @return	VOID
+			 *
+			 *************************************************************************/
+			function onClickSelectAll (self) {
+				var checkboxes = document.getElementsByClassName("grid_accounts_selector");
+				var isChecked = self.checked;
+				var sendOTPButton = document.getElementById("' . $this->classId . '_send_otp_btn");
+				sendOTPButton.disabled = !isChecked;
+				for (var i = 0; i < checkboxes.length; i++) {
+					checkboxes[i].checked = isChecked;
+				}
+			}
+			/*************************************************************************
+			 *
+			 * OnClick event for individual checkbox
+			 *
+			 * @param	VOID
+			 * @return	VOID
+			 *
+			 *************************************************************************/
+			function onClickSelect () {
+				var checkboxes = document.getElementsByClassName("grid_accounts_selector");
+				var selectAllCheckbox = document.getElementById("grid_accounts_selector_all");
+				var sendOTPButton = document.getElementById("' . $this->classId . '_send_otp_btn"); 
+				var checkedCount = 0;
+				var total = checkboxes.length;
+				for (var i = 0; i < total; i++) {
+					if (checkboxes[i].checked) {
+						checkedCount++;
+					}
+				}
+				if (checkedCount === 0) {
+					selectAllCheckbox.checked = false;
+					sendOTPButton.disabled = true;
+				} else if (checkedCount === total) {
+					selectAllCheckbox.checked = true;
+				} else {
+					selectAllCheckbox.checked = false;
+					sendOTPButton.disabled = false;
+				}
+			}
 			';
 
 		$this->jsAppend .= '
@@ -1118,6 +1078,9 @@
 				// Confirm delete request before continuing...
 					return;
 				}
+				if (t_value == 9) {
+					document.getElementById("' . $this->classId . '_send_otp_btn").disabled = true;
+				}
 			if (document.forms["form_mainView"].actionStr.value == "") {
 				setCookie("currentPageId","vLocalDBAccounts");
 				document.forms["form_mainView"].target = "mainView";
@@ -1253,29 +1216,42 @@
 	*
 	********************************************************************/
     function setCSSAppend () {
+		$column_width = array(
+			0 => '80px',
+			1 => '',
+			2 => '120px',
+			3 => '120px',
+			4 => '80px',
+			5 => '100px',
+			6 => '120px',
+			7 => '130px',
+			8 => '100px',
+			9 => '100px',
+			10 => '100px',
+			11 => '160px',
+			12 => '140px',
+			13 => '120px',
+			14 => '180px',
+			15 => '180px',
+			16 => '120px',
+			17 => '120px',
+			18 => '120px',
+			19 => '120px',
+			20 => '180px',
+			21 => '180px'
+		);
+		if ($this->viewMode == CLI_CONFIG) {
+			// Set width for checkbox column
+			array_unshift($column_width, '20px');
+		}
 		$this->cssAppend .= '#grid_accounts {height: 300px; width: 100%; border: 2px inset; background: white}';
-		$this->cssAppend .= '#grid_accounts .aw-column-0 {text-align: left; width: 80px;}';
-		$this->cssAppend .= '#grid_accounts .aw-column-1 {text-align: left;}';
-		$this->cssAppend .= '#grid_accounts .aw-column-2 {text-align: left; width: 120px;}';
-		$this->cssAppend .= '#grid_accounts .aw-column-3 {text-align: left; width: 120px;}';
-		$this->cssAppend .= '#grid_accounts .aw-column-4 {text-align: left; width: 80px;}';
-		$this->cssAppend .= '#grid_accounts .aw-column-5 {text-align: left; width: 100px;}';
-		$this->cssAppend .= '#grid_accounts .aw-column-6 {text-align: left; width: 120px;}';
-		$this->cssAppend .= '#grid_accounts .aw-column-7 {text-align: left; width: 130px;}';
-		$this->cssAppend .= '#grid_accounts .aw-column-8 {text-align: left; width: 100px;}';
-		$this->cssAppend .= '#grid_accounts .aw-column-9 {text-align: left; width: 100px;}';
-		$this->cssAppend .= '#grid_accounts .aw-column-10 {text-align: left; width: 100px;}';
-		$this->cssAppend .= '#grid_accounts .aw-column-11 {text-align: left; width: 160px;}';
-		$this->cssAppend .= '#grid_accounts .aw-column-12 {text-align: left; width: 140px;}';
-		$this->cssAppend .= '#grid_accounts .aw-column-13 {text-align: left; width: 120px;}';
-		$this->cssAppend .= '#grid_accounts .aw-column-14 {text-align: left; width: 180px;}';
-		$this->cssAppend .= '#grid_accounts .aw-column-15 {text-align: left; width: 180px;}';
-		$this->cssAppend .= '#grid_accounts .aw-column-16 {text-align: left; width: 120px;}';
-		$this->cssAppend .= '#grid_accounts .aw-column-17 {text-align: left; width: 120px;}';
-		$this->cssAppend .= '#grid_accounts .aw-column-18 {text-align: left; width: 120px;}';
-		$this->cssAppend .= '#grid_accounts .aw-column-19 {text-align: left; width: 120px;}';
-		$this->cssAppend .= '#grid_accounts .aw-column-20 {text-align: left; width: 180px;}';
-		$this->cssAppend .= '#grid_accounts .aw-column-21 {text-align: left; width: 180px;}';
+		for ($i = 0, $cnt = count($column_width); $i < $cnt; $i++) {
+			$width_css = "";
+			if (!empty($column_width[$i])) {
+				$width_css = ' width: ' . $column_width[$i] . ';';
+			}
+			$this->cssAppend .= '#grid_accounts .aw-column-' . $i . ' { text-align: left;' . $width_css . ' }';
+		}
 	}
 	
 	/********************************************************************
@@ -1303,5 +1279,50 @@
     function setJsOnSaveAppend () {
 		// Example:  $this->jsOnSaveAppend .= 'window.alert("Hello from setJsOnSaveAppend...");';
 	}
+
+	/********************************************************************
+	*
+	* call QR Code generator for OTP and send Email CLI by accounts
+	* <FORM> onSave event.
+	*
+	* $param	array $selectedAccounts
+	* @return	string $error_msg
+	*
+	********************************************************************/
+    function callSendOTPCLIByAccounts ($selectedAccounts) {
+		$error_arr = array(
+			'no_account' => array(),
+			'no_mail' => array(),
+			'other' => array()
+		);
+		for ($i = 0, $cnt = count($selectedAccounts); $i < $cnt; $i++) {
+			$accounts = escapeshellarg($selectedAccounts[$i]);
+			$t_resp = cli::exec_direct("localdb qrcode $accounts");
+			if ($t_resp->result == 1) {
+				continue;
+			}
+			$t_errStr = cli::get_reason_info($t_resp);
+			if (strpos($t_errStr, "Cannot find the account name") !== false) {
+				$error_arr['no_account'][] = $selectedAccounts[$i];
+			} else if (strpos($t_errStr, "Cannot find the mail address for user") !== false) {
+				$error_arr['no_mail'][] = $selectedAccounts[$i];
+			} else {
+				$html_safte_str = urldecode(str_replace('%0A', '\n', urlencode(addslashes($t_errStr))));
+				$error_arr['other'][] = $html_safte_str . ": " . $selectedAccounts[$i];
+			}
+		}
+		$error_msg = '';
+		if (!empty($error_arr['no_account'])) {
+			$error_msg .= "Cannot find the account name: " . implode(', ', $error_arr['no_account']) . "\\n";
+		}
+		if (!empty($error_arr['no_mail'])) {
+			$error_msg .= "Cannot find the mail address for user: " . implode(', ', $error_arr['no_mail']) . "\\n";
+		}
+		if (!empty($error_arr['other'])) {
+			$error_msg .= implode(', ', $error_arr['other']) . "\\n";
+		}
+		return $error_msg;
+	}
+
 }
 ?>
