I have SMS notifications that get sent via Nexmo. They are working, but I've changed how I set the recipient's phone number and need to override the default by using the routeNotificationForNexmo method.
However, it seems no matter what I do with that method, the app ignores it and nothing changes. I can change other things in this file, such as the SMS message contents, and see the changes in new messages sent from Nexmo.
Why isn't routeNotificationForNexmo working? Is there something else that could be overriding this?
Here is my whole /Notifications/TeamMessage.php file:
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\NexmoMessage;
class TeamMessage extends Notification implements ShouldQueue
{
use Queueable;
protected $custom_message;
protected $short_code;
protected $reply_to;
protected $reply_to_name;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct($custom_message, $short_code, $channels, $reply_to, $reply_to_name)
{
$this->custom_message = $custom_message;
$this->short_code = $short_code;
$this->channels = $channels;
$this->reply_to = $reply_to;
$this->reply_to_name = $reply_to_name;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return $this->channels; //
}
/**
* Route notifications for the Nexmo channel.
*
* #return string
*/
public function routeNotificationForNexmo()
{
$number = $this->phone_number_country . $this->phone_number;
$number = preg_replace("/[^0-9]/","", $number); //Strips all non-numbers
return $number;
}
/* Send SMS */
public function toNexmo($notifiable)
{
return (new NexmoMessage)
->from(00000000000)
->content($this->custom_message . " \n\n Unsubscribe at http://" . env('TL_DOMAIN') . '/s/' . $this->short_code );
}
/**
* Get the mail representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->replyTo($this->reply_to, $this->reply_to_name)
->line($this->custom_message)
->line('--')
->line("To unsubscribe, go to http://" . env('TL_DOMAIN') . '/s/' . $this->short_code);
//->action('Notification Action', 'https://laravel.com');
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}
I'm on Laravel 5.3, running locally via Docker (Laradock). I've tried stopping and restarting all the containers.
Solved. Not sure where I got the idea to put the routeNotificationForNexmo() function inside the notification class. It belongs in the the User class, User.php file.
Related
I have a WordPress running application, which I would like to access using a separate interface that uses Laravel 5.8.(don't worry about the hashing)
As such, instead of cloning passwords back and forth, I would like to use the user_email and user_pass columns in the Laravel User model instead.
I have tried what the official docs say :
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
/**
* Handle an authentication attempt.
*
* #param \Illuminate\Http\Request $request
*
* #return Response
*/
public function authenticate(Request $request)
{
$credentials = $request->only('user_email', 'user_pass');
if (Auth::attempt($credentials)) {
// Authentication passed...
return redirect()->intended('dashboard');
}
}
}
I then edited the blade files, but no avail. Any pointers?
Laravel provides a way to change the default columns for auth (email, password) by overriding some functions.
In your User model add this function that overrides the default column for password:
App/User.php
/**
* Get the password for the user.
*
* #return string
*/
public function getAuthPassword()
{
return $this->user_pass;
}
And, in your LoginController change from email to user_email
App/Http/Controllers/Auth/LoginController.php
/**
* Get the login username to be used by the controller.
*
* #return string
*/
public function username()
{
return 'user_email';
}
Now you have overridden the default columns used by Laravel's Auth logic. But you are not finished yet.
LoginController has a function that validates the user's input and the password column is hardcoded to password so in order to change that, you also need to add these functions in LoginController:
App/Http/Controllers/Auth/LoginController.php
/**
* Validate the user login request.
*
* #param \Illuminate\Http\Request $request
* #return void
*
* #throws \Illuminate\Validation\ValidationException
*/
protected function validateLogin(Request $request)
{
$request->validate([
$this->username() => 'required|string',
'user_pass' => 'required|string',
]);
}
/**
* Get the needed authorization credentials from the request.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
protected function credentials(Request $request)
{
return $request->only($this->username(), 'user_pass');
}
Next step is to create a custom Provider, let's call it CustomUserProvider that will be used instead of the default one EloquentUserProvider and where you will override the password field.
App/Providers/CustomUserProvider.php
<?php
namespace App\Providers;
class CustomUserProvider extends EloquentUserProvider
{
/**
* Retrieve a user by the given credentials.
*
* #param array $credentials
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByCredentials(array $credentials)
{
if (empty($credentials) ||
(count($credentials) === 1 &&
array_key_exists('user_pass', $credentials))) {
return;
}
// First we will add each credential element to the query as a where clause.
// Then we can execute the query and, if we found a user, return it in a
// Eloquent User "model" that will be utilized by the Guard instances.
$query = $this->createModel()->newQuery();
foreach ($credentials as $key => $value) {
if (Str::contains($key, 'user_pass')) {
continue;
}
if (is_array($value) || $value instanceof Arrayable) {
$query->whereIn($key, $value);
} else {
$query->where($key, $value);
}
}
return $query->first();
}
/**
* Validate a user against the given credentials.
*
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #param array $credentials
* #return bool
*/
public function validateCredentials(UserContract $user, array $credentials)
{
$plain = $credentials['user_pass'];
return $this->hasher->check($plain, $user->getAuthPassword());
}
}
Now that you extended the default provider you need to tell Laravel to use this one instead of EloquentUserProvider. This is how you can do it.
App/Providers/AuthServiceProvider.php
/**
* Register any authentication / authorization services.
*
* #return void
*/
public function boot()
{
$this->registerPolicies();
$this->app->auth->provider('custom', function ($app, $config) {
return new CustomUserProvider($app['hash'], $config['model']);
});
}
Finally update the config information config/auth.php and change the driver from eloquent to custom (that's how I named it above; you can change it to whatever you want). So the config/auth.php file should have this bit:
'providers' => [
'users' => [
'driver' => 'custom',
'model' => App\User::class,
],
],
Hope it helps!
Regards
It would be up and working, If you can just use sessions here instead of using Auth::attempt just like working on core PHP.
I am trying to make a login verification step with Behat.
I am cleaning the database and execute a Fixture loader before the tests start so the users are in the database (I checked this and it is correct).
For some reason after the inputs are filled (with correct data) submit fails with invalid credentials. If I enter the data again manually (username and password) on the same open session of the opened browser it works.
See updates at the bottom for a solution
feature
Feature: Login to application
In order to access admin area
I need to login as an administrator
#javascript
Scenario Outline: Login as <administrator>
When I am logged in as an administrator with <administrator> credentials
Then I wait for "10" seconds
...
Examples:
| administrator |
| admin |
LoginContext
use SensioLabs\Behat\PageObjectExtension\Context\PageObjectContext;
use Behat\Behat\Context\Context;
require_once __DIR__.'/Resources/Users/ymlParser/UserCredentialsParser.php';
/**
* Class LoginContext
*/
class LoginContext extends PageObjectContext implements Context
{
/**
* #param $administrator
*
* #When /^I am logged in as an administrator with ([^"]*) credentials$/
*/
public function iAmLoggedInAsAnAdministrator($administrator)
{
$this->getPage('Login Page')->open();
$this->getPage('Login Page')->enterLoginDetails(UserCredentialsParser::load($administrator));
$this->getPage('Login Page')->submitLoginForm();
//$this->getPage('Login Page')->showLoginSuccessful();
//$this->getPage('Login Page')->isLoggedInUserAdmin(true);
}
}
UserCredentialParser
require_once __DIR__.'/../../../../../../vendor/autoload.php';
use Symfony\Component\Yaml\Yaml;
use Symfony\Component\Yaml\Parser;
use Symfony\Component\Yaml\Exception\ParseException;
/**
* Class UserCredentialsParser
*/
class UserCredentialsParser
{
/**
* #param $fileName
*
* #return null|stdClass
*/
public static function load($fileName)
{
$oUser = null;
$filePath = __DIR__.'/../usersCredentials/'.$fileName.'.yml';
if (file_exists($filePath)) {
try {
$userData = Yaml::parse(file_get_contents($filePath));
$oUser = new stdClass();
$oUser->username = $userData['username'];
$oUser->password = $userData['password'];
} catch (ParseException $e) {
printf("Unable to parse YAML string %s", $e->getMessage());
}
}
return $oUser;
}
}
Login Page
namespace Page;
use SensioLabs\Behat\PageObjectExtension\PageObject\Page;
/**
* Class LoginPage
* #package Page
*/
class LoginPage extends Page
{
protected $path = '/login';
/**
* #param $oUser
*
* #return mixed
*/
public function enterLoginDetails($oUser)
{
return $this->getElement('Login Form')->enterLoginDetails($oUser);
}
/**
* Submit registration form
*
* #return mixed
*/
public function submitLoginForm()
{
return $this->getElement('Login Form')->submitLoginForm();
}
}
Login Form
namespace Page\Element;
use SensioLabs\Behat\PageObjectExtension\PageObject\Element;
/**
* Class LoginForm
* #package Page\Element
*/
class LoginForm extends Element
{
/**
* #var string
*/
protected $selector = 'form';
/**
* Fill up the login form
*
* #param $oUser
*/
public function enterLoginDetails($oUser)
{
$this->fillField('_username', $oUser->username);
$this->fillField('_password', $oUser->password);
}
/**
* Submit registration form button
*/
public function submitLoginForm()
{
$this->pressButton('_submit');
}
}
UPDATE:
The problem was that for any kind of free text there was a tab inserted after the text in the input boxes (probably by the driver - in my case chrome). When i switched to Firefox it worked without any problem and no extra white spaces were added.
I've updated chrome-driver to the newest version currently at 2.24 and it fixed the problem for me.
The problem was that for any kind of free text there was a tab inserted after the text in the input boxes (probably by the driver - in my case chrome). When i switched to Firefox it worked without any problem and no extra white spaces were added.
UPDATE: As for 2nd November 2016 I updated to the latest CHROME driver and it seems it works as expected.
I am using the PayPal IPN simulator here: https://developer.paypal.com/webapps/developer/applications/ipn_simulator
to send information to an application built with symfony2 and payum bundle (older version of symfony and bundle).
It is definitely getting to the application at the notify URL (so not a firewall issue) because a record is stored in the database with the payment name and the date. However there are no 'details' stored.
However, if I use a Rest Client to POST to a URL with data, as suggested here: https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNIntro/#id08CKFJ00JYK
Then the record is stored with payment name and date and details!!
Is this an issue with the IPN simulator? I'm really not sure what is going on here, maybe I could try and log the request object somehow?
#hsb1007 I think this was what I used finally. But I'm pretty sure there was some settings on the paypal side which was the main issue. I just remember doing lots and lots of testing and waiting
<?php
namespace LabIT\CMSBundle\EventListener;
use Buzz\Client\ClientInterface;
use Exception;
use LabIT\CMSBundle\Entity\Payments\DetailsInterface;
use LabIT\CMSBundle\Helper\ApiHelper;
use LabIT\CMSBundle\Helper\PaymentHelper;
use LabIT\CMSBundle\Helper\SubscriptionHelper;
use LabIT\CMSBundle\Helper\UserHelper;
use Payum\Core\Action\PaymentAwareAction;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Core\Request\Notify;
use Payum\Paypal\Ipn\Api;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
class PaymentListener extends PaymentAwareAction
{
/**
* #var UserHelper
*/
protected $userHelper;
/**
* #var PaymentHelper
*/
protected $paymentHelper;
/**
* #var ApiHelper
*/
protected $apiHelper;
/**
* #var SubscriptionHelper
*/
protected $subscriptionHelper;
/**
* #var LoggerInterface
*/
protected $logger;
/**
* #var
*/
protected $buzz;
/**
* #var
*/
protected $sandbox;
/**
* #var
*/
protected $paypalValidation;
/**
* #param UserHelper $userHelper
* #param PaymentHelper $paymentHelper
* #param ApiHelper $apiHelper
* #param SubscriptionHelper $subscriptionHelper
* #param LoggerInterface $logger
* #param ClientInterface $buzz
* #param $sandbox
* #param $paypalValidation
*/
public function __construct(
UserHelper $userHelper,
PaymentHelper $paymentHelper,
ApiHelper $apiHelper,
SubscriptionHelper $subscriptionHelper,
LoggerInterface $logger,
ClientInterface $buzz,
$sandbox,
$paypalValidation
) {
$this->userHelper = $userHelper;
$this->paymentHelper = $paymentHelper;
$this->apiHelper = $apiHelper;
$this->subscriptionHelper = $subscriptionHelper;
$this->logger = $logger;
$this->buzz = $buzz;
$this->sandbox = $sandbox;
$this->paypalValidation = $paypalValidation;
}
/**
* {#inheritDoc}
*
* This is where all the IPNs from paypal get processed, acts in some ways like a controller
*
* #param Notify $request
*/
public function execute($request)
{
$data = $_POST;
// would be better to get this dynamically. It is the payment name defined in config,
// but also forms part of the url set in the paypal notification backend
$paymentName = 'post_a_job_with_paypal'; // todo maybe get this from the url
$this->logger->notice('IPN received');
// validate with paypal so it stops notifying (do this first because needs to be done within 30 seconds)
if (true === $this->paypalValidation) {
$this->validateWithPaypal($this->getPaypalArray());
}
$notificationDetails = $this->paymentHelper->createPaymentNotification($paymentName, $data);
// todo other inspections of data? check email?
$user = $this->paymentHelper->getNotificationUser($notificationDetails, $data);
// these are only done for individual transactions //TODO STORE TRANSACTIONS IN TABLE?
if (isset($data['txn_id'])) {
$this->paymentHelper->getTransactionProcessed($data['txn_id']); // so don't process more than once //TODO ADD BACK IN AFTER ADDING TRANSACTION CLASS
$this->subscriptionHelper->determineUserMembership($user, $notificationDetails); // automatically demote if payment fails
$this->apiHelper->sendPaymentNotifications($user, $notificationDetails); // notify affiliates
$this->paymentHelper->setTransactionProcessed($data['txn_id']); //set transaction to processed //TODO ADD BACK IN AFTER ADDING TRANSACTION CLASS
}
// handle recurring payment data (recurring payment info, but not recurring payment transaction
if (isset($data['recurring_payment_id']) && !isset($data['txn_id'])) {
$this->paymentHelper->setRecurringPaymentStatus($data);
// cron job will determine user membership level because needs to check timestamp
}
}
/**
* {#inheritDoc}
*/
public function supports($request)
{
return $request instanceof Notify;
}
/**
* Send data back to paypal so paypal knows it was delivered successfully
*
* #param array $data
*
* #throws Exception
*/
protected function validateWithPaypal(array $data)
{
$this->logger->notice('I am here');
$options = array();
$options['sandbox'] = $this->sandbox;
$api = new Api($this->buzz, $options);
// Verify the IPN via PayPal
if (Api::NOTIFY_VERIFIED !== $api->notifyValidate($data)) {
$this->logger->notice('paypal validation UNSUCCESSFUL');
throw new Exception('Invalid IPN');
}
$this->logger->notice('paypal validation SUCCESSFUL');
return;
}
/**
* #return array
*/
protected function getPaypalArray()
{
// Read POST data
// reading posted data directly from $_POST causes serialization
// issues with array data in POST. Reading raw POST data from input stream instead.
$raw_post_data = file_get_contents('php://input');
$raw_post_array = explode('&', $raw_post_data);
$myPost = array();
foreach ($raw_post_array as $keyval) {
$keyval = explode ('=', $keyval);
if (count($keyval) == 2)
$myPost[$keyval[0]] = urldecode($keyval[1]);
}
return $myPost;
}
I have registered users in my application, when a user wants to unsubscribe or is locked in the database is marked as Locked
ArrayCollection all need to be changed or do I have to make a new function for each ArrayCollection?
for Example:
this is arrayCollection
/**
* Get like
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getLike()
{
return $this->like;
}
When I user is locked i have to create a new function like:
/**
* Get like2
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getLike2()
{
//USE CRITERIA OR
$out = array();
foreach ($this->like as $like2) {
if (!getUser()->getLocked()) {
$out[] = $like2;
}
}
}
Or I can use the "default" ArrayCollection with some modifications
I get this exeption when I submit my form:
Found the public method "addRemote", but did not find a public "removeRemote" on class App\CoreBundle\Entity\Scene
The weired think is that the remove method exist ...
But i wrote it myself (When I did php app/console doctrine:generate:entities) doctrine didn't generated it. Did I make something wrong ?
/**
* #var array $remote
*
* #ORM\Column(name="remote", type="array", nullable=true)
*/
private $remote;
/**
* Set remote
*
* #param array $remote
* #return Scene
*/
public function addRemote($value, $key=null) {
if($key!=null){
$this->remote[$key] = $value;
}else{
$this->remote[] = $value;
}
return $this;
}
/**
* Remove remote
*/
public function removeRemote(){
unset($this->remote);
}
I allso tried:
/**
* Remove remote
*/
public function removeRemote($key=null){
if($key!=null && array_key_exists($key, $this->remote)){
unset($this->remote[$key]);
}
unset($this->remote);
return $this;
}
You have bigger problem than this; you are abusing your forms :)
Add.. and Remove... methods should be used for relations, not columns as per your code. Also, both add and remove methods must accept parameter that will be either added or removed.
If you still need an array, than getRemotes() method should return key=>value array. Adder and remover will later get that key, based on what user have picked in choice form type.