Framework PHP: Symfony2.6
Problem: I would like to add the following functionality to FosUserBundle:
"The admin can re-send the registration confirmation email to a specific user" (in the admin section of the website).
I've already built the "user details" page where the admin can see all the information entered in the registration form and if the the user is enabled/confirmed. If the user is not enabled I will add a button to re-send the confirmation email.
Another solution is to to display a link to the user, after he tries to login with credentials that are not confirmed. Here is a similar question (that unfortunately has no feedback and it's not very clear to me and only covers the second approach): https://stackoverflow.com/questions/25204877/re-sending-confirmation-email-fosuserbundle
Can you please point me towards the easiest and quickest solution?
I know this an old question but today I came across the same problem and I found a simpler solution. Maybe this also helpful to others:
Simply ask the FOSUserBundle for its mailer and use it to re-send the message:
$mailer = $this->get('fos_user.mailer');
$mailer->sendConfirmationEmailMessage($user);
That's it! This will re-send an exact copy of the confirmation mail, since the same FOSUserBundle code is used. No need to manually re-create the message.
Here is a shot at what it takes. Assumptions:
in config.yml, fos_user.service.mailer: fos_user.mailer.twig_swift
user email is known
Controller
/**
* #Route("/remind")
*
*/
class RemindController extends Controller
{
/**
* #Route("/{email}")
* #Template()
*/
public function remindAction($email)
{
$user = $this->get('fos_user.user_manager')->findUserByEmail($email);
$url = $this->generateUrl('fos_user_registration_confirm', array('token' => $user->getConfirmationToken()), true);
$message = \Swift_Message::newInstance()
->setSubject('Registration confirmation')
->setFrom('admin#acmedemo.com')
->setTo($email)
->setContentType('text/html')
->setBody(
$this->renderView(
"AcmeDemoBundle:Remind:email.html.twig", array(
'user' => $user,
'confirmationUrl' => $url))
)
;
$sent = $this->get('mailer')->send($message);
return ['user' => $user,
'url' => $url,
'success' => $sent ? 'Yes' : 'No'];
}
}
Minimalist AcmeDemoBundle:Remind:remind.html.twig template
{{ user.email }}<br>
{{ url }}<br>
{{ success }}
Minimalist AcmeDemoBundle:Remind:email.html.twig template
Please confirm your registration by visiting this link
Related
I want to have the registration after the auth, so the admin could create a user.I tried to find something about that but all examples are in Laravel 5s and the methodes of the controllers aren't the same.
Have you an idea,please?
If you want to create a new user as an admin, you just have to use the User::create([ ... ]) method. Remeber to put an email and a hashed password, such that the user can log in.
Example for user creation:
User::create([
'email' => 'foo#bar.com',
'password' => bcrypt('foobar'),
]);
If you want to remove the registration for guests, you should remove the RegistrationController and change the route to Auth::routes(['register' => false]);.
Its common for administrators to create users for their application. You have to be carefull with the creating of user. I personally dont want a administrator that creates a password for my account.
I would do the following:
1.Create a user with a temporary password.
public function create(){
$user = new User();
$user->name = $request->name;
$user->email = $request->email;
$user->password = Hash::make(Str::random(32));
$user->regToken = Str::random(32);
}
2.Save the user with a temporary password and send a registration email to the new user so he can change his password.
$user->notify(new MailCompleteRegistrationNotification($user->regToken, $user->email));
I use mail notifications to send the registration mail. in the notification i have the following tomail function.
public function toMail($notifiable)
{
return (new MailMessage)
->subject("Subject")
->line('Welcome to the application. We created a account for you but still need some details.')
->line('If you want to complete the registration you can click the button below:')
->action('Complete registration', url('registration', [$this->regToken, $this->email]));
}
I would recommend doing the user creation like that
In my Node.js application, I followed the Mailgun docs https://documentation.mailgun.com/en/latest/quickstart-sending.html#send-with-smtp-or-api for sending an email similar to the following:
mailgun.messages().send(data, (error, body) => {
console.log(body);
// body is {id: some_mailgun_built_id, message: 'Queued. Thank You'}
// which I store the body.id in my database
});
The issue I am faced with is how can I access that same Mailgun response when I send an email with Laravel? The Mailgun docs don't provide any examples showing how to retrieve that data.
This is how I am sending emails with Laravel:
\Mail::to($recipients)->send(
// this just renders my blade file which formats my email
new SendEmail($email);
);
// ?? How to get Message was sent object here
If anyone knows of any solution it would be greatly appreciated!
Hello and Welcome to SO!
Laravel has two events for the emails as explained in the official documentation: MessageSending and MessageSent
You can follow the events official documentation in order to listen for these specific events:
/**
* The event listener mappings for the application.
*
* #var array
*/
protected $listen = [
'Illuminate\Mail\Events\MessageSending' => [
'My\Email\Listener',
],
'Illuminate\Mail\Events\MessageSent' => [
'My\Other\Listener',
],
];
You will receive as input the Swift_message which contains a header that is the Mailgun ID you're looking for. Let's have a look at the MailgunTransport#send source code in order to understand what's going on behind the scenes:
/**
* {#inheritdoc}
*/
public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null)
{
// [...]
$response = $this->client->request(
'POST',
"https://{$this->endpoint}/v3/{$this->domain}/messages.mime",
$this->payload($message, $to)
);
$message->getHeaders()->addTextHeader(
'X-Mailgun-Message-ID', $this->getMessageId($response) // <-- HERE LARAVEL SETS THE MESSAGE ID
);
// [...]
}
Looking for the same key in your listener you can recover the message ID that mailgun assigned to your e-mail. Unfortunately you can't access the entire Mailgun response but with the API you can easily retrieve your message.
I want to test login method inside the controller, because I am a beginner in writing the tests and because I think that it is wise to test login method. if anyone has any objections let me know. Also I have found many solutions to test the login, but in cakephp 2.
/**
* Log the user into the application
*
* #return void|\Cake\Network\Response
*/
public function login()
{
if ($this->request->is("post")) {
$user = $this->Auth->identify();
if ($user) {
$user = $this->addUsersAssociated($user);
$user = $this->addUserDetailsToSession($user);
$this->Auth->setUser($user);
return $this->redirect($this->Auth->redirectUrl());
}
// User not identified
$this->Flash->error(__d('cockpit', 'Your username or password is incorrect'));
}
}
For example I want to test it when someone comes with wrong and right username/password.
I am a total beginner and I would also like if someone can point me into the right direction (where is the quickest way to learn how to do this). I would like from someone who is independent to learn how to test my code. In other words I don't want to go to official documentation. (Have been there already)
Figure out what happens, respectively what should happen in the different situations, and create tests with proper expectations.
Like on successful login, the user data is being set in the auth storage and a redirect header is being set, that's something you could test. Likewise on a non-successful login attempt, no user data is stored, no redirect header is being set, and a flash message is being rendered.
All these things can easily be checked in a controller integration test using either helper assertion methods, or even manually via the provided session and response objects, check:
$_requestSession
$_response
assertSession()
assertRedirect()
assertRedirectContains()
assertResponse()
assertResponseContains()
etc...
Here's two very basic examples:
namespace App\Test\TestCase\Controller;
use Cake\TestSuite\IntegrationTestCase;
class AccountControllerTest extends IntegrationTestCase
{
public function testLoginOk()
{
$this->enableCsrfToken();
$this->enableSecurityToken();
$this->post('/account/login', [
'username' => 'the-username',
'password' => 'the-password'
]);
$expected = [
'id' => 1,
'username' => 'the-username'
];
$this->assertSession($expected, 'Auth.User');
$expected = [
'controller' => 'Dashboard',
'action' => 'index'
];
$this->assertRedirect($expected);
}
public function testLoginFailure()
{
$this->enableCsrfToken();
$this->enableSecurityToken();
$this->post('/account/login', [
'username' => 'wrong-username',
'password' => 'wrong-password'
]);
$this->assertNull($this->_requestSession->read('Auth.User'));
$this->assertNoRedirect();
$expected = __d('cockpit', 'Your username or password is incorrect');
$this->assertResponseContains($expected);
}
}
See also
Cookbook > Testing > Controller Integration Testing
Cookbook > Testing > Controller Integration Testing > Testing Actions That Require Authentication
Cookbook > Testing > Controller Integration Testing > Assertion methods
I am beginner in Laravel. Currently I am learning this framework. My curent Laravel version is 5.3.
I am scaffolding my auth by using php artisan make:auth All are working fine. Also I configured gmail smtp in my .env file and mail.php in config directgory. All are perfectly working. But I saw by-default the forgot password email subject is going Reset Password. I want to change that.
I saw some blog. I found some blog. I have implement that in my site. But same output coming.
I followed these links -
https://laracasts.com/discuss/channels/general-discussion/laravel-5-password-reset-link-subject
https://laracasts.com/discuss/channels/general-discussion/reset-password-email-subject
https://laracasts.com/discuss/channels/laravel/how-to-override-message-in-sendresetlinkemail-in-forgotpasswordcontroller
You can change your password reset email subject, but it will need some extra work. First, you need to create your own implementation of ResetPassword notification.
Create a new notification class insideapp\Notifications directory, let's named it ResetPassword.php:
<?php
namespace App\Notifications;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\MailMessage;
class ResetPassword extends Notification
{
public $token;
public function __construct($token)
{
$this->token = $token;
}
public function via($notifiable)
{
return ['mail'];
}
public function toMail($notifiable)
{
return (new MailMessage)
->subject('Your Reset Password Subject Here')
->line('You are receiving this email because we received a password reset request for your account.')
->action('Reset Password', url('password/reset', $this->token))
->line('If you did not request a password reset, no further action is required.');
}
}
You can also generate the notification template using artisan command:
php artisan make:notification ResetPassword
Or you can simply copy-paste the above code. As you may notice this notification class is pretty similar with the default Illuminate\Auth\Notifications\ResetPassword. You can actually just extend it from the default ResetPassword class.
The only difference is here, you add a new method call to define the email's subject:
return (new MailMessage)
->subject('Your Reset Password Subject Here')
You may read more about Mail Notifications here.
Secondly, on your app\User.php file, you need to override the default sendPasswordResetNotification() method defined by Illuminate\Auth\Passwords\CanResetPassword trait. Now you should use your own ResetPassword implementation:
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\Notifications\ResetPassword as ResetPasswordNotification;
class User extends Authenticatable
{
use Notifiable;
...
public function sendPasswordResetNotification($token)
{
// Your your own implementation.
$this->notify(new ResetPasswordNotification($token));
}
}
And now your reset password email subject should be updated!
Hope this help!
You may easily modify the notification class used to send the password reset link to the user. To get started, override the sendPasswordResetNotification method on your User model. Within this method, you may send the notification using any notification class you choose. The password reset $token is the first argument received by the method, See the Doc for Customization
/**
* Send the password reset notification.
*
* #param string $token
* #return void
*/
public function sendPasswordResetNotification($token)
{
$this->notify(new ResetPasswordNotification($token));
}
Hope this helps!
In Laravel 5.7 the default implementation is similar to this:
return (new MailMessage)
->subject(Lang::getFromJson('Reset Password Notification'))
->line(Lang::getFromJson('You are receiving this email because we received a password reset request for your account.'))
->action(Lang::getFromJson('Reset Password'), url(config('app.url').route('password.reset', $this->token, false)))
->line(Lang::getFromJson('This password reset link will expire in :count minutes.', ['count' => config('auth.passwords.users.expire')]))
->line(Lang::getFromJson('If you did not request a password reset, no further action is required.'));
All you have to do is change your locale from config/app.php for example to ro, then in your resources/lang, create a file ro.json similar to this:
{
"Reset Password Notification": "Viața Medicală CMS :: Resetare parolă",
"Hello!": "Salut,",
"You are receiving this email because we received a password reset request for your account.": "Primești acest email deoarece am primit o solicitare de resetare a parolei pentru contul tău.",
"Reset Password": "Reseteză parola",
"This password reset link will expire in :count minutes.": "Acest link va expira în :count de minute.",
"If you did not request a password reset, no further action is required.": "Dacă nu ai solicitat resetarea parolei, nu este necesară nicio altă acțiune.",
"Regards": "Toate cele bune",
"Oh no": "O, nu",
"Whoops!": "Hopa!",
"If you’re having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser: [:actionURL](:actionURL)": "Dacă nu reușești să dai click pe butonul de \":actionText\", dă copy-paste la URL-ul de mai jos în browser:\n [:actionURL](:actionURL)"
}
It will translate both the subject (first key) and the mail body.
UPDATE for Laravel 6.*
This can be also used for VerifyEmail.php notification.
Laravel 8
In AuthServiceProvider.php
Add these code.
ResetPassword::toMailUsing(function ($notifiable, $url) {
return (new MailMessage)
->subject(Lang::get('Reset Password Notification'))
->line(Lang::get('You are receiving this email because we received a password reset request for your account.'))
->action(Lang::get('Reset Password'), $url)
->line(Lang::get('This password reset link will expire in :count minutes.', ['count' => config('auth.passwords.' . config('auth.defaults.passwords') . '.expire')]))
->line(Lang::get('If you did not request a password reset, no further action is required.'));
});
To everyone asking how to update the Hello, Regards, and subcopy text:
php artisan vendor:publish (option 11)
then in views/vendor/notifications/email.blade.php
In this file there will be the text like Hello, wich you can change by changing:
for example:
line 9# #lang('Hallo!, Hei!, Bonjour!, Guten Tag!, Geia!')
You can create a custom function that will create the reset password token like this.
$user = User::where('email', 'example#name.com' )->first();
$password_broker = app(PasswordBroker::class); //so we can have dependency injection
$token = $password_broker->createToken($user); //create reset password token
$password_broker->emailResetLink($user, $token, function (Message $message) {
$message->subject('Custom Email title');
});//send email.
a note about this answer :
https://stackoverflow.com/a/40574428/9784378
you can copy vendor file functions and paste them into Resetpassword.php file
that you have created in notification folder .
Just add the line:
->subject('New Subjetc')
in the the method toMail of the file Illuminate\Auth\Notifications\ResetPassword
like this:
public function toMail($notifiable)
{
return (new MailMessage)
->subject('New Subjetc')
->line('You are receiving this email because we received a password reset request for your account.')
->action('Restaurar Contraseña', url(config('app.url').route('password.reset', $this->token, false)))
->line('If you did not request a password reset, no further action is required.');
}
In my controller I have a function to login a user.
In case the login was successful I can simply use return Redirect::back().
My problem starts when the credentials are incorrect and I want to redirect with a flash message.
I know I can chain the with method to the Redirect, but that would send the data to the specific view, and NOT to the layout, where the login HTML lies.
I could load a view like so:
$this->layout
->with('flash',$message)
->content = View::make('index');
But I need to redirect back to the referring page.
Is it possible to redirect while passing data to the layout?
The Laravel Validator class handles this quite well....
The way I usually do it is to add a conditional within my layout/view in blade...
{{ $errors->has('email') ? 'Invalid Email Address' : 'Condition is false. Can be left blank' }}
This will display a message if anything returns with an error..
Then in your validation process you have...
$rules = array(check credentials and login here...);
$validation = Validator::make(Input::all(), $rules);
if ($validation->fails())
{
return Redirect::to('login')->with_errors($validation);
}
This way...when you go to the login page, it will check for errors regardless of submission, and if it finds any, it displays your messages.
EDITED SECTION
For dealing with the Auth class..
This goes in your view...
#if (Session::has('login_errors'))
<span class="error">Username or password incorrect.</span>
#endif
Then in your auth...something along these lines..
$userdata = array(
'username' => Input::get('username'),
'password' => Input::get('password')
);
if ( Auth::attempt($userdata) )
{
// we are now logged in, go to home
return Redirect::to('home');
}
else
{
// auth failure! lets go back to the login
return Redirect::to('login')
->with('login_errors', true);
}