Yii2 - How can I remove unnecessary information from log file? - yii2

I am trying to log messages in Yii2 which are then emailed to my specified email address.
config file web.php contains:
'mail' => [
'class' => 'yii\log\EmailTarget',
'categories' => ['mail'],
'logVars' => [],
'mailer' => 'mailer',
'message' => [
'from' => ['user#example.com'],
'to' => ['user1#example.com'],
'subject' => 'Log message',
],
],
I am logging message like this:
Yii::info('Log message example','mail');
After successful execution, I am receiving mail like this:
2018-07-31 09:01:12 [127.0.0.1][user#example.com][-][info][mail] Log message example
So what I am trying to do is that I want to remove unwanted information like IP address, User Name etc. from this messages and at the end what I want is
2018-07-31 09:01:12 Log message example

You can remove first three parts from log by setting prefix property:
'mail' => [
'class' => 'yii\log\EmailTarget',
'categories' => ['mail'],
'logVars' => [],
'prefix' => function () {
return '';
},
'mailer' => 'mailer',
'message' => [
'from' => ['user#example.com'],
'to' => ['user1#example.com'],
'subject' => 'Log message',
],
],
Last two parts (level and category) are hardcoded, you need to extend EmailTarget and override formatMessage() to remove them.

You can set this either in your configuration file web.php or in your code.
Yii::$app->log->targets['test']->prefix = function (){
return null;
};
or
'mail' => [
'class' => 'yii\log\EmailTarget',
'categories' => ['mail'],
'logVars' => [],
'mailer' => 'mailer',
'prefix' => function () {
return null;
},
'message' => [
'from' => ['user#example.com'],
'to' => ['user1#example.com'],
'subject' => 'Log message',
],
],

Related

Update swiftmailer configuration based on user input

I need to update SwiftMailer configuration based on user input, particularly, the user may decide to send emails using SMTP protocol or store them locally (in an specific filesystem's folder). The user will use a view to decide the option, then the controller catch the decision and update a session var. The current approach is to read that session var from config/web.php and then choose the appropriate configuration.
I am not sure whether web.php is loaded just once during the application execution, in fact I can not check if the session is active and then get the info from the var. I'm not sure what approach may be the appropriate one.
This is my config/web.php:
<?php
use yii\web\Session;
$params = require __DIR__ . '/params.php';
$db = require __DIR__ . '/db.php';
$session = Yii::$app->session;
if($session->isActive){
$mailTransport = Yii::app()->session->get('emailtransport');
}
else{ //session is not started
$mailTransport = 'local';
}
if($mailTransport=='local'){
$config = [
'id' => 'basic',
'basePath' => dirname(__DIR__),
'bootstrap' => ['log'],
'aliases' => [
'#bower' => '#vendor/bower-asset',
'#npm' => '#vendor/npm-asset',
],
'components' => [
'request' => [
// !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
'cookieValidationKey' => 'a4ARdWYauHJ-UEAvVfagzk0LTyT_KEuZ',
],
'cache' => [
'class' => 'yii\caching\FileCache',
],
'user' => [
'identityClass' => 'app\models\User',
'enableAutoLogin' => true,
],
'errorHandler' => [
'errorAction' => 'site/error',
],
'mailer' => [
'class' => 'yii\swiftmailer\Mailer',
'viewPath' => '#app/mail',
'htmlLayout' => 'layouts/main-html', //template to Send Emails Html based
'textLayout' => 'layouts/main-text', //template to Send Emails text based
'messageConfig' => [
'charset' => 'UTF-8',
'from' => ['clients#ok.com' => 'OK Premiun Clients'],
], //end of messageConfig
// send all mails to a file by default. You have to set
// 'useFileTransport' to false and configure a transport
// for the mailer to send real emails.
'useFileTransport' => true,
//'useFileTransport' => false,
'transport' => [
'class' => 'Swift_SmtpTransport',
'host' => 'smtp.gmail.com',
'username' => 'ok#gmail.com',
'password' => "password",
'port' => '465',
'encryption' => 'ssl',
],
],
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,
'targets' => [
[
'class' => 'yii\log\FileTarget',
'levels' => ['error', 'warning'],
],
],
],
'db' => $db,
/*
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
],
],
*/
],
'params' => $params,
];
}//end of if loop
else{
$config = [
'id' => 'basic',
'basePath' => dirname(__DIR__),
'bootstrap' => ['log'],
'aliases' => [
'#bower' => '#vendor/bower-asset',
'#npm' => '#vendor/npm-asset',
],
'components' => [
'request' => [
// !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
'cookieValidationKey' => 'a4ARdWYauHJ-UEAvVfagzk0LTyT_KEuZ',
],
'cache' => [
'class' => 'yii\caching\FileCache',
],
'user' => [
'identityClass' => 'app\models\User',
'enableAutoLogin' => true,
],
'errorHandler' => [
'errorAction' => 'site/error',
],
'mailer' => [
'class' => 'yii\swiftmailer\Mailer',
'viewPath' => '#app/mail',
'htmlLayout' => 'layouts/main-html', //template to Send Emails Html based
'textLayout' => 'layouts/main-text', //template to Send Emails text based
'messageConfig' => [
'charset' => 'UTF-8',
'from' => ['ok#namesilo.com' => 'OK Premiun Clients'],
], //end of messageConfig
// send all mails to a file by default. You have to set
// 'useFileTransport' to false and configure a transport
// for the mailer to send real emails.
'useFileTransport' => false,
'transport' => [
'class' => 'Swift_SmtpTransport',
'host' => 'smtp.gmail.com',
'username' => 'ok#gmail.com',
'password' => "password",
'port' => '465',
'encryption' => 'ssl',
],
],
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,
'targets' => [
[
'class' => 'yii\log\FileTarget',
'levels' => ['error', 'warning'],
],
],
],
'db' => $db,
/*
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
],
],
*/
],
'params' => $params,
];
}//end of if else loop
Because the session is never reached on web.php the emails are always stored locally.
What you are trying to do requires you to instantiate the yii\swiftmailer\Mailer class from within your code and set the transport by calling setTransport(), rather than using it via defining under the components in the config file.
I will give you an example where i will use the smtp.gmail.com as the host for the transport and send an email
public function actionTest(){
//instantiate the mailer
$mailer = new \yii\swiftmailer\Mailer(
[
'useFileTransport' => false
]
);
//set the transport params
$mailer->setTransport(
[
'class' => 'Swift_SmtpTransport',
'host' => 'smtp.gmail.com',
'username' => 'username',
'password' => 'password',
'port' => '587',
'encryption' => 'tls'
]
);
//to email
$to = "someemail#gmail.com";
//email subject
$subject = "this is a test mail";
//email body
$body ="Some body text for email";
//send the email
$mailer->compose()->setTo($to)->setFrom(
[
'support#site.com' => 'Site Support'
]
)->setTextBody($body)->setSubject($subject)->send();
}
Another solution is this: I did some changes using my Model Class:
public function sendMail($transport, $view, $subject, $params = []){
//set layout params
\Yii::$app->mailer->getView()->params['userName'] = $this->username;
if($transport == 'local'){
\Yii::$app->mailer->useFileTransport=true;
}
else{
\Yii::$app->mailer->useFileTransport=false;
}
$result = \Yii::$app->mailer->compose([
'html' => 'views/' . $view . '-html',
'text' => 'views/' . $view . '-text',
], $params)->setTo([$this->email => $this->username])
->setSubject($subject)
->send();
//reset layout params
\Yii::$app->mailer->getView()->params['userName'] = null;
return $result;
} //end of sendMail
It helped me to simplify my web.php, it wasn't necessary to use session vars.

Relation between belongsTo and hasMany in cakephp 3.4.9

I need to save user and invoice data at the same time. When I am submitting the form, the user data is saving, but the invoice table isn't. When I print the request data, I get the following:
object(Cake\ORM\Entity) {
'u_firstname' => 'John',
'u_lastname' => 'den',
'u_email' => 'john#man.com',
'u_phone' => '123',
'password' => '123',
'membership_id' => (int) 1,
'Invoices' => [
'inv_membership_name' => 'basic1',
'inv_membership_cost' => '55',
'inv_purchase_date' => '2017-07-11'
],
'id' => (int) 19,
'[new]' => false,
'[accessible]' => [
'*' => true
],
'[dirty]' => [],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Users'
}
I am not able to save the Invoice part. I define InvoicesTable.php
public function initialize(array $config)
{
parent::initialize($config);
$this->setTable('invoices');
$this->setDisplayField('id');
$this->setPrimaryKey('id');
$this->belongsTo('Users', [
'foreignKey' => 'user_id',
'joinType' => 'INNER'
]);
}
and UsersTable like this:
$this->hasMany('Invoices', [
'foreignKey' => 'user_id',
'joinType' => 'INNER'
]);
I use the following code to save data:
$user = $this->Users->newEntity();
if ($this->request->is('post')) {
$user = $this->Users->patchEntity($user, $this->request->data(), [
'associated' => [
'Invoices'
]
]);
if ($this->Users->save($user)) {
$this->Flash->success(__('The user has been saved.'));
}else{
$this->Flash->error(__('The user could not be saved. Please, try again.'));
}
}
Please suggest what I'm missing. Or how I should debug.
When you print request data invoices array should be something like this:
object(Cake\ORM\Entity) {
'invoices' => [
[0] => Array
(
['inv_membership_name'] => 'basic1',
['inv_membership_cost'] => '55',
['inv_purchase_date'] => '2017-07-11'
),
[1] => array('...') // if you have multiple records
],
For that change input name to something like this in your form:
invoices[]['inv_membership_name']
invoices[]['inv_membership_cost']
invoices[]['inv_purchase_date']
See here Saving HasMany Associations in CakePhp3.
You are close to the solution but you have the naming conventions wrong in the form. The correct name would be invoices.0.inv_membership_name
invoices.1.inv_membership_name etc. I included the creating inputs for associated data link from the cookbook.
echo $this->Form->control('invoices.0.inv_membership_name');
https://book.cakephp.org/3.0/en/views/helpers/form.html#creating-inputs-for-associated-data

How to use basic authentication for json requests and form authentication for html requests with Cakephp 3?

I need to filter json requests and allow basic authentication for those requests, while allowing only form authentication for html requests. When I filter the requests in my initialize function in AppController.php:
if ($this->request->is('json')) {
$this->loadComponent('Auth', [
'authorize' => ['Controller'],
'authenticate' => [
'Basic' => [
'fields' => ['username' => 'email', 'password' => 'password'],
'contain' => ['Districts']
]
]
]);
} else {
$this->loadComponent('Auth', [
'authorize' => ['Controller'],
'authenticate' => [
'Form' => [
'fields' => ['username' => 'email', 'password' => 'password'],
'contain' => ['Districts']
]
],
'loginAction' => [
'controller' => 'Users',
'action' => 'login'
],
'logoutRedirect' => [
'controller' => 'Users',
'action' => 'login'
]
]);
}
The json request creates and stores a session allowing the user to then access the rest of the site, including html requests because it has an authorized session. I struggled trying to find what was causing this and eventually found that you have to explicitly declare that the storage medium for the Basic Authentication method as 'Memory'. I'll post the correct code in my answer below.
This question is similar to this one for cakephp 2: CakePHP form authentication for normal requests with basic authentication for JSON
You have to explicitly declare that the Basic Authentication uses Memory for the storage medium, or else it will create a session. Here is the correct code:
if ($this->request->is('json')) {
$this->loadComponent('Auth', [
'authorize' => ['Controller'],
'authenticate' => [
'Basic' => [
'fields' => ['username' => 'email', 'password' => 'password'],
'contain' => ['Districts']
]
],
'storage' => 'Memory'
]);
} else {
$this->loadComponent('Auth', [
'authorize' => ['Controller'],
'authenticate' => [
'Form' => [
'fields' => ['username' => 'email', 'password' => 'password'],
'contain' => ['Districts']
]
],
'loginAction' => [
'controller' => 'Users',
'action' => 'login'
],
'logoutRedirect' => [
'controller' => 'Users',
'action' => 'login'
]
]);
}

Yii2 Email How to set sender name

i using Mailer to send email, so i have problem about sender name
This is my config
'mailer' => [
'class' => 'yii\swiftmailer\Mailer',
'useFileTransport' => false,
'messageConfig' => [
'charset' => 'UTF-8',
'from' => ['admin#app.com' => 'App Sender Name'],
],
'transport' => [
'class' => 'Swift_MailTransport',
],
],
So it's not work. I goto inbox and only email showed.
And i need show as
Example:
it's work when i update setFrom() method.
Ex: $mailer->setFrom(['email#app.com' => 'App Name']). And here is config Yii2 for send by PHP mail and with sender name
'mailer' => [
'class' => 'yii\swiftmailer\Mailer',
'useFileTransport' => false,
'messageConfig' => [
'charset' => 'UTF-8',
'from' => ['admin#app.com' => 'App Sender Name'],
],
'transport' => [
'class' => 'Swift_MailTransport',
],
],
and send email
Yii::$app->mailer->compose()
->setTo('client#email.com')
->setFrom(['admin#app.com' => 'App Name'])
....
Every component and sub-component in yii2 is configurable by dependency injection, this case is same, eg:
'transport' => [
'class' => 'Swift_SmtpTransport',
'host' => 'smtp.gmail.com',
'username' => 'noreply.yourcompany#gmail.com',
'password' => '123456',
'port' => '465',
'encryption' => 'ssl',
],

Is it possible to use both CakePHP default auth with the hybrid Auth?

Right now I am working on project where we need facebook and twitter login.
And I have also configure the login in my project but now its not authenticate with simple username and password.
This is AppController.php
class AppController extends Controller
{
public function initialize()
{
$this->loadComponent('Flash');
$this->loadComponent('Auth', [
'loginRedirect' => [
'controller' => 'Users',
'action' => 'index'
],
'logoutRedirect' => [
'controller' => 'Users',
'action' => 'login'
],
'authenticate' => [
'ADmad/HybridAuth.HybridAuth'
]
]);
}
}
Its working with only facebook and twitter login,and please let me know how to store user in database after successful login in fb or twitter.
Change above function to this.
public function initialize()
{
$this->loadComponent('Flash');
$this->loadComponent('Auth', [
'authenticate' => [
'Form',
'ADmad/HybridAuth.HybridAuth'
],
'loginRedirect' => [
'controller' => 'Users',
'action' => 'index',
'plugin' => false
],
'logoutRedirect' => [
'controller' => 'Users',
'action' => 'login',
'plugin' => false
]
]);
}
The point here is: just add "Form" under "authenticate".
CakePHP can handle many login types simultaneously.
To store your users in the database just follow the plugin's instructions to set up the users and social_profiles tables.
Then simply add your regular user logic (UsersController + login.ctp, etc, etc).
Here's how I load the Auth component to achieve this:
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'fields' => [
'username' => 'email',
'password' => 'password'
],
],
'ADmad/HybridAuth.HybridAuth' => [
'fields' => [
'provider' => 'provider',
'openid_identifier' => 'openid_identifier',
'email' => 'email'
],
'profileModel' => 'ADmad/HybridAuth.SocialProfiles',
'profileModelFkField' => 'user_id',
'hauth_return_to' => null
],
],
]);