can't send attachment with cakeemail - cakephp

I can't send an attachment with the email. I don't get an error and I do send the message so the email works but no attachment.
Is my filepath not correct as the file exists in this file? Is it because I am using windows with file paths?
This is just a test email below to see if this function actually works for an attachment but it isn't working for me. I checked other answers and this seems to be the way to construct this.
http://book.cakephp.org/2.0/en/core-utility-libraries/email.html
public function sendEmailattach($to,$message,$subject,$attach) {
$Email = new CakeEmail();
$Email->config('gmail3');
$Email->filePaths = array('D:\crm5\app\Attachments');
$Email->attachments =array('Ch9-anna tax.docx');
$to='jXXXXX#gmail.com';//testing real email account
// $Email->from( array('admin#a.com.au' => 'A'));
$Email->from( array('jxxxxx#gmail.com' => 'test'));
$Email->to($to);
$Email->subject($subject);
$Email->send();
// $Email->send($message);
}//public
UPDATE
Tried all 3 methods and no error and no attachment?
// $Email->attachments('D:\AA-website design\crm5\app\Attachments\Ch9-anna tax.docx') ;
$Email->attachments(array('Ch9-anna tax.docx' => array(
'file' => 'D:\AA-website design\crm5\app\Attachments\Ch9-anna tax.docx',
) ));
// $Email->attachments(array('D:\AA-website design\crm5\app\Attachments\Ch9-anna tax.docx'));

$email = new CakeEmail('default');
$attachment = [
'file.pdf' => [
'file' => '/my/absolute/path/on/server/file.pdf',
'mimetype' => 'application/pdf',
'contentId' => uniqid()
]
];
$variables = ['emailHeader' => 'Hello'];
$email->attachments($attachment);
$email->from(['info#example.com' => 'Example'])
->to('recipient#example.com')
->subject('Subject')
->template('template')
->emailFormat('both')
->viewVars($variables)
->send();

Just read the manual instead just looking at the links, honestly I have doubt's you read it at all:
http://book.cakephp.org/2.0/en/core-utility-libraries/email.html#sending-attachments
It clearly shows that attachments is a method and not a property. It even has examples of what the method accepts.

Related

drupal_mail() returns true but unable to send mail

I have written a drupal module with custom form api which will send email to my inbox on each submit. I have written a condition under drupal_mail which returns true but shows an error message "Unable to send e-mail. Contact the site administrator if the problem persists."
Below my code:
function my_module_name_mail($key, &$message, $params)
{
$headers = array(
'MIME-Version' => '1.0',
'Content-Type' => 'text/html; charset=UTF-8;',
'Content-Transfer-Encoding' => '8Bit',
'X-Mailer' => 'Drupal'
);
foreach ($headers as $key => $value) {
$message['headers'][$key] = $value;
}
$message['subject'] = $params['subject'];
$message['body'] = $params['body'];
}
function my_module_name_form_submit($form, &$form_state)
{
$from = $form_state['values']['email'];
$body= 'Name: '.$name.'<br />Email: '.$email;
$to = "my_mail_id#example.com";
$params = array(
'body' => $body,
'subject' => 'Website Information Request',
);
drupal_mail('my_module_name', 'some_mail_key', $to, language_default(), $params, $from, TRUE);
}
Check your Drupal logs for more clues. Enable devel / devel-admin modules. Once you do, drupal_mail will pipe emails to your temp directory, which will help you narrow down if the problem is with your email server or the config.

How to split the oauth logic and create the Google Calendar service

I am using php client for Google Calendar API
https://github.com/google/google-api-php-client and CakePHP.
Basically, I just want to manage the events in the calendar.
And I could do it following the example here https://github.com/google/google-api-php-client/blob/master/examples/user-example.php. It worked fine (so, I have no problems with client id, client secret etc).
The only problem is that in the example it's all written in one file - the oauth logic and service specific logic (like retrieving events from calendar). So the redirect uri in google console points to the same file.
The pseudo-code for this is:
//here we create the client which is used for authentication
$client = new Google_Client();
//some of the oauth logic
$client->authenticate($_GET['code']);
//other oauth logic
$service = new Google_Service_Calendar($client);
$events = $service->events->list();
//...
So, as you see, to create a service we need a client (which contains the access token and all the authentication data needed for service).
I want to split it, so that I have the callback where I could retrieve the client and then use it for creating the service. I could have also specify that callback in google console as a redirect uri.
I have a feeling that there should be some standard way which unfortunately I am not aware of. I broke my head thinking about how to do this, so any help is appreciated.
Thanks.
UPDATE
As it often happens, right after posting a question here I managed to do something.
After we get an access token, we need to save it into DB.
Next time, when we want to do something with the api, we create the google client again and just set the access token with the value from the DB. So that we don't need to go through the oauth logic again.
Something like this:
$accessToken = $db->getOne('SELECT token FROM users WHERE id=:id');
$client = new Google_Client();
$client->setClientId($clientId);
$client->setClientSecret($clientSecret);
$client->setRedirectUri($redirectUri);
$client->setAccessType($accessType);
$client->setScopes($scope);
$client->setAccessToken(json_encode($accessToken));
Well, it looks obvious. But still I'd like to get an answer from experts to this question:
is recreating the google client object the right approach?
Thanks.
CakePHP 3.x in Google Calendar API
Follow this steps:
Create google calendar api
https://console.developers.google.com/flows/enableapi?apiid=calendar&pli=1
Composer to install:
"google/apiclient": "^2.0"
Required google calendar api integration
https://console.developers.google.com/flows/enableapi?apiid=calendar&pli=1
https://developers.google.com/google-apps/calendar/quickstart/php
Create project and create secret key and client id Project in set
name and redirect URL
NOTE:- redirect URL must be .com and .org
domain If you develop in local machine then create follow type of
virtual host example.com and example.org Virtual host create then
Follow this step:
Set configuration file in app_globle.php
'Google' => [
'ClientID' => '7441260037.apps.googleusercontent.com',
'ClientSecret' => 'kashdjahdkjshdkjhjAs',
'RedirectUrl' => 'http://' . env("HTTP_HOST") . '/oauth2calendars',
'ClientCredentials' => WWW_ROOT . 'files'. DS.'google.txt',
'Scopes' => implode(' ', [Google_Service_Calendar::CALENDAR, Google_Service_Drive::DRIVE, Google_Service_Drive::DRIVE_FILE, Google_Service_Drive::DRIVE_APPDATA, Google_Service_Drive::DRIVE_METADATA]),
]
Route
$routes->connect('/events/add', ['controller' => 'Events', 'action' => 'add', ['_name' => 'add-event']);
$routes->connect('/events/edit/:id', ['controller' => 'Events', 'action' => 'edit', ['id' => '\d+', 'pass' => ['id'], '_name' => 'edit-event']);
$routes->connect('/events/delete/:id', ['controller' => 'Events', 'action' => 'delete', ['id' => '\d+', 'pass' => ['id'], '_name' => 'delete-event']);
Controller
use Google_Client;
use Google_Service_Calendar;
use Google_Service_Calendar_Event;
use Google_Service_Drive;
use Google_Service_Drive_DriveFile;
Authorize the google Oauth thriugh
/**
* get Google calendar client
*/
private function getClient()
{
$client = new Google_Client();
$client->setAccessType("offline");
$client->setClientId(Configure::read('Google.ClientID'));
$client->setClientSecret(Configure::read('Google.ClientSecret'));
$client->setRedirectUri(Configure::read('Google.RedirectUrl'));
$client->setScopes(Configure::read('Google.Scopes'));
$credentialsPath = Configure::read('Google.Credentials');
if (file_exists($credentialsPath)) {
$accessToken = json_decode(file_get_contents($credentialsPath), true);
} else {
// Request authorization from the user.
$authUrl = $client->createAuthUrl();
return $this->redirect($authUrl);
}
$client->setAccessToken($accessToken);
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}
return $client;
}
/**
* Create Google Calendar event
*/
public function createCredentials()
{
$client = new Google_Client();
$client->setAccessType("offline");
$client->setClientId(Configure::read('Google.ClientID'));
$client->setClientSecret(Configure::read('Google.ClientSecret'));
$client->setRedirectUri(Configure::read('Google.RedirectUrl'));
$client->setScopes(Configure::read('Google.Scopes'));
if (isset($this->request->query['code'])) {
$client->authenticate($this->request->query['code']);
$token = json_encode($client->getAccessToken());
$credentialsPath =WWW_ROOT . 'files'. DS.'google.txt';
if (!file_exists(dirname($credentialsPath))) {
mkdir(dirname($credentialsPath), 0700, true);
}
$file = new File($credentialsPath, true);
$file->write($token);
$client->setAccessToken($token);
return $this->redirect(‘/add-event’);
}
}
Add event functionality
Public function add()
{
$client = $this->getClient();
if ($this->request->is('post')) {
$dateStart = new \DateTime($this->request->data['start_date_time'], new \DateTimeZone('Asia/Kolkata'));
$dateStart->setTimezone(new \DateTimeZone('UTC'));
$startDate = $dateStart->format('Y-m-d H:i:s');
$dateEnd = new \DateTime($this->request->data['end_date_time'], new \DateTimeZone('Asia/Kolkata'));
$dateEnd->setTimezone(new \DateTimeZone('UTC'));
$endDate = $dateEnd->format('Y-m-d H:i:s');
$guests = $this->request->data['guests'];
$eventGuest = [];
$service = new Google_Service_Calendar($client);
foreach ($guests as $key => $value) {
$eventGuest[$key]['email'] = $value;
}
$eventData = [
'summary' => $this->request->data['name'],
'location' => $this->request->data['location'],
'description' => $this->request->data['description'],
'start' => [
'dateTime' => date("c", strtotime($startDate)),
],
'end' => [
'dateTime' => date("c", strtotime($endDate)),
],
'attendees' => $eventGuest,
'reminders' => [
'useDefault' => true,
],
'visibility' => 'private',
'privateCopy' => true,
];
$event = new Google_Service_Calendar_Event($eventData);
$sendNotifications = ['sendNotifications' => true];
$calendarId = 'primary';
$eventDataResponse = $service->events->insert($calendarId, $event, $sendNotifications);
}
}

Sending mails through cakePHP

I was using the following code snippet to send mails, but moving to another host messed everything up:
public function forgetpwd(){
//code omitted
if($this->User->saveField('token_hash',$fu['User']['token_hash'] )){
//============Email================//
/* SMTP Options */
$this->Email->smtpOptions = array(
'host' => 'smtp.gmail.com',
'port'=>'465',
'transport' => 'Smtp',
'username'=>'email#gmail.com',
'password'=>'secret',
'tls' => true
);
$this->Email->template = 'resetpw';
$this->Email->from = 'noreply#email.com';
$this->Email->to = $fu['User']['name'].'<'.$fu['User']['email'].'>';
$this->Email->subject = __('Recover your password');
$this->Email->sendAs = 'both';
$this->Email->delivery = 'smtp';
$this->set('ms', $ms);
$this->Email->send();
$this->set('smtp_errors', $this->Email->smtpError);
$this->Session->setFlash(__('Check your email to recover your password'));
}
//============EndEmail=============//
So there is this view forgetpwd.ctp where user is asked to enter his/her email address, and when trying to send the email, I am given the two following errors:
Error: The view for UsersController::forgetpwd() was not found.
Error: Confirm you have created the file: /home/public_html/development/app/View/Emails/text/resetpw.ctp
I am sure that the resetpw.ctp exists in the right place.
I have edited the email config and tried to adopt the 2.3 cakePHP way, and this is what I have ended up with:
if($this->User->saveField('token_hash',$fu['User']['token_hash'] )){
App::uses('CakeEmail', 'Network/Email');
$Email = new CakeEmail('default');
$Email->to($fu['User']['email'])
->subject('Reset your password')
->message($key)
->send();
$this->Session->setFlash(__('Check your mail to reset your password'));
$this->redirect(array('controller'=>'users','action'=>'reset'));
But now, when I try to send an email, I get the following error:
Call to a member function send() on a non-object
change
'host' => 'smtp.gmail.com',
to
'host' => 'ssl://smtp.gmail.com',
also put your config info in email.php inside App/Config/ as referenced here:
http://book.cakephp.org/2.0/en/core-utility-libraries/email.html#configuration

Testing laravel controllers with JSON request body

I am trying to write a phpunit test for a Laravel controller which expects post requests with a body in JSON format.
A simplified version of the controller:
class Account_Controller extends Base_Controller
{
public $restful = true;
public function post_login()
{
$credentials = Input::json();
return json_encode(array(
'email' => $credentials->email,
'session' => 'random_session_key'
));
}
}
Currently I have a test method which is correctly sending the data as urlencoded form data, but I cannot work out how to send the data as JSON.
My test method (I used the github gist here when writing the test)
class AccountControllerTest extends PHPUnit_Framework_TestCase {
public function testLogin()
{
$post_data = array(
'email' => 'user#example.com',
'password' => 'example_password'
);
Request::foundation()->server->set('REQUEST_METHOD', 'POST');
Request::foundation()->request->add($post_data);
$response = Controller::call('account#login', $post_data);
//check the $response
}
}
I am using angularjs on the frontend and by default, requests sent to the server are in JSON format. I would prefer not to change this to send a urlencoded form.
Does anyone know how I could write a test method which provides the controller with a JSON encoded body?
In Laravel 5, the call() method has changed:
$this->call(
'PUT',
$url,
[],
[],
[],
['CONTENT_TYPE' => 'application/json'],
json_encode($data_array)
);
I think that Symphony's request() method is being called:
http://symfony.com/doc/current/book/testing.html
This is how I go about doing this in Laravel4
// Now Up-vote something with id 53
$this->client->request('POST', '/api/1.0/something/53/rating', array('rating' => 1) );
// I hope we always get a 200 OK
$this->assertTrue($this->client->getResponse()->isOk());
// Get the response and decode it
$jsonResponse = $this->client->getResponse()->getContent();
$responseData = json_decode($jsonResponse);
$responseData will be a PHP object equal to the json response and will allow you to then test the response :)
Here's what worked for me.
$postData = array('foo' => 'bar');
$postRequest = $this->action('POST', 'MyController#myaction', array(), array(), array(), array(), json_encode($postData));
$this->assertTrue($this->client->getResponse()->isOk());
That seventh argument to $this->action is content. See docs at http://laravel.com/api/source-class-Illuminate.Foundation.Testing.TestCase.html#_action
There is a lot easier way of doing this. You can simply set Input::$json property to the object you want to send as post parameter. See Sample code below
$data = array(
'name' => 'sample name',
'email' => 'abc#yahoo.com',
);
Input::$json = (object)$data;
Request::setMethod('POST');
$response = Controller::call('client#create');
$this->assertNotNull($response);
$this->assertEquals(200, $response->status());
I hope this helps you with your test cases
Update : The original article is available here http://forums.laravel.io/viewtopic.php?id=2521
A simple solution would be to use CURL - which will then also allow you to capture the 'response' from the server.
class AccountControllerTest extends PHPUnit_Framework_TestCase
{
public function testLogin()
{
$url = "account/login";
$post_data = array(
'email' => 'user#example.com',
'password' => 'example_password'
);
$content = json_encode($post_data);
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, array("Content-type: application/json"));
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $content);
$json_response = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
$response = json_decode($json_response, true);
// Do some $this->Assert() stuff here on the $status
}
}
CURL will actually simulate the raw HTTP post with JSON - so you know you are truly testing your functionality;
As of Laravel 5.1 there is a much easier way to test JSON controllers via PHPunit. Simply pass an array with the data and it'll get encoded automatically.
public function testBasicExample()
{
$this->post('/user', ['name' => 'Sally'])
->seeJson([
'created' => true,
]);
}
From the docs: http://laravel.com/docs/5.1/testing#testing-json-apis
Since at least Laravel 5.2 there is a json() method in Illuminate\Foundation\Testing\Concerns\MakesHttpRequests therefore you can do the following:
$data = [
"name" => "Foobar"
];
$response = $this->json('POST', '/endpoint', $data);
Also since Laravel 5.3 there are also convenient methods like putJson(), postJson(), etc. Therefore it can be even shortened further to:
$data = [
"name" => "Foobar"
];
$response = $this->postJson('/endpooint', $data);
And then you can do $response->assertJson(...) like:
$response->assertJson(fn (AssertableJson $json) => $json->hasAll(['id', 'name']));

CakePHP view file via Media Views doesn't work

I have CakePHP 1.3 and I have an action that is supposed to view a file, the file maybe a .pdf, .doc, .... or any document, but I get a blank page instead.
code sample:
public function view_attachments($attachment_id){
$attachment = $this->get_attachment($attachment_id);
if($attachment){
$path = pathinfo($attachment['EmailAttachment']['file']);
$this->view = 'Media';
$this->autoRender = false;
$params = array(
'id' => $path['basename'],
'name' => $path['filename'],
'download' => false,
'mimeType'=> $this->Common->get_mime_content_type($path['basename']),
'extension' => strtolower($path['extension']), // must be lower case
'path' => APP . $path['dirname'] . DS // don't forget terminal 'DS'
);
$this->set($params);
}
}
Any Ideas? Please advise.
check that media.ctp file must be blank... to get proper output...
Thanks
Checking on my own working code, the only difference I see is
$this->autoRender = false;
Of course your not supposed to have a "view_attachments.ctp" file.
Be aware that browser's can't open ".doc" natively. So even if you want him to display it, he will offer you to download it.
function download($id) {
$data = $this->OdmPieceJointe->read(null, $id);
$path = pathinfo($data['OdmPieceJointe']['fichier']);
$this->view = 'Media';
$params = array(
'id' => $path['basename'],
'name' => $path['filename'],
'download' => false,
'extension' => $path['extension'],
'path' => 'uploads/',
);
$this->set($params);
}
maybe :
Configure::write('debug', 0);

Resources