How to pass data into a Listener when using Guzzle? - cakephp

I am using Guzzle 3.8.1 with CakePHP 2.5.6 and I am making a call to the Box API. I would like to be able to refresh my access token when it expires. The issue I am encountering is that I may be using any one of N possible access tokens and I want to be able to use the $account_id that is in scope for the function I am defining this in. I don't see how I can pass it into the Listener defined function, since I am not calling that myself. I do see that the expired access_token exists in the $event variable, but I would have to locate it and then parse it from the header that I am passing to Box. I think I could add a custom header containing the account_id, but that seems like a bad way to approach the problem. Can I somehow specify a variable in the request that wouldn't be added as part of the call to Box? Any suggestions are much appreciated, thanks!
`
public function get_folder_list ($account_id = null, $parent_folder_id = null) {
// Get account
$this->Account = ClassRegistry::init('Account');
$account = $this->Account->findById($account_id);
$this->Account->log($account);
// If account doesn't exist, throw error
if(empty($account)) {
throw new NotFoundException(__('Account id not found: ' . $account_id));
}
// Call Box for folder list
$box_url = "https://api.box.com/2.0/folders/";
if (empty($parent_folder_id)) {
$box_url .= "0";
} else {
$box_url .= $parent_folder_id;
}
$this->Account->log($box_url);
$bearer = 'Bearer ' . $account['Account']['access_token'];
$client = new Guzzle\Http\Client();
$client->getEventDispatcher()->addListener('request.error', function(Event $event) {
if ($event['response']->getStatusCode() == 401) {
$Account = new Account;
$Account->log($event);
$Box = new BoxLib;
$account = $Box->refresh_token($account_id);
$bearer = 'Bearer ' . $account['Account']['access_token'];
$request = $client->get($box_url, array('Authorization' => $bearer));
$event['response'] = $newResponse;
$event->stopPropagation();
}
});
$request = $client->get($box_url, array('Authorization' => $bearer));
try {
$response = $request->send();
} catch (Guzzle\Http\Exception\BadResponseException $e) {
// HTTP codes 4xx and 5xx throw BadResponseException
$this->Account->log('Service exception!');
$this->Account->log('Uh oh! ' . $e->getMessage());
$this->Account->log('HTTP request URL: ' . $e->getRequest()->getUrl() . "\n");
$this->Account->log('HTTP request: ' . $e->getRequest() . "\n");
$this->Account->log('HTTP response status: ' . $e->getResponse()->getStatusCode() . "\n");
$this->Account->log('HTTP response: ' . $e->getResponse() . "\n");
}
$this->Account->log($response->json());
$json = $response->json();
$folder_list = array();
foreach ($json['item_collection']['entries'] as $folder) {
$folder_list[$folder['id']] = $folder['name'];
}
//$this->Account->log($folder_list);
return $folder_list;
}
`

Related

PUT request with formData could not update using react.js and laravel

I am not receiving formdata in the Api endpoint which is being sent by fetch and PUT method.
I used the POST method and it worked out which i think is not recommended for updating.
I have react running on localhost:3000 and the laravel-API running on localhost:5000.
This is the route in API
Route::put('updateSlide/{id}', 'SlidesController#updateSlide');
This is what is in the controller
public function updateImage(Request $request, int $id)
{
$image = $this->slideRepo->findSlideById($id)->image;
if ($image) {
$result = Storage::disk('ucmp')->delete($image);
}
if ($request->hasFile('image') && $request->file('image') instanceof UploadedFile) {
return $this->slideRepo->saveCover($request->file('image'));
} // return response()->json($data);
// data is an array (note)
return null;
}
public function updateSlide(Request $request, int $id)
{
$imageUrl=$this->updateImage($request, $id);
return response()->json($this->slideRepo->updateSlide([
'caption' => $request['caption'],
'image' => $imageUrl,
'url' => $request['url']
],$id));
}
This is the function sending to fetch
export const updateSlideApi = (token, _slide, id) => {
return {
url: `${BASE_URL}/api/updateSlide/${id}`,
opt: API.requestOptions("PUT",token,null,{ body: _slide }, true)
};
};
In my header i do not have the content-type.
I expect json data from the laravel API function but am having an error, " Can only throw objects"
PHP does not parse the body of a PUT request.
Use POST and add parameter _method with value PUT.
PHP put has given me tough time, use this function will save your time parsing it else method:
function parsePutRequest()
{
// Fetch content and determine boundary
$raw_data = file_get_contents('php://input');
$boundary = substr($raw_data, 0, strpos($raw_data, "\r\n"));
// Fetch each part
$parts = array_slice(explode($boundary, $raw_data), 1);
$data = array();
foreach ($parts as $part) {
// If this is the last part, break
if ($part == "--\r\n") break;
// Separate content from headers
$part = ltrim($part, "\r\n");
list($raw_headers, $body) = explode("\r\n\r\n", $part, 2);
// Parse the headers list
$raw_headers = explode("\r\n", $raw_headers);
$headers = array();
foreach ($raw_headers as $header) {
list($name, $value) = explode(':', $header);
$headers[strtolower($name)] = ltrim($value, ' ');
}
// Parse the Content-Disposition to get the field name, etc.
if (isset($headers['content-disposition'])) {
$filename = null;
preg_match(
'/^(.+); *name="([^"]+)"(; *filename="([^"]+)")?/',
$headers['content-disposition'],
$matches
);
list(, $type, $name) = $matches;
isset($matches[4]) and $filename = $matches[4];
// handle your fields here
switch ($name) {
// this is a file upload
case 'userfile':
file_put_contents($filename, $body);
break;
// default for all other files is to populate $data
default:
$data[$name] = substr($body, 0, strlen($body) - 2);
break;
}
}
}
return $data;
}

Single Signon with Magento account from drupal

I have custom Magento script file as below which does login by just passing email and password to that PHP file.
It works fine when i'm making a call from browser.
But, I want to make this call through Drupal Module which i have created.
As i expected call is happening from Drupal module and i'm getting success message too. But login is not happening.
My hunch is that magento have some login restrictions which happening outside magento root folder.
Please find the source below.
Drupal directory - /www/drupal/
Magento directory - /www/drupal/store/
/www/drupal/store/api_config.php
<?php
error_reporting(E_ALL);
ini_set('display_errors', 'On');
require_once (dirname(dirname(realpath(__FILE__))).'/store/app/Mage.php');
umask(0);
Mage::app();
Mage::getSingleton('core/session', array('name' => 'frontend'));
$websiteId = Mage::app()->getWebsite()->getId();
$store = Mage::app()->getStore();
$response = array();
/www/drupal/store/api_login.php
<?php
require_once "api_config.php";
$session = Mage::getSingleton('customer/session');
//$session->start();
if (isset($_GET['email']) && !empty($_GET['email']) && isset($_GET['password']) && !empty($_GET['password'] )) {
if (!filter_var($_GET['email'], FILTER_VALIDATE_EMAIL) === false) {
$email = $_GET['email'];
$password = $_GET['password'];
try {
if ($session->login($email, $password )) {
$response['status'] = 'success';
$response['data'] = array($_GET);
$response['message'] = array('User loggedin Successfully.');
} else {
$response['status'] = 'error';
$response['data'] = array($_GET);
$response['message'] = array('User login failed.');
}
if ($session->getCustomer()->getIsJustConfirmed()) {
$this->_welcomeCustomer($session->getCustomer(), true);
}
} catch (Mage_Core_Exception $e) {
switch ($e->getCode()) {
case Mage_Customer_Model_Customer::EXCEPTION_EMAIL_NOT_CONFIRMED:
$value = Mage::helper('customer')->getEmailConfirmationUrl($email);
$message = Mage::helper('customer')->__('This account is not confirmed. Click here to resend confirmation email.', $value);
break;
case Mage_Customer_Model_Customer::EXCEPTION_INVALID_EMAIL_OR_PASSWORD:
$message = $e->getMessage();
break;
default:
$message = $e->getMessage();
}
//$session->addError($message);
$response['status'] = 'error';
$response['data'] = array($_GET);
$response['message'] = array($message);
echo $message;
$session->setUsername($email);
} catch (Exception $e) {
$response['status'] = 'error';
$response['data'] = array($_GET);
$response['message'] = array($e);
// Mage::logException($e); // PA DSS violation: this exception log can disclose customer password
}
} else {
//$session->addError('Login and password are required.');
$response['status'] = 'error';
$response['data'] = array($_GET);
$response['message'] = array('Invalid Email address');
}
} else {
//$session->addError('Login and password are required.');
$response['status'] = 'error';
$response['data'] = array($_GET);
$response['message'] = array('Login and password are required.');
}
print_r(json_encode($response, JSON_FORCE_OBJECT));die;
?>
/www/drupal/sites/all/modules/single_signon/single_signon.module
<?php
function single_signon_user_login(&$edit, $account) {
//store variable values
$postData = array();
$postData['email'] = $account->mail;
$postData['password'] = $edit['input']['pass'];
$inc = 1; //count of registration
if (!empty($postData['email']) && !empty($postData['password'])) {
// use of drupal_http_request
$data = http_build_query($postData, '', '&');
//$url = url('http://127.0.0.1/drupal/store/api_login.php?'.$data);
//$headers = array('Content-Type' => 'application/x-www-form-urlencoded');
//print_r($url);
// the actual sending of the data
$JSONresponse = drupal_http_request('http://127.0.0.1/drupal/store/api_login.php?email=john#example.com&password=password');
//print_r($JSONresponse);die;
$response = json_decode($JSONresponse->data, true);
if ($response['status']=='success') {
$inc+=1;
$message = 'Logged in successfully('.$inc.')';
drupal_set_message($message, $type = 'status', $repeat = FALSE); //message goes here
} else {
$message = 'Logged in failed. Due to '.$response['message'].'('.$inc.')';
drupal_set_message($message, $type = 'error ', $repeat = FALSE);
}
} else {
$message = 'Not able to log inside store('.$inc.')';
drupal_set_message($message, $type = 'status', $repeat = FALSE); //message goes here
}
}
?>
Any suggestions for findings to solve this mystery would be really helpful.
I'm not sure to understand it well : You have a php script using data send in the URL (GET) to connect a user in a session. And you would like the Drupal server to use it to connect directly to your Magento.
I think your code is working, but unfortunately it could not help the user to connect to Magento.
As this is the Drupal server asking for the connection, it would be the Drupal server session that will be connected and not the navigation user one.
If the user have to be connected, in his navigator, to the Magento server, it has to be the navigator witch must call the Magento script directly.
It could be done in an iframe or via Ajax I think.
I think you can also find some other solutions, as OAuth, but it will need a lot more of coding.
EDIT
I found some interesting subject about your problem :
Getting logged in user ID from Magento in external script - multiple session issue?
Magento Session from external page (same domain)
Magento external login will not create session cookie
I think you have to manually create the Magento session cookie on the user navigator, from the Drupal script.
You'll need to send back to Drupal the SessionID from Magento, using this method (I think, you'll have to verify) :
$response['sessionId'] = $session->getEncryptedSessionId();
And inside the Drupal script, you'll have to record a new cookie with the Magento session information. Maybe you have to have a look at a working Magento cookie to see how it is defined and what is its name.
if ($response['status']=='success') {
...
setcookie('frontend', $response['sessionId'], time() + 3600 * 24 * 180, '/');
...
}
You'll probably have to declare, in the settings of Magento, the path for cookies at '/'.
Can you give an example of the structure of the session cookie from Magento ?

GetLowestPricedOffersForSKU failed processing arguments

I have a slight issue when trying to call GetLowestPricedOffersForSKU, I get the response :
Failed processing arguments of org.jboss.resteasy.spi.metadata
I can call other functions in the Product Api and they work fine, just get the above error on this function.
I have looked round the net for the answer but can't find anything that is related to this, does anybody have any idea why I'm getting this ?
By the way it all works fine in the MWS Scratchpad !
Posting in case anyone else comes to this and is as confused as I was. There is a fundamental difference with how nearly all Amazon MWS requests work except this particular one. All other requests technically accept the parameters as query parameters instead of POST data. The scratchpad even suggests this is how it is actually working (although the MWS Scratchpad actually sends the data as Post Data Fields also).
MWS needs the POST data passed as form params instead of as a query string for some operations. Otherwise, it pukes a Failed processing arguments of org.jboss.resteasy.spi.metadata style 400 Bad Request error for some operations such as this one (GetMyFeesEstimate is another that suffers from this).
For instance, if you did the following POST request in Guzzle 6 then you'd likely get the error:
$response = $client->request('POST', 'https://mws.amazonservices.com/Products/2011-10-01/?AWSAccessKeyId=YOURAWSACCESSKEY&Action=GetLowestPricedOffersForASIN&SellerId=YOURSELLERID&MWSAuthToken=amzn.mws.fghsffg-4t44e-hfgh-dfgd-zgsdbfe5erg&SignatureVersion=2&Timestamp=2017-07-09T15%3A45%3A18%2B00%3A00&Version=2011-10-01&Signature=bCasdxXmYDCasdaXBhsdgse4pQ6hEbevML%2FJvzdgdsfdy2o%3D&SignatureMethod=HmacSHA256&MarketplaceId=ATVPDKIKX0DER&ASIN=B007EZK19E');
To fix this you'd submit it as form data as in this Guzzle 6 example:
$response = $client->request('POST', 'https://mws.amazonservices.com/Products/2011-10-01', [
'form_params' => [
'AWSAccessKeyId' => 'YOURAWSACCESSKEY',
'Action' => 'GetLowestPricedOffersForASIN',
'SellerId' => 'YOURSELLERID',
'MWSAuthToken' => 'amzn.mws.fghsffg-4t44e-hfgh-dfgd-zgsdbfe5erg',
'SignatureVersion' => 2,
'Timestamp' => '2017-07-09T15%3A45%3A18%2B00%3A00',
'Version' => '2011-10-01',
'Signature' => 'bCasdxXmYDCasdaXBhsdgse4pQ6hEbevML%2FJvzdgdsfdy2o%3D',
'SignatureMethod' => 'HmacSHA256',
'MarketplaceId' => 'ATVPDKIKX0DER',
'ASIN' => 'B007EZK19E',
]
]);
This code worked for me. Hope it will help someone.
<?php
require_once('.config.inc.php');
// More endpoints are listed in the MWS Developer Guide
// North America:
$serviceUrl = "https://mws.amazonservices.com/Products/2011-10-01";
// Europe
//$serviceUrl = "https://mws-eu.amazonservices.com/Products/2011-10-01";
// Japan
//$serviceUrl = "https://mws.amazonservices.jp/Products/2011-10-01";
// China
//$serviceUrl = "https://mws.amazonservices.com.cn/Products/2011-10-01";
$config = array (
'ServiceURL' => $serviceUrl,
'ProxyHost' => null,
'ProxyPort' => -1,
'ProxyUsername' => null,
'ProxyPassword' => null,
'MaxErrorRetry' => 3,
);
$service = new MarketplaceWebServiceProducts_Client(
AWS_ACCESS_KEY_ID,
AWS_SECRET_ACCESS_KEY,
APPLICATION_NAME,
APPLICATION_VERSION,
$config);
// #TODO: set request. Action can be passed as MarketplaceWebServiceProducts_Model_GetLowestPricedOffersForSKU
$request = new MarketplaceWebServiceProducts_Model_GetLowestPricedOffersForSKURequest();
$request->setSellerId(MERCHANT_ID);
$request->setMWSAuthToken(MWSAUTH_TOKEN);
$request->setMarketplaceId(MARKETPLACE_ID);
$request->setSellerSKU($sellerSKU);
$request->setItemCondition($ItemCondition);
// object or array of parameters
invokeGetLowestPricedOffersForSKU($service, $request);
function invokeGetLowestPricedOffersForSKU(MarketplaceWebServiceProducts_Interface $service, $request)
{
try {
$response = $service->GetLowestPricedOffersForSKU($request);
echo ("Service Response\n");
echo ("=============================================================================\n");
$dom = new DOMDocument();
$dom->loadXML($response->toXML());
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
echo $dom->saveXML();
echo("ResponseHeaderMetadata: " . $response->getResponseHeaderMetadata() . "\n");
} catch (MarketplaceWebServiceProducts_Exception $ex) {
echo("Caught Exception: " . $ex->getMessage() . "\n");
echo("Response Status Code: " . $ex->getStatusCode() . "\n");
echo("Error Code: " . $ex->getErrorCode() . "\n");
echo("Error Type: " . $ex->getErrorType() . "\n");
echo("Request ID: " . $ex->getRequestId() . "\n");
echo("XML: " . $ex->getXML() . "\n");
echo("ResponseHeaderMetadata: " . $ex->getResponseHeaderMetadata() . "\n");
}
}
?>
As #eComEvoFor stated, for this particularly method, Amazon demands you specify the params in the body of the request. In node.js using axios library you can do this:
const paramsSorted = {}
Object.keys(params)
.sort()
.forEach((key) => {
paramsSorted[key] = params[key]
})
const data = new URLSearchParams(paramsSorted)
url = urljoin(this.marketplace.url, this.api, this.api.Version)
const response = await axios.post(url, data)

JRequest::getVar is keeping previous values even after refreshing

I am writing a mail function as a module in joomla 3. e mail works fine, but when i reload the page and insert a different email and send, but it seemed to return the previous email by JRequest::getVar function. Is there a way to solve this issue? thanks in advance..
this is the code i used:
<?php
defined('_JEXEC') or die('Direct Access to this location is not allowed.');
require_once(dirname(__FILE__) . DS . 'helper.php');
//declaration
$input = JFactory::getApplication()->input;
$form_send = $input->get('form_send', 'notsend');
$fanme = $input->get('firstName');
$lname = $input->get('lastinput');
$email = $input->get('email', 0 , 'STRING');
$mail=false;
$emailexist=false;
echo '<script>
var php_var = "chk is first:'.$email.'";
alert(php_var);
</script>';
switch ($form_send) {
case 'send':
if ((is_null($fanme) || is_null($lname) || is_null($email)) || (!filter_var($email, FILTER_VALIDATE_EMAIL))) {
echo '<div> Fields are empty or not valid. <br></div>';
} else {
$mail = ModLittleContactHelper::SendMail($email, $fanme, $lname);
echo '<script>
var php_var = "chk when mail sending:'.$email.'";
alert(php_var);
</script>';
$input = JFactory::getApplication();//i have tried $app also
$input ->setUserState('mod_mycontact.email', null);
}
//echo $respond
if (!$mail) {
echo 'Error sending email:';
require(JModuleHelper::getLayoutPath('mod_myecontact', 'default_tmpl'));
}else{
require(JModuleHelper::getLayoutPath('mod_mycontact', 'sendok_tmpl'));
break;
}
default:
require(JModuleHelper::getLayoutPath('mod_littlecontact', 'default_tmpl'));
unset($var);
}
?>
#Mario this is the helper code:
class ModLittleContactHelper{
public function SendMail($email, $fname, $lname) {
$body = "<p style='font-family:arial;font-size:20px;'>Hi " . $fname . " " . $lname . ",</p>";
$body.="<p style='font-family:arial;font-size:20px;'>Welcome to Crowd Logistics! Please verify your email address below.</p><br/><br/>";
$body.= "<hr><br/>";
$body.= "<p style='align:center;background-color:#40B3DF;font-family:arial;color:#FFFFFF;font-size:20px;'><a href='http://suriyaarachchi.com/crowdlogistics/index.php?option=com_content&view=article&id=192' target='_blank'>Verify " . $email . "</a></p>";
$body.= "<br/><hr><br/>";
$body.="<p style='text-align:right;font-family:arial;font-size:20px;'>Or, paste this link into your browser:<br/>";
$body.= "http://crowdlogistics/index.php?option=com_content&view=article&id=192<br/><br/>";
$body.= "Thanks.<br/>";
$body.= "CrowdLogistics</p><br/>";
$mailer = & JFactory::getMailer();
$mailer->setSender('info#crowdlogistics.com');
$mailer->addRecipient($email);
$mailer->setSubject('Mail from CrowdLogistics - Confirm your email');
$mailer->setBody($body);
$mailer->IsHTML(true);
$send = & $mailer->Send();
return $send;
}
Since you're trying to run your code on Joomla 3, there are a few things that are wrong. Below os your code, corrected where I was able to correct it. Now you have to test it in your module environment with the class being instantiated (in other words, test the below code in your module).
<?php
defined('_JEXEC') or die('Direct Access to this location is not allowed.');
require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'helper.php');
//declaration
$doc = JFactory::getDocument();
$app = JFactory::getApplication();
$input = $app->input;
$form_send = $input->get('form_send', 'notsend');
$fanme = $input->get('firstName');
$lname = $input->get('lastinput');
$email = $input->get('email', 0 , 'STRING');
$mail=false;
$emailexist=false;
$doc->addScriptDeclaration('
var php_var = "chk is first:'.$email.'";
alert(php_var);
');
switch ($form_send) {
case 'send':
if ((is_null($fanme) || is_null($lname) || is_null($email)) || (!filter_var($email, FILTER_VALIDATE_EMAIL))) {
echo '<div> Fields are empty or not valid. <br></div>';
} else {
$mail = ModLittleContactHelper::SendMail($email, $fanme, $lname);
$doc->addScriptDeclaration('
var php_var = "chk when mail sending:'.$email.'";
alert(php_var);');
$app->setUserState('mod_littlecontact.email', null);
}
//echo $respond
if (!$mail) {
echo 'Error sending email:';
require(JModuleHelper::getLayoutPath('mod_littlecontact', 'default_tmpl'));
break;
}else{
require(JModuleHelper::getLayoutPath('mod_littlecontact', 'sendok_tmpl'));
break;
}
default:
require(JModuleHelper::getLayoutPath('mod_littlecontact', 'default_tmpl'));
unset($var);
}
?>
First, JRequest class is deprecated in J3. You should use JInput:
$input = JFactory::getApplication()->input;
$your_var = $input->get('your_var');
Then, regarding the email, you probably need to unset the session variables when the success is achieved (mail sent), or in other words, when you don't need them any longer.
$app = JFactory::getApplication();
// your_var is the variable you want to unset
$app->setUserState('mod_your_module.your_var', null);
Hope it helps
you can use this code
$email = $input->get('email', 0 , 'STRING','');
4th argument for the default value,

detect if core flash messages in cakephp is an error or success message

I have copied the SesionHelper from the core into myapp/View/Helper so I can alter the div structure around the message outputted.
My problem is that I cant seem to detect if the message is an error or success message from the default cakephp message. I know I can set a flash message in my controller and add an attribute. But there doesn't seem to be any extra data that I can see from the core messages.
Example if the data is saved to the database i wish to show the message as green. Or if the data could not be saved then as red message.
public function flash($key = 'flash', $attrs = array()) {
$out = false;
if (CakeSession::check('Message.' . $key)) {
$flash = CakeSession::read('Message.' . $key);
$message = $flash['message'];
unset($flash['message']);
if (!empty($attrs)) {
$flash = array_merge($flash, $attrs);
}
if ($flash['element'] === 'default') {
$class = 'message';
if (!empty($flash['params']['class'])) {
$class = $flash['params']['class'];
}
$out = '<div id="' . $key . 'Message" class="' . $class . '">' . $message . '</div>';
} elseif (!$flash['element']) {
$out = $message;
} else {
$options = array();
if (isset($flash['params']['plugin'])) {
$options['plugin'] = $flash['params']['plugin'];
}
$tmpVars = $flash['params'];
$tmpVars['message'] = $message;
$out = $this->_View->element($flash['element'], $tmpVars, $options);
}
CakeSession::delete('Message.' . $key);
}
return $out;
}
What you are doing is reinventing the wheel as far as CakePHP is concerned.
You can specify an element as the second argument when you set a flash message in your controller method:
$this->Session->setFlash('Your record has been saved', 'flash_success');
Then in elements create an element Element/flash_success.ctp like this:
<div class="alert-success"><?php echo $message;?></div>
And finally in your view:
<?php echo $this->Session->flash()?>
Here is the section that deals with this in detail from the official documentation:
http://book.cakephp.org/2.0/en/core-libraries/components/sessions.html#creating-notification-messages

Resources