Currently, in web applications that generated by PHPMaker 9, when user forgot his/her password, and request the new one, then system will send the new password to his/her email. The problem is, when user A, for example, knows about email that belongs to user B, then user A can reset password that belongs to user B directly, and if user B rarely checks his/her email, then he/she never know about this change. The worst possibility is user B cannot login until he/she checks the email that contains the new password.
The following PHMaker template customization will solve the problem by adding a request password confirmation before system send a new password to the user.
So, when user request a new password, then the following steps will be happened:
1. System send email that contains a link to confirm that user will agree about the password change (request password confirmation). If user clicks on that link, then it will trigger the system will do the next process in the step number two below. Otherwise, when user ignore that email, then the step number two below will never be happened.
2. System send email that contains a new password.
In other words, this will prevent user to reset password directly that belongs to other user without the approval confirmation from the proper user. This more makes sense than the previous condition that provided PHPMaker until version 9.0.2.
Please note that since this customization is related with my customization in the articles:
- How to Use Email Template Based on the Selected Language in Websites that Created with PHPMaker 9.0.1
- How to Get Root or Homepage URL of Websites that Generated by PHPMaker 9.0.2
- How to Save the Last Users’ Visitted Page in Websites that Generated with PHPMaker 9.0.1
so you have to implement that customization first, before doing the customization below.
Just wanted to make sure that I have tested this following customization, and it works fine.
🙂
Updated on July 22, 2012: This customization has been implemented in PHPMaker version 9.0.3, it matches to each other, and as a result, it works properly.
Updated on September 5, 2012: This customization has been implemented in PHPMaker version 9.0.4, it matches to each other, and as a result, it works properly.
Updated on November 29, 2012: This customization has been implemented in PHPMaker version 9.1.0, it matches to each other, and as a result, it works properly.
Updated on February 9, 2013: This customization has been implemented in PHPMaker version 9.2.0, it matches to each other, and as a result, it works properly.
[hidepost]
-
First of all, we have to alter the users table by adding two new fields as following:
- new_password VARCHAR (128) DEFAULT NULL,
- new_password_activate VARCHAR (128) DEFAULT NULL. -
Open your C:\Program Files\PHPMaker 9\languages\english.xml file, and find this code:
<phrase id="PwdEmailSent" value="Password sent to your email"/>
before that line, please insert the following code:
<phrase id="InvalidActivationKey" value="Invalid activation key or password has been already activated."/> <phrase id="PwdActKey" value="Please check your email to proceed."/>
do the same way for your another .xml language you have. For example, I am also using Indonesian, then I will also insert the following code to C:\Program Files\PHPMaker 9\languages\indonesian.xml:
<phrase id="InvalidActivationKey" value="Kode validasi tidak valid atau kata sandi sudah pernah diaktifkan."/> <phrase id="PwdActKey" value="Silahkan cek email Anda untuk melanjutkan."/>
-
Create a new .txt file and save it as requestpwd-en.txt and put it under your \Script sub-directory of your template, and copy-paste the following code:
Subject: Request Password Confirmation From: <!--$From--> To: <!--$To--> Cc: Bcc: Format: TEXT Dear Sir/Madam, You are receiving this notification because you have (or someone pretending to be you has) requested a new password be sent for your account. If you did not request this notification then please ignore it, if you keep receiving it please contact your Administrator. Otherwise, to use the new password you need to activate it. To do this, please click on this link: <!--$Activation_Key_URL--> After activating it, we will send a new password to your email. Best regards, Support
do the same way with your another language, for example, I am also using Indonesian, then I will create \Script\requestpwd-id.txt file that contains the following code:
Subject: Konfirmasi Permintaan Kata Sandi From: <!--$From--> To: <!--$To--> Cc: Bcc: Format: TEXT Yth. Bapak/Ibu, Anda menerima pemberitahuan ini karena Anda (atau seseorang yang menganggap dirinya sebagai Anda) telah meminta kata sandi baru untuk akun Anda. Jika ternyata Anda tidak meminta kata sandi yang baru tersebut, maka abaikan saja email ini. Tapi jika Anda benar yang meminta kata sandi baru tersebut, maka mohon konfirmasikan kepada kami dengan cara mengaktifkan kata sandi tersebut terlebih dulu. Untuk mengaktifkan kata sandi yang baru, silahkan klik tautan berikut ini (browser Anda akan otomatis muncul): <!--$Activation_Key_URL--> Selanjutnya, kami akan segera mengirimkan kata sandi yang baru ke email Anda. Salam, Administrator
-
Open your \Script\forgotpwd.php file, and find this code (for those of you who are using PHPMaker <= 9.0.3):
[code lang="php"]
$conn->Execute($this->UpdateSQL($rsnew));
[/code]whereas if you are using PHPMaker >= 9.0.4, then find this code:
$this->Update($rsnew);
then replace it with the following code:
// $conn->Execute($this->UpdateSQL($rsnew)); // Old method? <= 9.0.3 // Begin of modification Activate Password, by Masino Sinaga, May 31, 2012 //$this->Update($rsnew); // Started from PHPMaker 9.0.4 // End of modification Activate Password, by Masino Sinaga, May 31, 2012
-
Still in that \Script\forgotpwd.php file, find again this code:
$Email = new cEmail();
before that line, please insert the following code:
// Begin of modification Activate Password, by Masino Sinaga, May 31, 2012 $key_len = 54 - 20; $key_len = max(6, $key_len); // we want at least 6 $key_len = (8) ? min($key_len, 8) : $key_len; // we want at most 8 $user_actkey = substr($this->gen_rand_string(10), 0, $key_len); $user_password = $this->gen_rand_string(8); $url_activation = ms_RootHomepageUrl() . '/activatepassword.php?key='.$user_actkey; // End of modification Activate Password, by Masino Sinaga, May 31, 2012
-
Still in that \Script\forgotpwd.php file, find again this code:
// Begin of modification Email Template based on Selected Language, by Masino Sinaga, May 4, 2012 $Email->Load(str_replace('forgotpwd.txt', 'forgotpwd-'.$GLOBALS["Language"]->LanguageId.'.txt', '<!--##=sFnForgotPwdTxt##-->')); // End of modification Email Template based on Selected Language, by Masino Sinaga, May 4, 2012
then replace it with the following code:
// Begin of modification Email Template based on Selected Language, by Masino Sinaga, May 4, 2012 // Begin of modification Activate Password, by Masino Sinaga, May 31, 2012 $Email->Load(str_replace('requestpwd.txt', 'requestpwd-'.$GLOBALS["Language"]->LanguageId.'.txt', 'phptxt/requestpwd.txt')); // End of modification Activate Password, by Masino Sinaga, May 31, 2012 // End of modification Email Template based on Selected Language, by Masino Sinaga, May 4, 2012
-
Still in that \Script\forgotpwd.php file, and find again this code:
$Email->ReplaceContent('<!--$UserName-->', $sUserName); $Email->ReplaceContent('<!--$Password-->', $sPassword);
then replace it with the following code:
$Email->ReplaceContent('<!--$Activation_Key_URL-->', $url_activation); // Modified by Masino Sinaga, Activate Password, May 31, 2012
-
Still in that \Script\forgotpwd.php file, find again this code:
<!--## if (SYSTEMFUNCTIONS.ServerScriptExist("Other","Email_Sending")) { ##--> $Args = array(); $Args["rs"] = &$rsnew; if ($this->Email_Sending($Email, $Args)) $bEmailSent = $Email->Send(); <!--## } else { ##--> $bEmailSent = $Email->Send(); <!--## } ##-->
then replace it with the following code:
<!--## if (SYSTEMFUNCTIONS.ServerScriptExist("Other","Email_Sending")) { ##--> $Args = array(); $Args["rs"] = &$rsnew; // Begin of modification Activate Password, by Masino Sinaga, May 31, 2012 if ($this->Email_Sending($Email, $Args)) { $bEmailSent = $Email->Send(); $sql = "UPDATE ".EW_USER_TABLE." SET new_password='".$user_password."', new_password_activate='".$user_actkey."' WHERE email = '".$this->Email."'"; $conn->Execute($sql); } // End of modification Activate Password, by Masino Sinaga, May 31, 2012 <!--## } else { ##--> $bEmailSent = $Email->Send(); $sql = "UPDATE ".EW_USER_TABLE." SET new_password='".$user_password."', new_password_activate='".$user_actkey."' WHERE email = '".$this->Email."'"; $conn->Execute($sql); <!--## } ##-->
-
Still in that \Script\forgotpwd.php file, find again this code:
$this->setSuccessMessage($Language->Phrase("PwdEmailSent")); // Set up success message
then replace it with the following code:
// $this->setSuccessMessage($Language->Phrase("PwdEmailSent")); // Set up success message $this->setSuccessMessage($Language->Phrase("PwdActKey")); // Modified by Masino Sinaga, Activate Password, May 31, 2012
-
Still in that \Script\forgotpwd.php file, find again this code:
<!--##~SYSTEMFUNCTIONS.GetServerScript("Other","User_RecoverPassword")##--> <!--##/session##-->
then replace it with the following code:
<!--##~SYSTEMFUNCTIONS.GetServerScript("Other","User_RecoverPassword")##--> /** Just additional function, please be kind... ;-) **/ /** * Generates an alphanumeric random string of given length */ function gen_rand_string($num_chars = 8) { $rand_str = $this->unique_id(); $rand_str = str_replace('0', 'Z', strtoupper(base_convert($rand_str, 16, 35))); return substr($rand_str, 0, $num_chars); } /** * Return unique id * @param string $extra additional entropy */ function unique_id($extra = 'c') { static $dss_seeded = false; global $config; $val = '4cc5d71c0f7208b4c7708e3c6ae41d10' . microtime(); $val = md5($val); return substr($val, 4, 16); } <!--##/session##-->
-
Create a new file and save it as activatepassword.php and put it under your \Script\ template sub-directory, and copy-paste the following code (please note that some parts of the following code needs to be customized by yourself, such as Project ID and URL Activation. So please be kind):
<?php if (session_id() == "") session_start(); // Initialize Session data ob_start(); // Turn on output buffering ?> <?php include_once "ewcfg9.php" ?> <?php if (EW_IS_MYSQL) { include_once "ewmysql9.php"; } else if (EW_IS_MSSQL) { include_once "adodb5/adodb.inc.php"; } ?> <?php include_once "phpfn9.php" ?> <?php include_once "".MS_USER_TABLE_NAME."info.php" ?> <?php include_once "userfn9.php" ?> <?php // // Page class // $ActivatePassword = NULL; // Initialize page object first class cActivatePassword { // Page ID var $PageID = 'ActivatePassword'; var $sEmail = ''; // Added by Masino Sinaga, in order to prevent error undefined variable $sEmail, January 16, 2012 var $sSrchWhere = ''; // Added by Masino Sinaga, in order to prevent error undefined variable $sSrchWhere, January 16, 2012 var $sDisabled = ''; // Added by Masino Sinaga, in order to prevent error undefined variable $sDisabled, January 16, 2012 // Project ID var $ProjectID = ""; // <-- Don't forget to adjust your Project ID here !!! // Page object name var $PageObjName = 'ActivatePassword'; // Page name function PageName() { return ew_CurrentPage(); } // Page URL function PageUrl() { $PageUrl = ew_CurrentPage() . "?"; return $PageUrl; } // Message function getMessage() { return @$_SESSION[EW_SESSION_MESSAGE]; } function setMessage($v) { ew_AddMessage($_SESSION[EW_SESSION_MESSAGE], $v); } function getFailureMessage() { return @$_SESSION[EW_SESSION_FAILURE_MESSAGE]; } function setFailureMessage($v) { ew_AddMessage($_SESSION[EW_SESSION_FAILURE_MESSAGE], $v); } function getSuccessMessage() { return @$_SESSION[EW_SESSION_SUCCESS_MESSAGE]; } function setSuccessMessage($v) { ew_AddMessage($_SESSION[EW_SESSION_SUCCESS_MESSAGE], $v); } function getWarningMessage() { return @$_SESSION[EW_SESSION_WARNING_MESSAGE]; } function setWarningMessage($v) { ew_AddMessage($_SESSION[EW_SESSION_WARNING_MESSAGE], $v); } // Show message function ShowMessage() { $hidden = TRUE; $html = ""; // Message $sMessage = $this->getMessage(); $this->Message_Showing($sMessage, ""); if ($sMessage <> "") { // Message in Session, display $html .= "<p class=\"ewMessage\">" . $sMessage . "</p>"; $_SESSION[EW_SESSION_MESSAGE] = ""; // Clear message in Session } // Warning message $sWarningMessage = $this->getWarningMessage(); $this->Message_Showing($sWarningMessage, "warning"); if ($sWarningMessage <> "") { // Message in Session, display $html .= "<table class=\"ewMessageTable\"><tr><td class=\"ewWarningIcon\"></td><td class=\"ewWarningMessage\">" . $sWarningMessage . "</td></tr></table>"; $_SESSION[EW_SESSION_WARNING_MESSAGE] = ""; // Clear message in Session } // Success message $sSuccessMessage = $this->getSuccessMessage(); $this->Message_Showing($sSuccessMessage, "success"); if ($sSuccessMessage <> "") { // Message in Session, display $html .= "<table class=\"ewMessageTable\"><tr><td class=\"ewSuccessIcon\"></td><td class=\"ewSuccessMessage\">" . $sSuccessMessage . "</td></tr></table>"; $_SESSION[EW_SESSION_SUCCESS_MESSAGE] = ""; // Clear message in Session } // Failure message $sErrorMessage = $this->getFailureMessage(); $this->Message_Showing($sErrorMessage, "failure"); if ($sErrorMessage <> "") { // Message in Session, display $html .= "<table class=\"ewMessageTable\"><tr><td class=\"ewErrorIcon\"></td><td class=\"ewErrorMessage\">" . $sErrorMessage . "</td></tr></table>"; $_SESSION[EW_SESSION_FAILURE_MESSAGE] = ""; // Clear message in Session } echo "<div class=\"ewMessageDialog\"" . (($hidden) ? " style=\"display: none;\"" : "") . ">" . $html . "</div>"; } // // Page class constructor // function __construct() { global $conn, $Language; $GLOBALS["Page"] = &$this; // Language object if (!isset($Language)) $Language = new cLanguage(); // User table object (MS_USER_TABLE_NAME) // if (!isset($GLOBALS["".MS_USER_TABLE_NAME.""])) $GLOBALS["".MS_USER_TABLE_NAME.""] = new cpengguna; // Page ID if (!defined("EW_PAGE_ID")) define("EW_PAGE_ID", 'ActivatePassword', TRUE); // Start timer if (!isset($GLOBALS["gTimer"])) $GLOBALS["gTimer"] = new cTimer(); // Open connection if (!isset($conn)) $conn = ew_Connect(); } // // Page_Init // function Page_Init() { global $gsExport, $gsExportFile, $UserProfile, $Language, $Security, $objForm, $conn; // User profile $UserProfile = new cUserProfile(); $UserProfile->LoadProfileFromDatabase(CurrentUserName()); // Security $Security = new cAdvancedSecurity(); // Uncomment codes below for security /* if (!$Security->IsLoggedIn()) $Security->AutoLogin(); if (!$Security->IsLoggedIn()) $this->Page_Terminate("login.php"); */ // Global Page Loading event (in userfn*.php) Page_Loading(); // Page Load event $this->Page_Load(); } // // Page_Terminate // function Page_Terminate($url = "") { global $conn; // Page Unload event $this->Page_Unload(); // Global Page Unloaded event (in userfn*.php) Page_Unloaded(); $this->Page_Redirecting($url); // Close connection $conn->Close(); // Go to URL if specified if ($url <> "") { if (!EW_DEBUG_ENABLED && ob_get_length()) ob_end_clean(); header("Location: " . $url); } exit(); } // // Page main // function Page_Main() { global $Security, $Language; //$this->setSuccessMessage("Welcome " . CurrentUserName()); // Put your custom codes here } // Page Load event function Page_Load() { //echo "Page Load"; } // Page Unload event function Page_Unload() { //echo "Page Unload"; } // Page Redirecting event function Page_Redirecting(&$url) { // Example: //$url = "your URL"; } // Message Showing event // $type = ''|'success'|'failure' function Message_Showing(&$msg, $type) { // Example: //if ($type == 'success') $msg = "your success message"; } } ?> <?php ew_Header(FALSE) ?> <?php // Create page object if (!isset($ActivatePassword)) $ActivatePassword = new cActivatePassword(); // Page init $ActivatePassword->Page_Init(); // Page main $ActivatePassword->Page_Main(); // Begin of modification Displaying Breadcrumb Links in All Pages, by Masino Sinaga, May 4, 2012 getCurrentPageTitle(ew_CurrentPage()); // End of modification Displaying Breadcrumb Links in All Pages, by Masino Sinaga, May 4, 2012 ?> <?php include_once "header.php" ?> <?php $ActivatePassword->ShowMessage(); ?> <!-- Put your custom html here --> <?php global $conn, $Language; if($_GET && (!empty($_GET['key']) )) { $passwact=trim($_GET['key']); } $sqlNewPass= "SELECT ".MS_USER_NAME_PARAMETER.", email, new_password FROM ".EW_USER_TABLE." WHERE new_password_activate='".$passwact."'"; $rsNewPass = $conn->Execute($sqlNewPass); if ($rsNewPass->RecordCount() > 0) { $real_new_password = $rsNewPass->fields('new_password'); $new_password = (EW_ENCRYPTED_PASSWORD) ? md5($rsNewPass->fields('new_password')) : $rsNewPass->fields('new_password'); $user_email = $rsNewPass->fields('email'); $user_name = $rsNewPass->fields(''.MS_USER_NAME_PARAMETER.''); $bValidEmail = FALSE; $bEmailSent = FALSE; $Email = new cEmail(); $Email->Load(str_replace('forgotpwd.txt', 'forgotpwd-'.$GLOBALS["Language"]->LanguageId.'.txt', 'phptxt/forgotpwd.txt')); $Email->ReplaceSender(EW_SENDER_EMAIL); // Replace Sender $Email->ReplaceRecipient($user_email); // Replace Recipient $Email->ReplaceContent('<!--$UserName-->', $user_name); $Email->ReplaceContent('<!--$Password-->', $real_new_password); $Email->Charset = EW_EMAIL_CHARSET; $bEmailSent = $Email->Send(); if ($bEmailSent) { $sql = "UPDATE ".EW_USER_TABLE." SET ".MS_USER_PASSWORD_FIELD."='".$new_password."', new_password='', new_password_activate='' WHERE email='".$user_email."'"; $conn->Execute($sql); $ActivatePassword->setSuccessMessage($Language->Phrase("PwdEmailSent")); // Set success message // Modified by Masino Sinaga, May 23, 2012 $ActivatePassword->Page_Terminate("login.php"); // Return to login page } elseif ($bValidEmail) { $ActivatePassword->setFailureMessage($Language->Phrase("FailedToSendMail")); // Set up error message } } else { $ActivatePassword->setFailureMessage($Language->Phrase("InvalidActivationKey")); // Invalid activation key! if (!$Security->IsLoggedIn()) { $ActivatePassword->Page_Terminate("login.php"); } } ?> <?php include_once "footer.php" ?> <?php $ActivatePassword->Page_Terminate(); ?>
-
Open your \Script\control.xml file, and find this code:
<control id="jquery" type="copy" ofolder="jquery" ifolder="jquery" cond="proj/LocalYUIjQueryFiles/EQ/True" />
after that line, please insert the following code:
<control id="activatepassword.php" type="copy" remark="Copy activatepassword.php" ifiles="activatepassword.php" />
-
Still in that \Script\control.xml file, find again this code:
<control id="forgotpwd.txt" type="copy" remark="Copy forgotpwd.txt" ofolderid="_txt" ifiles="forgotpwd.txt" />
before that line, please insert the following code:
<control id="requestpwd-en.txt" type="copy" remark="Copy requestpwd-en.txt" ofolderid="_txt" ifiles="requestpwd-en.txt" /> <control id="requestpwd-id.txt" type="copy" remark="Copy requestpwd-id.txt" ofolderid="_txt" ifiles="requestpwd-id.txt" />
- Finally, re-generate your script files using PHPMaker, as always.
[/hidepost]
Keith Howard says
I think step 6 is incorrect. It refers to something that doesn’t exist in the template.
Masino Sinaga says
Please note that since this customization is related to my customization in article How to Use Email Template Based on the Selected Language in Websites that Created with PHPMaker 9.0.1, then you have to implement the customization in that article first, before doing the customization in this article.
keithh0427 says
Doesn’t step 4 have to change with 9.0.4?
Masino Sinaga says
Yes, I have fixed it. Actually, the code in step 4 is unnecessary though, since we will use our own method to update the table (see step 8). In other words, we will not use the code in step 4 (make sure you have commented/closed this code).
Ahmad Ibrahim says
SMTP connect() failed. msg error that appeared after add/edit action . any help plz thx infront.
Masino Sinaga says
That error message is usually caused by invalid setting for your SMTP Server or your network could not connect to SMTP Server. Please double-check again your SMTP Server settings and make sure you are already connected to it properly.