Recuperação de senha através de link seguro! Pessoal boa noite, Estou utilizando o framework a quase um mês, e resolvi implantar uma lógica de recuperação de senha, utilizando o mesmo template da tela de login. A ideia é o seguinte: O usuário clica num link adicionado na tela de login de "Esqueceu a senha", é levado para uma tela onde ele digita o email cadastrado e o sistema envia um email com o link para reset, ao clicar nesse li...
HA
Recuperação de senha através de link seguro!  
Pessoal boa noite,

Estou utilizando o framework a quase um mês, e resolvi implantar uma lógica de recuperação de senha, utilizando o mesmo template da tela de login. A ideia é o seguinte: O usuário clica num link adicionado na tela de login de "Esqueceu a senha", é levado para uma tela onde ele digita o email cadastrado e o sistema envia um email com o link para reset, ao clicar nesse link, faço a checagem se é válido e o usuário digita a nova senha.

1º - Adicionar colunas na tabelas de usuários
ALTER TABLE `system_user` ADD `reset_pass` CHAR(1) NULL DEFAULT NULL AFTER `active`, ADD `data_pass` VARCHAR(50) NULL DEFAULT NULL AFTER `reset_pass`, ADD `uid_pass` VARCHAR(50) NULL DEFAULT NULL AFTER `data_pass`;


2º - Adicionar os campos acima no SystemUser para permitir edição
 
  1. <?php
  2. public function __construct($id = NULL)
  3. {
  4. //....
  5. parent::addAttribute('reset_pass');
  6. parent::addAttribute('data_pass');
  7. parent::addAttribute('uid_pass');
  8. }
  9. ?>


3º - Alterei na SystemUserForm para não permitir o cadastro de email já existente, hoje o template valida apenas login
 
  1. <?php
  2. public static function onSave($param)
  3. {
  4. //....
  5. if (SystemUser::newFromLogin($object->login) instanceof SystemUser)
  6. {
  7. throw new Exception(_t('An user with this login is already registered'));
  8. }
  9. if (SystemUser::newFromEmail($object->email) instanceof SystemUser)
  10. {
  11. throw new Exception(_t('An user with this email is already registered'));
  12. }
  13. //...
  14. }
  15. ?>


4º - Controller SendEmailPasswordForm responsável por capturar o email do usuário e enviar o link para reset
 
  1. <?php
  2. class SendEmailPasswordForm extends TPage
  3. {
  4. protected $form; // form
  5. function __construct($param)
  6. {
  7. parent::__construct();
  8. $table = new TTable;
  9. $table->width = '100%';
  10. // creates the form
  11. $this->form = new TForm('form_send_email_pass');
  12. $this->form->class = 'tform';
  13. $this->form->style = 'max-width: 450px; margin:auto; margin-top:120px;';
  14. // add the notebook inside the form
  15. $this->form->add($table);
  16. // create the form fields
  17. $email = new TEntry('email');
  18. // define the sizes
  19. $email->setSize('70%', 40);
  20. $email->style = 'height:35px; font-size:14px;float:left;border-bottom-left-radius: 0;border-top-left-radius: 0;';
  21. $row=$table->addRow();
  22. $row->addCell( new TLabel('Resetar senha') )->colspan = 2;
  23. $row->class='tformtitle';
  24. $email->placeholder = 'user@email.com';
  25. $email->setLabel('Email');
  26. $email->addValidation('Email', new TEmailValidator);
  27. $envelope = '<span style="float:left;width:35px;margin-left:45px;height:35px;" class="input-group-addon"><span class="glyphicon glyphicon-envelope"></span></span>';
  28. $container1 = new TElement('div');
  29. $container1->add($envelope);
  30. $container1->add($email);
  31. $row=$table->addRow();
  32. $row->addCell($container1)->colspan = 2;
  33. // create an action button (save)
  34. $recuperar_button=new TButton('recovery');
  35. // define the button action
  36. $recuperar_button->setAction(new TAction(array($this, 'onReset')), _t('Send'));
  37. $recuperar_button->class = 'btn btn-success';
  38. $recuperar_button->style = 'font-size:18px;width:90%;padding:10px';
  39. $row=$table->addRow();
  40. $row->class = 'tformaction';
  41. $cell = $row->addCell( $recuperar_button );
  42. $cell->colspan = 2;
  43. $cell->style = 'text-align:center';
  44. $login = new TActionLink('Login', new TAction(array($this, 'onLogin')) );
  45. $login->style = 'font-size:16px;width:90%;padding:10px';
  46. $row = $table->addRow();
  47. $cell = $row->addCell( $login );
  48. $this->form->setFields(array($email, $recuperar_button));
  49. // add the form to the page
  50. parent::add($this->form);
  51. }
  52. public function onLogin()
  53. {
  54. AdiantiCoreApplication::gotoPage('LoginForm');
  55. }
  56. public function onReset()
  57. {
  58. try
  59. {
  60. TTransaction::open('permission');
  61. $data = $this->form->getData('StdClass');
  62. $this->form->validate();
  63. $user = SystemUser::newFromEmail( $data->email );
  64. TTransaction::close();
  65. // valida se existe usuário com email digitado
  66. if ($user)
  67. {
  68. try
  69. {
  70. TTransaction::open('permission');
  71. $object = new SystemUser($user->id);
  72. $object->reset_pass = 'Y'; //parâmetro que diz que há uma requisição de reset de senha
  73. $object->data_pass = time(); //variável para validação de link
  74. $object->uid_pass = uniqid(rand(), true); //variável para validação de link
  75. $object->store();
  76. // monta parâmetros GET com criptografia
  77. $url = sprintf( 'id=%s&email=%s&uid=%s&key=%s',$object->id, md5($object->email), md5($object->uid_pass), md5($object->data_pass) );
  78. // monta link que será enviado para o email do usuário
  79. $link = ($_SERVER['HTTPS']=='on' ? "https" : "http") . '://'. $_SERVER['HTTP_HOST'] . '/index.php?class=ResetPasswordForm&'. $url;
  80. // pega parâmetros para envio de email
  81. $prefs = SystemPreference::getAllPreferences();
  82. TTransaction::close();
  83. $mail = new TMail;
  84. $mail->setFrom($prefs['mail_from'], 'Falko ERP');
  85. $mail->setSubject('Reset de senha');
  86. $mail->setHtmlBody("Você solicitou o reset de senha, por favor clique no link abaixo para cadastrar sua nova senha!<br><br>
  87. Usuário: $object->login<br>
  88. <a href='$link'>Resetar senha</a><br><br>
  89. Atenciosamente,<br>
  90. <b>Falko ERP</b>");
  91. $mail->addAddress($object->email);
  92. $mail->SetUseSmtp();
  93. $mail->SetSmtpHost($prefs['smtp_host'], $prefs['smtp_port']);
  94. $mail->SetSmtpUser($prefs['smtp_user'], $prefs['smtp_pass']);
  95. $mail->send();
  96. new TMessage('info', 'Você receberá um email com as instruções para redefinição de senha.');
  97. }
  98. catch (Exception $e)
  99. {
  100. // shows the exception error message
  101. new TMessage('error', $e->getMessage());
  102. // undo all pending operations
  103. TTransaction::rollback();
  104. }
  105. }
  106. else
  107. {
  108. throw new Exception(_t('User not found'));
  109. }
  110. }
  111. catch (Exception $e)
  112. {
  113. new TMessage('error',$e->getMessage());
  114. TTransaction::rollback();
  115. }
  116. }
  117. }
  118. ?>



5º - Controller ResetPasswordForm responsável por checar se o link é válido e permitir o usuário digitar a nova senha
 
  1. <?php
  2. class ResetPasswordForm extends TPage
  3. {
  4. protected $form; // form
  5. /**
  6. * Class constructor
  7. * Creates the page and the registration form
  8. */
  9. function __construct($param)
  10. {
  11. parent::__construct();
  12. $valido = TRUE;
  13. if (isset($_GET['id']))
  14. {
  15. $id = $_GET['id'];
  16. $email = $_GET['email'];
  17. $uid = $_GET['uid'];
  18. $data = $_GET['key'];
  19. // checa se id do usuário existe
  20. TTransaction::open('permission');
  21. $user = new SystemUser($id);
  22. TTransaction::close();
  23. if ($user instanceof SystemUser)
  24. {
  25. if ($user->active == 'N') //não permite que usuário inativo altera a senha
  26. {
  27. $valido = FALSE;
  28. }
  29. else if ($user->reset_pass != 'Y') //valida se há uma requisição de reset de senha
  30. {
  31. $valido = FALSE;
  32. }
  33. else if (md5($user->email) != $email) //valida se email criptografado é igual ao do link acessado
  34. {
  35. $valido = FALSE;
  36. }
  37. else if (md5($user->uid_pass) != $uid) //valida se uid criptografado é igual ao do link acessado
  38. {
  39. $valido = FALSE;
  40. }
  41. else if (md5($user->data_pass) != $data) //valida se email criptografado é igual ao do link acessado
  42. {
  43. $valido = FALSE;
  44. }
  45. }
  46. else
  47. {
  48. $valido = FALSE;
  49. }
  50. }
  51. // se não é valido, leva usuário para tela de login
  52. if (!$valido && isset($_GET['id']))
  53. {
  54. new TMessage('error', _t('User not found'), new TAction(array($this, 'onLogin')) );
  55. return false;
  56. }
  57. $table = new TTable;
  58. $table->width = '100%';
  59. // creates the form
  60. $this->form = new TForm('form_reset_password');
  61. $this->form->class = 'tform';
  62. $this->form->style = 'max-width: 450px; margin:auto; margin-top:120px;';
  63. // add the notebook inside the form
  64. $this->form->add($table);
  65. // create the form fields
  66. $user_id = new THidden('user_id');
  67. $user_id->setValue($user->id);
  68. $senha = new TPassword('senha');
  69. $senha_confirm = new TPassword('senha_confirm');
  70. $senha->placeholder = 'Nova senha';
  71. $senha_confirm->placeholder = 'Digite novamente';
  72. // define the sizes
  73. $senha->setSize('70%', 40);
  74. $senha_confirm->setSize('70%', 40);
  75. $senha->style = 'height:35px; font-size:14px;float:left;border-bottom-left-radius: 0;border-top-left-radius: 0;';
  76. $senha_confirm->style = 'height:35px; font-size:14px;float:left;border-bottom-left-radius: 0;border-top-left-radius: 0;';
  77. $row=$table->addRow();
  78. $row->addCell( new TLabel('Nova senha') )->colspan = 2;
  79. $row->class='tformtitle';
  80. $senha->setLabel('Senha');
  81. $senha_confirm->setLabel('Senha_confirm');
  82. $senha->addValidation('Senha', new TRequiredValidator);
  83. $senha_confirm->addValidation('Senha_confirm', new TRequiredValidator);
  84. $locker = '<span style="float:left;width:35px;margin-left:45px;height:35px;" class="input-group-addon"><span class="glyphicon glyphicon-lock"></span></span>';
  85. $container1 = new TElement('div');
  86. $container1->add($locker);
  87. $container1->add($senha);
  88. $container1->add($user_id);
  89. $container2 = new TElement('div');
  90. $container2->add($locker);
  91. $container2->add($senha_confirm);
  92. $row=$table->addRow();
  93. $row->addCell($container1)->colspan = 2;
  94. $row=$table->addRow();
  95. $row->addCell($container2)->colspan = 2;
  96. // create an action button (save)
  97. $recuperar_button=new TButton('recovery');
  98. // define the button action
  99. $recuperar_button->setAction(new TAction(array($this, 'onReset')), _t('Save'));
  100. $recuperar_button->class = 'btn btn-success';
  101. $recuperar_button->style = 'font-size:18px;width:90%;padding:10px';
  102. $row=$table->addRow();
  103. $row->class = 'tformaction';
  104. $cell = $row->addCell( $recuperar_button );
  105. $cell->colspan = 2;
  106. $cell->style = 'text-align:center';
  107. $this->form->setFields(array($senha, $senha_confirm, $user_id, $recuperar_button));
  108. // add the form to the page
  109. parent::add($this->form);
  110. }
  111. public function onLogin()
  112. {
  113. TApplication::loadPage('LoginForm', '', $_REQUEST);
  114. }
  115. /**
  116. * Reset password
  117. */
  118. public function onReset()
  119. {
  120. try
  121. {
  122. $data = $this->form->getData('StdClass');
  123. $this->form->validate();
  124. //valida se senha e senha de confirmação foram digitadas iguais
  125. if ($data->senha != $data->senha_confirm)
  126. {
  127. new TMessage('error', 'Senhas não conferem, por favor digite as senhas iguais.');
  128. }
  129. else
  130. {
  131. // se passou pelas validações, instancia o usuário
  132. TTransaction::open('permission');
  133. $object = new SystemUser($data->user_id);
  134. if ($object)
  135. {
  136. // grava nova senha e limpa os outros campos, assim é garantido que o link é válido para apenas uma utilização
  137. $object->password = password_hash($data->senha, PASSWORD_DEFAULT);
  138. $object->reset_pass = NULL;
  139. $object->data_pass = NULL;
  140. $object->uid_pass = NULL;
  141. $object->store();
  142. new TMessage('info', 'Senha alterada com sucesso!', new TAction(array($this, 'onLogin')) );
  143. }
  144. else
  145. {
  146. new TMessage('error', _t('User not found'), new TAction(array($this, 'onLogin')) );
  147. }
  148. TTransaction::close();
  149. }
  150. }
  151. catch (Exception $e)
  152. {
  153. new TMessage('error',$e->getMessage());
  154. TTransaction::rollback();
  155. }
  156. }
  157. }
  158. ?>

Curso Dominando o Adianti Framework

O material mais completo de treinamento do Framework.
Curso em vídeo aulas + Livro completo + Códigos fontes do projeto ERPHouse.
Conteúdo Atualizado!


Dominando o Adianti Framework Quero me inscrever agora!

Comentários (38)


HA

Pessoal, qualquer melhoria ou sugestão, podem infomar.
Apenas para pontuar no último arquivo, salvei a senha usando password_hash, pois estou utilizando PHP superior a 5.5. Alterei toda a forma de encriptação no sistema.

Futuramente farei a parte de cadastro de usuário, utilizando uma lógica parecida, com confirmação por link enviado por email.

Abraços!
JA

Muito legal, acho que deveria integrar ao templete, facilita nos próximos updates. Parabéns!
AL

Parabéns pela iniciativa Henrique. Será muito útil para todos.
AL

Alguem pode ajudar, informando de que forma devo chamar o formulario para digitar o email para recuperação de senha. Coloquei o seguinte codigo na pagina Login.html


<a href="index.php?class=LoginForm&method=onRecovery">[Esqueceu sua Senha?]</a></div>

<!-- /#wrapper -->

Esse metodo =onRecovery, faz ligação onde?
HA

Amos ao invés de editar diretamente o Login.html, coloque um link na controladora de LoginForm logo depois do código do botão de entrar.

 
  1. <?php
  2. $recupera = new TActionLink(_t('Forgot your password?'), new TAction(array($this, 'onRecovery')) );
  3. $recupera->style = 'font-size:16px;width:90%;padding:10px';
  4. $row = $table->addRow();
  5. $cell = $row->addCell( $recupera ); ?>


Esse método onRecovery está no próprio LoginForm

 
  1. <?php
  2. public function onRecovery()
  3. {
  4. AdiantiCoreApplication::gotoPage('SendEmailPasswordForm');
  5. } ?>
CF

Henrique, boa noite. Muito legal a sua iniciativa, e funcionou perfeitamente, mas somente se eu acessar o sistema e chamar as telas manualmente. O link está na tela de login chamado o evento OnRecovery, que chama a tela SendEmailPasswordForm conforme abaixo:

 
  1. <?php
  2. public static function OnRecovery()
  3. {
  4. AdiantiCoreApplication::gotoPage('SendEmailPasswordForm');
  5. }
  6. ?>


Ao clicar no link, ele não abre a tela para digitar o e-mail e volta para a tela de login. O que esqueci de fazer?

Abs
Cleber Fosse
HA

Cleber boa noite,

Fico feliz em ter ajudado. Esqueci de informar uma coisa no tutorial, pode ser o seu caso, é preciso adicionar as controladoras como páginas públicas no application.ini, pois as páginas podem estar caindo na checagem de permissão do framework. Veja se isso resolve.

[permission] ; Public classes, anyone (logged or not) has access public_classes[] = PublicForm public_classes[] = PublicView public_classes[] = SendEmailPasswordForm public_classes[] = ResetPasswordForm


Abraços!
CF

Exatamente Henrique, era o que falta.

Muito obrigado pela ajuda.

Abraço e sucesso!!!
CF

Henrique, boa noite.
Voltei a trabalhar no meu sistema e deparei com um problema ao recuperar a senha. Eu recebo o e-mail, faz a atualização no usuário corretamente, mas ao clicar no link não está indo para o formulário solicitar a nova senha e sim para a tela de login normal.

o meu link está da seguinte maneira:
index.php?class=LoginForm&method=&id=1&email=12d23a1861905a1801f13c61da3229fc&uid=71bf7b51c38c2f420b39742fd24cadc0&key=209f532ecce01b409ebf588c325c5f04&_ga=GA1.3.1374476325.1502645928&_gid=GA1.3.334907752.1506130288&PHPSESSID=6bm1gcs0b2gfasjampu8dttpo6


Será que está correto?

Abs,
Cleber
CF

Henrique, corrigindo link:

index.php?class=ResetPasswordForm&id=1&email=12d23a1861905a1801f13c61da3229fc&uid=71bf7b51c38c2f420b39742fd24cadc0&key=209f532ecce01b409ebf588c325c5f04&_ga=GA1.3.1374476325.1502645928&_gid=GA1.3.334907752.1506130288&PHPSESSID=em1b87kfjh95nmoi9p2ichpou1
HA

Cleber tudo bem?
Percebi que você está passando mais algumas outras variáveis GET no link, realizou alguma modificação? Me passa por gentileza qual deveria ser o link inteiro, não só a parte do index.php. Sugiro depurar o código no arquivo ResetPasswordForm e ver onde ele está caindo na variável $valido = false;
CF

Henrique, boa noite.

Fiz uns testes e até comparei a versão do ResetPasswordForm com a sua e está igual. Vou colocar alguns TMessage para ver o resultado das variáveis.
CF

Henrique, na url abaixo tem a mudança que eu fiz para testar se a classe estaria recebe-se ao menos o ID do usuário.

www.s2s.com.br/sid/index.php?class=ResetPasswordForm&id=1

TC

Poxa Vida Cara, Muiiito Show de bola obrigado pela colaboração
HA

Cléber desculpa a demora! Ainda não consegui pegar o problema exato que vc está tendo, o ideal seria a depuração mesmo. Mas tem uma coisa que eu adianto, acabei realizando uma melhoria na montagem do link de reset de senha. Usar a variável $_SERVER['HTTP_HOST'] implica num problema de não conseguirmos pegar o endereço correto quando o sistema está em subpastas. Pra isso, criei mais um campo de preferências para setar a url_base do sistema. Ficando assim:

 
  1. <?php
  2. // pega parâmetros para envio de email
  3. $prefs = SystemPreference::getAllPreferences();
  4. // monta link que será enviado para o email do usuário
  5. $link = $prefs['url_base'] . '/index.php?class=ResetPasswordForm&'. $url;
  6. ?>


No seu exemplo, essa url_base você salvaria no banco como s2s.com.br/sid ou com HTTPS se for o caso. Recomendo bastante realizar essa alteração, isso ajuda a configurar URLs para diferentes ambientes e clientes.
CF

Henrique, boa noite.

Fiz o ajuste para a montagem da URL e da maneira que fez acima (sem nenhuma alteração) e fez a montagem abaixo:
http://s2s.com.br/sid/index.php?class=ResetPasswordForm&id=1


Estranho o porque a tela não é apresentada.

Obrigado pela ajuda. Vou tentar debugar amanhã,
CF

Henrique, aproveitando: eu coloquei os dois fontes PHP na pasta app/control/public, está certo?

Outra coisa que identifiquei foi que o PublicView eu consigo carregar esse arquivo ao logar no sistema e digitando o nome do programa na URL. Fiz a mesma coisa para o ResetPasswordForm e ele dá erro de permissão de acesso.

Talvez essa informação possa ajudar.

Abs
Cleber Fosse
HA

Cléber você pode estar com o mesmo problema que passou antes. Os arquivos não precisam necessariamente estar na pasta Control/public, mas precisam serem adicionadas no application.ini no public_classes
CF

Henrique, boa noite.

O application.ini está OK.
CF

Henrique,
[permission]
; Public classes, anyone (logged or not) has access
public_classes[] = PublicForm
public_classes[] = PublicView
public_classes[] = SendEmailPasswordForm
public_classes[] = ResetPasswordForm
NR

Henrrique esto com problema ao eviar o email na tela de resetar a senha ao enviar o email me da este erro "Método SystemUser::newFromEmail() não encontrado"

O que sera que etou fazendo errado..

Segui todos os passos.
Pode me ajudar
NR

Henrrique esto com problema ao eviar o email na tela de resetar a senha ao enviar o email me da este erro "Método SystemUser::newFromEmail() não encontrado"

O que sera que etou fazendo errado..

Segui todos os passos.
Pode me ajudar
HA

Nilton é preciso implementar o método no modelo SystemUset.
 
  1. <?php
  2. static public function newFromEmail($email)
  3. {
  4. $repos = new TRepository('SystemUser');
  5. $criteria = new TCriteria;
  6. $criteria->add(new TFilter('email', '=', $email));
  7. $objects = $repos->load($criteria);
  8. if (isset($objects[0]))
  9. {
  10. return $objects[0];
  11. }
  12. }
  13. ?>
RB

Henrique,

Estou implementando a opção de recuperá com a sua dica, porém na hora de enviar o email -, da um erro de conexão,

error: connection failed;

MC

Ficou muito bom, show de bola, testando o código acrescentei uma linha para redirecionar para a página de login depois de enviar o email.

na linha 115 alterei para:

 
  1. <?php
  2. new TMessage('info', 'Você receberá um email com as instruções para redefinição de senha.', new TAction(array('LoginForm', 'onClear')) );
  3. ?>


e no arquivo LoginForm.php acrescentei

 
  1. <?php
  2. public function onClear( $param )
  3. {
  4. $this->form->clear(TRUE);
  5. }
  6. ?>


Amanhã vou tentar implementar um bloqueio de tela no form SendEmailPasswordForm enquanto estiver enviando o email. para o usuário não ficar clicando no botão " enviar ". Parabéns!
RB

Miuller,

Tem como postar a solução, pois segui passo a passo, mas na hora que clico em emviar email da o sguinte erro :


Notice: Undefined index: url_base in /var/www/html/centinel/app/control/public/SendEmailPasswordForm.class.php on line 99

Fatal error: Uncaught Error: Class 'PHPMailerPHPMailerPHPMailer' not found in /var/www/html/centinel/app/lib/util/TMail.class.php:23 Stack trace: #0 /var/www/html/centinel/app/control/public/SendEmailPasswordForm.class.php(103): TMail->__construct() #1 [internal function]: SendEmailPasswordForm->onReset(Array) #2 /var/www/html/centinel/lib/adianti/control/TPage.php(51): call_user_func(Array, Array) #3 /var/www/html/centinel/lib/adianti/control/TPage.php(205): AdiantiControlTPage->run() #4 /var/www/html/centinel/lib/adianti/core/AdiantiCoreApplication.php(62): AdiantiControlTPage->show(Array) #5 /var/www/html/centinel/engine.php(34): AdiantiCoreAdiantiCoreApplication::run(true) #6 /var/www/html/centinel/engine.php(43): TApplication::run(true) #7 {main} thrown in /var/www/html/centinel/app/lib/util/TMail.class.php on line 23
MC

Boa tarde Rubens, o email está enviando?, se a resposta for sim, na sua caixa de email, clicando no link teria que redirecionar para o seu projeto, tente substituir a variável urlbase.

 
  1. <?php;
  2. // monta link que será enviado para o email do usuário
  3. $prefs['url_base'] = sprintf( 'http%s://%s%s' , isset( $_SERVER[ 'HTTPS' ] ) ? 's' : null , $_SERVER[ 'HTTP_HOST' ] , dirname( $_SERVER[ 'REQUEST_URI' ] ) );
  4. $link = $prefs['url_base'] . '/index.php?class=ResetPasswordForm&'. $url;
  5. ?>


Qualquer coisa posta ai

RB

Miuller,

Então, fiz como indicado, porém agora da a mensagem que não esta encontrando a classe PHPMailer;

Observação isto esta acontecendo após atualizar para versão 5.0



Fatal error: Uncaught Error: Class 'PHPMailerPHPMailerPHPMailer' not found in /var/www/html/centinel/app/lib/util/TMail.class.php:23 Stack trace: #0 /var/www/html/centinel/app/control/public/SendEmailPasswordForm.class.php(104): TMail->__construct() #1 [internal function]: SendEmailPasswordForm->onReset(Array) #2 /var/www/html/centinel/lib/adianti/control/TPage.php(51): call_user_func(Array, Array) #3 /var/www/html/centinel/lib/adianti/control/TPage.php(205): AdiantiControlTPage->run() #4 /var/www/html/centinel/lib/adianti/core/AdiantiCoreApplication.php(62): AdiantiControlTPage->show(Array) #5 /var/www/html/centinel/engine.php(34): AdiantiCoreAdiantiCoreApplication::run(true) #6 /var/www/html/centinel/engine.php(43): TApplication::run(true) #7 {main} thrown in /var/www/html/centinel/app/lib/util/TMail.class.php on line 23
MC

Boa tarde, observando a mensagem de erro observei que tem um problema na classe "/var/www/html/centinel/app/lib/util/TMail.class.php:23 ", tenho uma sugestão, faça um backup da classe TMail de seu projeto e crie um novo projeto, dai nesse novo projeto copia a classe TMail e substitui pela de seu projeto (a que vc fez o backup), e vê se soluciona o problema.
RB

Miuller,

Não funcionou funcionou, já tinha feito isto antes.
MC

Rubens, tente criar um novo projeto e adicione as p áginas deste exemplo no novo projeto, só para testar mesmo, dando uma olhada no projeto, notei que o envio em localhost tava funcionando mas quando coloquei o projeto no servidor estava dando um erro no envio, talvez seja o seu caso, vou postar as modificações que realizei.

 
  1. <?php
  2. if ($user)
  3. {
  4. try
  5. {
  6. TTransaction::open('permission');
  7. $preferences = SystemPreference::getAllPreferences(); //mais ou menos na linha 81
  8. $object = new SystemUser($user->id);
  9. $object->reset_pass = 'Y'; //parâmetro que diz que há uma requisição de reset de senha
  10. $object->data_pass = time(); //variável para validação de link
  11. $object->uid_pass = uniqid(rand(), true); //variável para validação de link
  12. $object->store();
  13. // monta parâmetros GET com criptografia
  14. $url = sprintf( 'id=%s&email=%s&uid=%s&key=%s',$object->id, md5($object->email), md5($object->uid_pass), md5($object->data_pass) );
  15. // monta link que será enviado para o email do usuário
  16. $prefs['url_base'] = sprintf( 'http%s://%s%s' , isset( $_SERVER[ 'HTTPS' ] ) ? 's' : null , $_SERVER[ 'HTTP_HOST' ] , dirname( $_SERVER[ 'REQUEST_URI' ] ) );
  17. $link = $prefs['url_base'] . '/index.php?class=ResetPasswordForm&'. $url;
  18. $mail = new TMail;
  19. $mail->setFrom(trim($preferences['mail_from']), 'Seu nome');
  20. $mail->addAddress(trim($object->email));
  21. $mail->setSubject('Cadastrar nova senha do seu projeto');
  22. if ($preferences['smtp_auth'])
  23. {
  24. $mail->SetUseSmtp();
  25. $mail->SetSmtpHost($preferences['smtp_host'], $preferences['smtp_port']);
  26. $mail->SetSmtpUser($preferences['smtp_user'], $preferences['smtp_pass']);
  27. }
  28. $mail->setHtmlBody("Você solicitou uma nova senha, por favor clique no link abaixo para cadastrar a nova senha!<br><br>
  29. Usuário: $object->login<br>
  30. <a href='$link'>Cadastrar nova senha</a><br><br>
  31. Atenciosamente,<br>
  32. <b>Seu nome</b>");
  33. $mail->addAddress($object->email);
  34. //$mail->SetUseSmtp();
  35. //$mail->SetSmtpHost($preferences['smtp_host'], $preferences['smtp_port']);
  36. //$mail->SetSmtpUser($preferences['smtp_user'], $preferences['smtp_pass']);
  37. $mail->send();
  38. new TMessage('info', 'Tudo certo, Você receberá um email com as instruções para redefinição de senha em: '.$object->email.'', new TAction(array('LoginForm', 'onClear')) );//redireciona para a página login no metódo onClear
  39. TTransaction::close();
  40. }
  41. catch (Exception $e)
  42. {
  43. // shows the exception error message
  44. new TMessage('error', $e->getMessage());
  45. // undo all pending operations
  46. TTransaction::rollback();
  47. }
  48. ?>

Testei e deu tudo certo, se vc não conseguir me fala no miuller.de.faria@gmail.com e te dou uma mão, flw
MC

Essa modificação acima é no arquivo SendEmailPassordForm, agora fiz outra modificação no arquivo ResetPaswordForm, conforme abaixo, atentar para a linha 29, pois ele verifica se o parâmetro email é igual ao da base de dados, essa é uma segurança a mais.

 
  1. <?php
 
  1. <?php
  2. class ResetPasswordForm extends TPage
  3. {
  4. protected $form; // form
  5. /**
  6. * Class constructor
  7. * Creates the page and the registration form
  8. */
  9. function __construct($param)
  10. {
  11. parent::__construct();
  12. $this->form = new TForm('form_reset_password');
  13. $this->form->class = 'tform';
  14. $valido = TRUE;
  15. if (isset($param['id']))
  16. {
  17. $id = $param['id'];
  18. $email = $param['email'];
  19. $uid = $param['uid'];
  20. $data = $param['key'];
  21. // checa se id do usuário existe
  22. TTransaction::open('permission');
  23. $user = new SystemUser($id);
  24. //verifica se o parametro email é igual ao da base de dados
  25. if( md5($user->email) !== $email)
  26. {
  27. throw new Exception(_t('User not found'));
  28. }
  29. TTransaction::close();
  30. if ($user instanceof SystemUser)
  31. {
  32. if ($user->active == 'N') //não permite que usuário inativo altera a senha
  33. {
  34. $valido = FALSE;
  35. }
  36. else if ($user->reset_pass != 'Y') //valida se há uma requisição de reset de senha
  37. {
  38. $valido = FALSE;
  39. }
  40. else if (md5($user->email) != $email) //valida se email criptografado é igual ao do link acessado
  41. {
  42. $valido = FALSE;
  43. }
  44. else if (md5($user->uid_pass) != $uid) //valida se uid criptografado é igual ao do link acessado
  45. {
  46. $valido = FALSE;
  47. }
  48. else if (md5($user->data_pass) != $data) //valida se email criptografado é igual ao do link acessado
  49. {
  50. $valido = FALSE;
  51. }
  52. }
  53. else
  54. {
  55. $valido = TRUE;
  56. }
  57. }
  58. // se não é valido, leva usuário para tela de login
  59. // echo 'o parametro 222 id é:'. $id;
  60. if ( $valido = FALSE)
  61. {
  62. new TMessage('error', _t('User not found'), new TAction(array($this, 'onLogin')) );
  63. return false;
  64. }
  65. else
  66. {
  67. $table = new TTable;
  68. $table->width = '100%';
  69. // creates the form
  70. $this->form->style = 'max-width: 450px; margin:auto; margin-top:120px;';
  71. $this->form->add($table);
  72. // create the form fields
  73. $usuario = new THidden('usuario');
  74. $usuario->setValue($user->id);
  75. $senha = new TPassword('senha');
  76. $senha_confirm = new TPassword('senha_confirm');
  77. $senha->placeholder = 'Nova senha';
  78. $senha_confirm->placeholder = 'Digite novamente';
  79. // define the sizes
  80. $senha->setSize('70%', 40);
  81. $senha_confirm->setSize('70%', 40);
  82. $senha->style = 'height:35px; font-size:14px;float:left;border-bottom-left-radius: 0;border-top-left-radius: 0;';
  83. $senha_confirm->style = 'height:35px; font-size:14px;float:left;border-bottom-left-radius: 0;border-top-left-radius: 0;';
  84. $row=$table->addRow();
  85. $row->addCell( new TLabel('Nova senha') )->colspan = 2;
  86. $row->class='tformtitle';
  87. $senha->setLabel('Senha');
  88. $senha_confirm->setLabel('Senha_confirm');
  89. $senha->addValidation('Senha', new TRequiredValidator);
  90. $senha_confirm->addValidation('Senha_confirm', new TRequiredValidator);
  91. $locker = '<span style="float:left;width:35px;margin-left:45px;height:35px;" class="input-group-addon"><span class="glyphicon glyphicon-lock"></span></span>';
  92. $container1 = new TElement('div');
  93. $container1->add($locker);
  94. $container1->add($senha);
  95. $container1->add($usuario);
  96. $container2 = new TElement('div');
  97. $container2->add($locker);
  98. $container2->add($senha_confirm);
  99. $row=$table->addRow();
  100. $row->addCell($container1)->colspan = 2;
  101. $row=$table->addRow();
  102. $row->addCell($container2)->colspan = 2;
  103. // create an action button (save)
  104. $recuperar_button=new TButton('recovery');
  105. // define the button action
  106. $recuperar_button->setAction(new TAction(array($this, 'onReset')), _t('Save'));
  107. $recuperar_button->class = 'btn btn-success';
  108. $recuperar_button->style = 'font-size:18px;width:90%;padding:10px';
  109. $row=$table->addRow();
  110. $row->class = 'tformaction';
  111. $cell = $row->addCell( $recuperar_button );
  112. $cell->colspan = 2;
  113. $cell->style = 'text-align:center';
  114. $this->form->setFields(array($senha, $senha_confirm, $usuario, $recuperar_button));
  115. }
  116. // add the form to the page
  117. parent::add($this->form);
  118. }
  119. public function onLogin()
  120. {
  121. TApplication::loadPage('LoginForm', '', $_REQUEST);
  122. }
  123. /**
  124. * Reset password
  125. */
  126. public function onReset()
  127. {
  128. try
  129. {
  130. $data = $this->form->getData('StdClass');
  131. $this->form->validate();
  132. //valida se senha e senha de confirmação foram digitadas iguais
  133. if ($data->senha != $data->senha_confirm)
  134. {
  135. new TMessage('error', 'Senhas não conferem, por favor digite as senhas iguais.');
  136. }
  137. else
  138. {
  139. // se passou pelas validações, instancia o usuário
  140. TTransaction::open('permission');
  141. $object = new SystemUser($data->usuario);
  142. if ($object)
  143. {
  144. // grava nova senha e limpa os outros campos, assim é garantido que o link é válido para apenas uma utilização
  145. $object->password = md5($data->senha);
  146. $object->reset_pass = 'SIM';
  147. $object->data_pass = NULL; //não entendi a necessidade deste campo
  148. $object->uid_pass = date("Y-m-d H:i:s"); //acho que este campo seria para salvar a data da password modificada
  149. $object->store();
  150. new TMessage('info', 'Senha alterada com sucesso!', new TAction(array('LoginForm', 'onClear')) );
  151. }
  152. else
  153. {
  154. new TMessage('error', _t('User not found'), new TAction(array($this, 'onLogin')) );
  155. }
  156. TTransaction::close();
  157. }
  158. }
  159. catch (Exception $e)
  160. {
  161. new TMessage('error',$e->getMessage());
  162. TTransaction::rollback();
  163. }
  164. }
  165. }
  166. ?>


DC

Muito obrigado!!! Muito útil, também não entendi porque não vem no admin padrão do framework!

Funcionou perfeitamente, vale ressaltar alguns detalhes óbvios para alguns porém não para todos:

É necessário configurar corretamente o formulário de preferencias do sistema, e para tanto, deve-se ter uma conta de email valida na hospedagem. não consegui colocar pra funcionar com o gmail...

Na classe ResetPasswordForm Alterar o salvamento da nova senha para md5 pois como o amigo citou, ele reformulou o sistema de login dele e o login padrão do framework utiliza md5 para geração e verificação das senhas.
linha : 149
$object->password = password_hash($data->senha, PASSWORD_DEFAULT);
para :
$object->password = md5($data->senha);


feito isso tudo funcionou perfeitamente!
CF

Henrique, boa noite.
Como ficaria a parte abaixo do código usando a versão 5 do framework?

// $recupera = new TActionLink( _t('Forgot your password?'), new TAction(array($this, 'onRecovery')) );
// $recupera->style = 'font-size:16px;width:90%;padding:10px';
// $row = $table->addRow();
// $cell = $row->addCell( $recupera );

Abs
Cleber
MC

No meu caso, eu coloquei tudo em um panel, e abaixo coloquei o link para o método onRecovery

 
  1. <?php
  2. $login->placeholder = _t('User');
  3. $password->placeholder = _t('Password');
  4. $unidade->placeholder = 'Unidade';
  5. $regiao->placeholder = 'Região';
  6. $user = '<span style="float:left;margin-left:44px;height:35px;" class="login-avatar"><span class="glyphicon glyphicon-user"></span></span>';
  7. $locker = '<span style="float:left;margin-left:44px;height:35px;" class="login-avatar"><span class="glyphicon glyphicon-lock"></span></span>';
  8. $unit = '<span style="float:left;margin-left:44px;height:35px;" class="login-avatar"><span class="fa fa-university"></span></span>';
  9. $reg = '<span style="float:left;margin-left:44px;height:35px;" class="login-avatar"><span class="fa fa-university"></span></span>';
  10. $logo = '<img class="img-responsive" style="margin-left:170px" width="100" height="100" src="tim.php?src=app/images/logo.png&w=100&h=100&zc=2" >'; //adicionei um logo acima do panel
  11. $panel = new TPanelGroup;
  12. $panel->style = 'height:120px;max-width:460px;margin:auto';
  13. $panel->id ='imagem';
  14. $panel->add($logo); //adicionei o logo no panel para personalisar
  15. $recupera = new TActionLink('Esqueceu a senha?', new TAction(array($this, 'onRecovery')) );
  16. $recupera->style = 'font-size:12px;width:100%;padding:2px;text-align: center; float:left';
  17. $this->form->addFields( [$user, $login] );
  18. $this->form->addFields( [$locker, $password] );
  19. $this->form->addFields( [$unit, $regiao] );
  20. $this->form->addFields( [$reg, $unidade] );
  21. if (!empty($ini['general']['multiunit']) and $ini['general']['multiunit'] == '1')
  22. {
  23. $unit_id = new TCombo('unit_id');
  24. $unit_id->setSize('70%');
  25. $unit_id->style = 'height:35px;font-size:14px;float:left;border-bottom-left-radius: 0;border-top-left-radius: 0;';
  26. $this->form->addFields( [$unit, $unit_id] );
  27. $login->setExitAction(new TAction( [$this, 'onExitUser'] ) );
  28. }
  29. $btn = $this->form->addAction(_t('Log in'), new TAction(array($this, 'onLogin')), '');
  30. $btn->class = 'btn btn-primary';
  31. $btn->style = 'height: 40px;width: 90%;display: block;margin: auto;font-size:17px;';
  32. $wrapper = new TElement('div');
  33. $wrapper->style = 'margin:auto; margin-top:20px;max-width:460px;';
  34. $wrapper->id = 'login-wrapper';
  35. $wrapper->add( $panel);
  36. $wrapper->add($this->form);
  37. $wrapper->add($recupera);
  38. // add the form to the page
  39. parent::add($wrapper);
  40. }
  41. public function onRecovery()
  42. {
  43. AdiantiCoreApplication::gotoPage('SendEmailPasswordForm');
  44. }
  45. ?>
DC

Caso alguém precise dessa solução funcionando, dentro das minhas limitações de tempo eu passo todo o código funcionando em conjunto com o Admin padrão do adianti, já que estou muito grato por ter pego a solução aqui que me poupou muito trabalho e estudo, ajudarei com prazer outros usuários!
RB

Davidson,

Se puder disponibilizar ó código, serei muito grato.

Segue meu e-mail :rubensbispo29@gmail.com
DC

Vou mandar por e-mail e colocar aqui até amanhã de manhã! Mas adianto que a única modificação necessária foi a que postei acima na geração da nova senha em md5 , até lá, tente seguir o tutorial desde o início do post, caso não consiga, terá de estudar um pouco mais o framework.