Laravel 5.3 Redefine "reset email" blade template - laravel-5.3

How to customize the path of the reset email blade template in Laravel 5.3?
The template used is: vendor/laravel/framework/src/Illuminate/Notifications/resources/views/email.blade.php
I'd like to build my own.
Also, how to change the text of this email predefined in: vendor/laravel/framework/src/Illuminate/Auth/Notifications/ResetPassword.php
public function toMail()
{
return (new MailMessage)
->line([
'You are receiving this email because we received a password reset request for your account.',
'Click the button below to reset your password:',
])
->action('Reset Password', url('password/reset', $this->token))
->line('If you did not request a password reset, no further action is required.');
}

To change template you should use artisan command php artisan vendor:publish it will create blade templates in your resources/views/vendor directory. To change text of email you should override the sendPasswordResetNotification method on your User model. This is described here https://laravel.com/docs/5.3/passwords in Reset Email Customization section.
You must add new method to your User model.
public function sendPasswordResetNotification($token)
{
$this->notify(new ResetPasswordNotification($token));
}
and use your own class for notification instead ResetPasswordNotification.
UPDATED: for #lewis4u request
Step by step instruction:
To create a new Notification class, you must use this command line php artisan make:notification MyResetPassword . It will make a new Notification Class 'MyResetPassword' at app/Notifications directory.
add use App\Notifications\MyResetPassword; to your User model
add new method to your User model.
public function sendPasswordResetNotification($token)
{
$this->notify(new MyResetPassword($token));
}
run php artisan command php artisan vendor:publish --tag=laravel-notifications After running this command, the mail notification templates will be located in the resources/views/vendor/notifications directory.
Edit your MyResetPassword class method toMail() if you want to. It's described here https://laravel.com/docs/5.3/notifications
Edit your email blade template if you want to. It's resources/views/vendor/notifications/email.blade.php
Bonus: Laracast video: https://laracasts.com/series/whats-new-in-laravel-5-3/episodes/9
PS: Thanks #Garric15 for suggestion about php artisan make:notification

I wanted to elaborate on a very helpful Eugen’s answer, but didn’t have enough reputation to leave a comment.
In case you like to have your own directory structure, you don’t have to use Blade templates published to views/vendor/notifications/... When you create a new Notification class and start building your MailMessage class, it has a view() method that you can use to override default views:
/**
* Get the mail representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->view('emails.password_reset');
// resources/views/emails/password_reset.blade.php will be used instead.
}

In Addition to the above answer for Laravel 5.6, here it is easier to pass variables in an array to your custom email template.
/**
* Get the mail representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
$url = url('/invoice/'.$this->invoice->id);
return (new MailMessage)
->subject('Invoice Paid')
->markdown('emails.password_reset', ['url' => $url]);
}
https://laravel.com/docs/5.6/notifications

Related

Routing changes when upgrading easyadmin bundle symfony

I am in charge of upgrading easyadmin bundle on an app that was previously built using symfony v4.4.19. Initially we had: easycorp/easyadmin-bundle v2.3.12. Then, we decided to upgrade the easyadmin bundle to v3 because we faced some issues when enabling/disabling a boolean property from the list view.
When I was using the v2 :
php bin/console debug:router showed a route called easyadmin with a path /myworkshop/ .
I had no Dashboard controller nor NecklaceCrudController, I simply had a controlller called
AccessoriesController.php with several actions like deleteAction that is executed when the user
deletes an entity, editAction when the user edits an entity, SearchAction ...
In the deleteAction there is this line of code:
return $this->redirect($this->generateUrl('easyadmin', array('action' => 'list', 'entity'=> $this->entity['name'])));
so the url would become something like this
/myworkshop/?action=list&entity=necklace
To open the easy admin interface I have to click on a menu link whose link is :
->createItem('Visit my workshop', ['route' => 'easyadmin']);
When I open this interface /references, I get the list of the different entities in my app, if
I select one, I see the list view and I can edit one entity successfully but I cannot
enable/disable boolean properties from the list view as mentioned earlier.
We specified /myworkshop instead of /admin in app>config>routing.yml
# easy admin
easy_admin_bundle:
resource: "#myShop/Controller/AccessoriesController.php"
type: annotation
prefix: /myworkshop```
- A custom css was successfully employed in : app>config>config.yml
easy_admin:
design:
assets:
css:
- 'bundles/css/easyadmin.css
When I upgraded to v3:
php bin/console debug:router showed a route called myshop_admin_dashboard_index (which was
automatically generated) with a path /easyadmin.
Dashboard controller and NecklaceCrudController were created, The DashboardController only has configureCrud() and configureMenuItems() functions. The latter contains the links yield MenuItem::linkToCrud . Question 1 : In version 2 no menu links where created explicitely like here, so I was wondering how was the complete list of entities correctly showing up on my application interface?
In DashboardController there is no index () function nor a route nor a link just configureCrud() and configureMenuItems() functions.
I want to keep the AccessoriesController.php with his several actions but now, with the new route and path, it is completely being ignored. Question 2 : Is there something that I have to change in the generateUrl part? can someone give me an example of what this will become if I opt for adminUrlgenerator like I read in the documentation?
The routing.yml file remains the same however, my easyadmin interface appears only when visiting this link /easyadmin instead of /myworkshop . Question 3: I want to keep the /workshop url , what should I do in addition to keeping the routing.yml as it is now?
I wish we could change myshop_admin_dashboard_index to easyadmin and /easyadmin to my /myworkshop as it was in version 2, because there are many parts in AccessoriesController where I use $this->generateUrl('easyadmin',
Question 4: The css is no longer applicable any idea why? could be related to the AccessoriesController that is not currently being taken into account.
Well, you can add the index method to your dashboard controller with route annotation to change the route
class DashboardController extends AbstractDashboardController
{
/**
* #Route("/myworkshop", name="admin")
*/
public function index(): Response
{
return $this->render('dashboard/index.html.twig');
}
You can add any route to easyadmin menu like this
class DashboardController extends AbstractDashboardController
{
public function configureMenuItems(): iterable
{
yield MenuItem::linktoRoute('Some Route', 'fa fa-info', 'route_name_here');
#...
}
}
You can add any CSS/js file too
class DashboardController extends AbstractDashboardController
{
public function configureAssets(): Assets
{
return Assets::new()
->addCssFile('build/admin.css')
->addJsFile('build/admin.js')
;
}
}

Trying to add social media links to Drupal website

I'm trying to add social media icons to my drupal website and followed this guide to try and install them. The module seems to install fine, however when I try to configure it to add different links on clicking save it brings me to a webpage that simply says The website encountered an unexpected error. Please try again later.
I've tried uninstalling/reinstalling the plugin and that didn't seem to do anything. Are there some permissions I'm missing in my set up? I'm pretty much brand new to drupal
Edit: The url of the error is admin/structure/block/manage/socialmedialinks?destination=/node
and some of the error log:
TypeError: Argument 2 passed to Egulias\EmailValidator\EmailValidator::isValid() must implement interface Egulias\EmailValidator\Validation\EmailValidation, bool given,
It seems the social_media_links drupal module version 8.x-2.6 has a bug when it checks the validity of email addresses.
There is an issue in the module's issue queue for it HERE.
There is a patch attached to the issue (attached below):
diff --git a/src/Plugin/SocialMediaLinks/Platform/Email.php b/src/Plugin/SocialMediaLinks/Platform/Email.php
index 007e59f..2926d47 100755
--- a/src/Plugin/SocialMediaLinks/Platform/Email.php
+++ b/src/Plugin/SocialMediaLinks/Platform/Email.php
## -4,7 +4,6 ## namespace Drupal\social_media_links\Plugin\SocialMediaLinks\Platform;
use Drupal\social_media_links\PlatformBase;
use Drupal\Core\Form\FormStateInterface;
-use Egulias\EmailValidator\EmailValidator;
use Drupal\Core\Url;
/**
## -29,9 +28,9 ## class Email extends PlatformBase {
*/
public static function validateValue(array &$element, FormStateInterface $form_state, array $form) {
if (!empty($element['#value'])) {
- $validator = new EmailValidator();
+ $validator = \Drupal::service('email.validator');
- if (!$validator->isValid($element['#value'], TRUE)) {
+ if (!$validator->isValid($element['#value'])) {
$form_state->setError($element, t('The entered email address is not valid.'));
}
}

SilverStripe framework only - how to handle 404

I'm using just the framework without the CMS module for the first time. When I visit the app via a URL that is not handled by a controller/action, I just get a page with the text "No URL rule was matched". This results from Director::handleRequest() not matching any controllers to the url segments... Or "Action 'ABC' isn't available on class XYZController."
I'd like to direct any unmached requests to a controller equivalent of a nice 404 page. What is the correct or best way to do this?
The error templates are only included in the CMS. The framework just returns the HTTP response code with a message in plain text.
I've just started on my own framework-only project too and this is my solution:
[routes.yml]
---
Name: rootroutes
---
Director:
rules:
'': 'MyController'
'$URLSegment': 'MyController'
[MyController]
class MyController extends Controller {
private static $url_handlers = array(
'$URLSegment' => 'handleAction',
);
public function index() {
return $this->httpError(404, "Not Found");
}
/**
* Creates custom error pages. This will look for a template with the
* name ErrorPage_$code (ie ErrorPage_404) or fall back to "ErrorPage".
*
* #param $code int
* #param $message string
*
* #return SS_HTTPResponse
**/
public function httpError($code, $message = null) {
// Check for theme with related error code template.
if(SSViewer::hasTemplate("ErrorPage_" . $code)) {
$this->template = "ErrorPage_" . $code;
} else if(SSViewer::hasTemplate("ErrorPage")) {
$this->template = "ErrorPage";
}
if($this->template) {
$this->response->setBody($this->render(array(
"Code" => $code,
"Message" => $message,
)));
$message =& $this->response;
}
return parent::httpError($code, $message);
}
}
[ErrorPage.ss]
<h1>$Code</h1>
<p>$Message</p>
You can also create more specific error templates using ErrorPage_404.ss, ErrorPage_500.ss etc.
Without updating the routes as previously mentioned, there's a module that I've been recently working on which will allow regular expression redirection mappings, hooking into a page not found (404). This has been designed to function with or without CMS present :)
https://github.com/nglasl/silverstripe-misdirection
It basically makes use of a request filter to process the current request/response, appropriately directing you towards any mappings that may have been defined.

Get original request info after a forward in Symfony 2

I have created a custom Error 403 page using the access_denied_url parameter in security.yml: when showing the new page I want to tell people that the problem happened when trying to access page xxxxx.
$request->headers->get('referer') is empty.
/**
* #Route("/forbidden", name="error_403")
* #Template()
*/
public function error403Action(Request $request)
{
return array('referer' => $request->headers->get('referer'));
}
How can I get information about the original Request (the one that resulted in this forward)?
It got solved calling {{ app.request.server.get('PHP_SELF') }} directly in the Twig template.

Behat authenticate Symfony2 user

I'm using Behat in Symfony2 / Doctrine2. Now, I have this scenario that boils down to the fact that "if I'm logged in and I go to /login, I shoud go to / instead":
#login
Scenario: Go to the login page while being logged in
Given I am logged in
When I go to "/login"
Then I should be on "/"
For the #login, I created the following:
/**
* #BeforeScenario #login
*/
public function loginUser()
{
$doctrine = $this->getContainer()->get('doctrine');
$userRepository = $doctrine->getRepository('MyTestBundle:User');
$user = $userRepository->find(1); // 1 = id
$token = new UsernamePasswordToken($user, NULL, 'main', $user->getRoles());
$this->getContainer()->get('security.context')->setToken($token);
}
In the "when I go to /login" code (the controller gets called), the token seems gone (not what I intended):
/**
* #Route("/login", name="login")
*/
public function loginAction()
{
$token = $this->get('security.context')->getToken();
$fd = fopen('/tmp/debug.log', 'a');
fwrite($fd, $token);
// prints 'AnonymousToken(user="anon.", authenticated=true, roles="")'
...
But in the FeatureContext, it seems to stick around (the way I hoped it would work). In the "Given I am logged in":
/**
* #Given /^I am logged in$/
*/
public function iAmLoggedIn()
{
$token = $this->getContainer()->get('security.context')->getToken();
$fd = fopen('/tmp/debug.log', 'a');
fwrite($fd, $token);
// prints 'UsernamePasswordToken(user="admin", authenticated=true, roles="ROLE_ADMIN")'
...
I run behat like this:
app/console -e=test behat
I also did this in the controller to be sure it's test:
fwrite($fd, $this->get('kernel')->getEnvironment());
// prints 'test'
Any clue how to authenticate a user? I will have to test a lot of admin pages, so it would be nice if I could hook the login into #BeforeSuite, #BeforeFeature (or #BeforeScenario ...) so that I don't get blocked.
(Suggestions on disabling the authentication mechanism for testing, or a way to stub/mock a user are also welcome.)
Oh my. It doesn't work because the DIC inside your FeatureContext isn't shared with your app - your app has separate kernel and DIC. You can get it through Mink. Or, you can simply do it right way :-)
Right way means, that every part of behavior, that is observable by the enduser, should be described inside *.feature, not inside FeatureContext. It means, that if you want to login a user, you should simply describe it with steps (like: "i am on /login", "and i fill in username ...", "i fill in password" and stuf). If you want to do it in multiple times - you should create a metastep.
Metasteps are simply steps, that describe multiple other steps, for example - "i am logged in as everzet". You could read bout them here: http://docs.behat.org/guides/2.definitions.html#step-execution-chaining
Here is an solution for login with OAuth I've used. After number of times of searching for the answer and landing on this page I thought it would be great to share the solution. Hopefully it will help someone.
Background: Symfony2 App using HWIOAuthBundle, hooked up to some OAuth2 provider.
Problem: How do I implement Given I'm logged in when Behat context in not shared with Symfony context?
Solution:
HWIOAuthBundle uses #buzz service for all API calls to OAuth providers. So all you need to do is replace Buzz client with your implementation which doesn't call external services, but returns the result straight away. This is my implementation:
<?php
namespace Acme\ExampleBundle\Mocks;
use Buzz\Client\ClientInterface;
use Buzz\Message\MessageInterface;
use Buzz\Message\RequestInterface;
class HttpClientMock implements ClientInterface
{
public function setVerifyPeer()
{
return $this;
}
public function setTimeout()
{
return $this;
}
public function setMaxRedirects()
{
return $this;
}
public function setIgnoreErrors()
{
return $this;
}
public function send(RequestInterface $request, MessageInterface $response)
{
if(preg_match('/\/oauth2\/token/', $request->getResource()))
{
$response->setContent(json_encode([
'access_token' => 'valid',
'token_type' => 'bearer',
'expires_in' => 3600
]));
}
elseif(preg_match('/\/oauth2\/me/', $request->getResource()))
{
$response->setContent(json_encode([
'id' => 1,
'username' => 'doctor',
'realname' => 'Doctor Who'
]));
}
else throw new \Exception('This Mock object doesn\'t support this resource');
}
}
Next step is to hijack the class used by HWIOAuthBundle/Buzz and replace it with the implementation above. We need to do it only for test environment.
# app/config/config_test.yml
imports:
- { resource: config_dev.yml }
parameters:
buzz.client.class: Acme\ExampleBundle\Mocks\HttpClientMock
And finally, you need to set require_previous_session to false for test environment - therefore I suggest to pass it as parameter.
# app/config/security.yml
security:
firewalls:
secured_area:
oauth:
require_previous_session: false
Now you can implement your step like this.
Specification:
Feature: Access restricted resource
Scenario: Access restricted resource
Given I'm logged in
When I go to "/secured-area"
Then I should be on "/secured-area"
And the response status code should be 200
Implementation:
<?php
/**
* #Given /^I\'m logged in$/
*/
public function iMLoggedIn()
{
$this->getSession()->visit($this->locatePath('/login/check-yourOauthProvider?code=validCode'));
}
The code you're passing is not relevant, anything you pass will be OK as it's not being checked. You can customise this behaviour in HttpClientMock::send method.
http://robinvdvleuten.nl/blog/handle-authenticated-users-in-behat-mink/ is simple, clean article on how to create a login session and set the Mink session cookie so that the Mink session is logged in. This is much better than using the login form every time to login a user.
It’s ok to call into the layer “inside” the UI layer here (in symfony: talk to the models).
And for all the symfony users out there, behat recommends using a Given step with a tables arguments to set up records instead of fixtures. This way you can read the scenario all in one place and make sense out of it without having to jump between files:
Given there are users:
| username | password | email |
| everzet | 123456 | everzet#knplabs.com |
| fabpot | 22#222 | fabpot#symfony.com |

Resources