How to do a live user activity tracking function on Codeigniter? - codeigniter

I'm looking for how to tracking user activity (Example: XXX commented on the post/ XXX signed in/XXX shared a post or etc; XXX are registered User/IP address) on my Codeigniter website.
Is it have any recommendations for doing this function because I already researched a few days and get no idea.

Normally I would not even answer a question like this as it is too broad but since I have a similar system I will share my approach. Please note, I am offering no support on this, but rather this is for you to get an idea and/or develop your own implementation.
Let's assume we want to log user logins/failures:
if ($this->ion_auth->login($identity, $this->input->post('password'), false)) {
$this->curr_user->update_ip();
$this->activity->log('logged_in');
$this->response->success()->json();
} else {
$this->activity->log('login_failed', array('username' => $identity));
$this->response->error($this->ion_auth->errors())->json();
}
We simply call the log function before redirecting/rendering output but after the desired action (insert/update, login, .etc.) is completed.
MODEL
The log function uses a switch statement to keep messages homogeneous, and keeps certain information separate from the main message for querying purposes. To be honest using a switch statement isn't super elegant here especially if you have a lot of messages, so implementing a table to hold the messages wouldn't be a bad idea, but this works for my needs.
get_logs is a cool little function that combines all the data and renders it in a sorted list by date the action occurred.
class User_activity_model extends CI_Model {
/**
* Admin-only viewable log types
*
* #var array
*/
private $admin_types = array('page_access', 'user_registered', 'logged_in', 'login_failed', 'login_failed_sa', 'delete');
/**
* Main user activity logging function
*
* #param string $action
* #param array $arr Additional attributes per case
* #return void
*/
public function log($action, $arr = array()) {
switch ($action) {
default:
return;
case 'backup_created':
$msg = "{username} created a backup {$arr['filename']} at {time}.";
break;
case 'backup_restored':
$msg = "{username} restored backup {$arr['filename']} at {time}.";
break;
case 'user_registered':
$msg = "{$arr['first_name']} {$arr['last_name']} registered with username: {$arr['username']}.";
break;
case 'added':
$msg = "{username} added item {$arr['id']} to the {$arr['table']} table.";
break;
case 'modified':
$msg = "{username} modified item {$arr['id']} in the {$arr['table']} table.";
break;
case 'deleted':
$msg = "{username} deleted item {$arr['id']} from the {$arr['table']} table.";
break;
case 'published':
$msg = "{username} published item {$arr['id']} from the {$arr['table']} table.";
break;
case 'unpublished':
$msg = "{username} unpublished item {$arr['id']} from the {$arr['table']} table.";
break;
case 'logged_in':
$ip = $this->input->ip_address();
$msg = "{username} logged in at {time} from IP {$ip}.";
break;
case 'login_failed':
$ip = $this->input->ip_address();
$msg = "Someone tried to login with username {$arr['username']} at {time} from IP {$ip}.";
break;
case 'login_failed_sa':
$ip = $this->input->ip_address();
$msg = "Someone tried to login with social auth provider {$arr['provider']} at {time} from IP {$ip}.";
break;
case 'page_access':
$identity = $this->ion_auth->logged_in() ? '{username}' : "Someone (IP: {$this->input->ip_address()})";
$msg = "{$identity} tried to access a page they didn't have permission for: {$arr['uri']}.";
break;
case 'logout':
$msg = "{username} logged out at {time}.";
break;
case 'user_forgotten':
$msg = 'Someone deleted their account.';
break;
}
$this->add($action, $msg);
}
/**
* Adds identifier information to the insert query
*
* #param string $action
* #param string $msg
* #return void
*/
private function add($action, $msg) {
$data = array(
'ip' => $this->input->ip_address(),
'action' => $action,
'type' => in_array($action, $this->admin_types) ? 'admin' : 'all',
'message' => $msg
);
if ($this->ion_auth->logged_in()) {
$data['username'] = $this->curr_user->details()->username;
$data['user_id'] = $this->curr_user->id();
}
$this->db->set('time', 'NOW()', false);
$this->db->insert('user_activity', $data);
}
/**
* Generates log array
*
* Format:
*
* array(
* [date]
* array(
* [0] = message1
* [1] = message2
*
* #param boolean $admin_only Show all logs?
* #return boolean|object Logs object, FALSE otherwise
*/
public function get_logs($admin_only = true) {
if (!$admin_only) {
$this->db->where('type !=', 'admin');
}
$this->db->limit(10000);
$this->db->order_by('time', 'DESC');
$query = $this->db->get('user_activity');
if ($query->num_rows() < 1) {
return false;
}
$rows = $query->result();
$data = new \stdClass();
foreach ($rows as $row) {
// replace {time} with timezone converted time
$time = $this->timezone->convert($row->time);
$row->message = str_replace('{time}', $time, $row->message);
$username = is_null($row->username) ? $this->lang->line('deleted_user') : $row->username;
$row->message = str_replace('{username}', $username, $row->message);
// date Y-m-d acts as key for messages on that day
$key = date('Y-m-d', strtotime($time));
$data->{$key}[] = $row;
}
return $data;
}
/**
* Truncates user_activity table
*
* #return boolean
*/
public function delete_logs() {
return $this->db->truncate('user_activity');
}
/**
* Stub for automate hook to add 'added' activity
*
* #param array $data
*/
public function automate_activity_add($data) {
$this->activity->log('added', array('table' => $data['table'], 'id' => $data['id']));
}
/**
* Stub for automate hook to add 'modified' activity
*
* #param array $data
*/
public function automate_activity_modify($data) {
$this->activity->log('modified', array('table' => $data['table'], 'id' => $data['id']));
}
}
DATABASE
db dump: https://pastebin.com/wCEnUigH

Related

triggerCommand with argument in irazasyed/telegram-bot-sdk

I want to trigger a command with an argument from another command, and also trigger a command with an argument from a controller, so my command solutions of command are:
class ChangeLanguageCommand extends Command
{
/**
* #var string Command Name
*/
protected $name = 'change_language';
/**
* #var string Command Description
*/
protected $description = 'Change Language,Change language of bot';
protected $pattern = '{lang}';
/**
* {#inheritdoc}
*/
public function handle()
{
if($this->getArguments())
{
$args = $this->getArguments();
if($args['lang'])
$lang=$args['lang'];
else
$lang='fa';
}
else
$lang='fa';
if($lang=='fa')
{
$response=$this->replyWithMessage(['text' => 'زبان ربات تغییر یافت.']);
$this->triggerCommand('start',$this->getArguments());
}
else
{
$response=$this->replyWithMessage(['text' => 'Robot language changed.']);
$this->triggerCommand('start',$this->getArguments());
}
}
}
and my command solutions of controller is:
class TelegramBotController extends Controller
{
public function commandHandlerWebHook()
{
try
{
Telegram::addCommands([
Telegram\Bot\Commands\HelpCommand::class,
App\Telegram\Commands\StartCommand::class,
App\Telegram\Commands\ChangeLanguageCommand::class,
App\Telegram\Commands\ThingsToSendAdvertCommand::class,
App\Telegram\Commands\LuggageSpaceAdvertCommand::class,
]);
$commandsHandler = Telegram::commandsHandler(true);
$update =Telegram::getWebhookUpdate(['timeout' => 120]);
$chat_id = $update["message"]["chat"]["id"];
$data=$update["message"]["text"];
switch ($data) {
case 'تغییر زبان':
$command = "change_language";
$arguments =['fa'];
$res = Telegram::getCommandBus()->execute($command, $arguments, $commandsHandler);
break;
case 'Change Language':
$command = "change_language";
$arguments =['en'];
$res = Telegram::getCommandBus()->execute($command, $arguments, $commandsHandler);
break;
case 'راهنما':
$command = "help";
$arguments =[];
$res = Telegram::getCommandBus()->execute($command, $arguments, $commandsHandler);
break;
case 'Help':
$command = "help";
$arguments =[];
$res = Telegram::getCommandBus()->execute($command, $arguments, $commandsHandler);
break;
}
catch (\Exception $e)
{
dd($e->getMessage());
}
}
}
But it does not work and I do not get any answer from the bot(bot return 200 status).
Can anyone help me?
At irazasyed/telegram-bot-sdk version 3.4 you should use update as second parameters.
$update = Telegram::commandsHandler(true);
//...
triggerCommand('start', $update)
//Or
Telegram::getCommandBus()->execute('start', $update, [])

Can't get POST data with AJAX

I would like to show 4 objects in a page, with a "load more" button which show 4 more object each time we click.
I try to adapt a PHP script which works(tested) to Symfony.
The problem is that I can't get the POST data (a page number) in my Symfony function, even if I can see it in the chrome developer toolbar...
My controller:
<?php
/**
* Content controller.
*
* #Route("content")
*/
class ContentController extends Controller
{
/**
* Lists all software.
*
* #Route("/software", name="content_software")
* #Method({"POST", "GET"})
*/
public function softwareAction(Request $request)
{
if ($request->request->get('page')){
$page_number = filter_var($_POST["page"], FILTER_SANITIZE_NUMBER_INT,
FILTER_FLAG_STRIP_HIGH);
$item_per_page = 4;
$position = (($page_number-1) * $item_per_page);
$contents = $this->getRepo()->findBy(array(),null,$item_per_page,$position);
}
else
{
$contents = "didn't work";
}
return $this->render('content/index.html.twig', array(
'contents' => $contents
));
}
}
index.html.twig :
{% extends 'loicCoreBundle::Default/layout.html.twig' %}
{% block body %}
{{ dump(contents) }}
<script type="text/javascript">
var track_page = 1; //track user click as page number, right now page number is 1
load_contents(track_page); //load content
$("#load_more_button").click(function (e) { //user clicks on button
track_page++; //page number increment everytime user clicks load button
load_contents(track_page); //load content
});
//Ajax load function
function load_contents(track_page){
$.post( "{{ path('content_software') }}", {'page': track_page}, function(data){
if(data.trim().length == 0){
//display text and disable load button if nothing to load
$("#load_more_button").text("No more records!").prop("disabled", true);
}
});
}
</script>
{% endblock %}
I'm not sure where your errors are coming from, I've done a similar test which just works (no problems with $request->request->get()).
You are however loading a full template (index.html) for each sub request as well. Usually you want to separate calls like this into api like methods, however for simple things its a bit silly.
Here is an expanded/ updated version of what I tested, I avoided post data all together and just used the method as switch. This worked fine so try and figure out where you went wrong using this as reflection (or whatever you like to do with it). Note this uses a simple PHP range array for test data, not entities but it should remain the same principle.
Controller
/**
* #Route(
* "software/{page}/{limit}",
* name="content_software",
* requirements = {
* "page": "[1-9]\d*",
* "limit": "[1-9]\d*"
* }
* )
*/
public function softwareAction(Request $request, $page = 1, $limit = 4) {
if ($request->isMethod('POST')) {
// replace these two lines with database logic (SELECT & LIMIT)
$start = ($page - 1) * $limit;
$items = array_slice(range(1, 10), $start, $limit); // Just using a simple item array [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] as test data
$content = '';
foreach ($items as $item) {
$content .= $this->get('twig')->render('someItemTemplate.html.twig', ['item' => $item]);
}
return new Response($content);
} else {
// alternatively you can send out the default items here for the first get request
// and use the same item template above with {% embed %} to render them in this template
return $this->render('someTemplate.html.twig');
}
}
someTemplate.html.twig
<html>
<head>
<title>Cake or death?</title>
</head>
<body>
<ul id="put-the-things-here"></ul>
<button id="next_please">Ehh cake please!</button>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script>
var pageTracker = 1,
$next = $("#next_please"),
$target = $('#put-the-things-here');
load_contents(pageTracker);
$next.click(function (e) {
load_contents(++pageTracker);
});
function load_contents(page) {
// using post just for the request method (page is in the url)
$.post("{{ path('content_software') }}/" + page,
function (data) {
if (data) {
$target.append(data);
} else {
$target.append('<li>We are out of cake.</li>');
$next.attr('disabled', true);
}
}
);
}
</script>
</body>
</html>
someItemTemplate.twig
<li>Cake: {{ item }}</li>
try this:
$request->request->get('page')
Instead of this:
$request->request->has('page')
If it doesn't work try to:
var_dump($request->request->all());
And check the variables
I think too the problem comes from routes.
Error message when I use only "POST" method :
"No route found for "GET /content/software": Method Not Allowed (Allow: POST, DELETE)".
Because when I arrive on my page by clicking a link , I arrive with a GET method.
However, when I click on my "load more" button the AJAX works and I can see in my symfony debugbar that I get POST variable:
The problem is when I first arrive on my page I think.
#Jenne van der Meer: Thank you but I would like not to use GET parameters.
I add my full controller code, in case of:
/**
* Content controller.
*
* #Route("content")
*/
class ContentController extends Controller
{
public function getRepo(){
$em = $this->getDoctrine()->getManager();
$repo = $em->getRepository('loicContentBundle:Content');
return $repo;
}
// private $theRepo = getDoctrine()->getManager()->getRepository('loicContentBundle:Content');
/**
* Lists all content entities.
*
* #Route("/", name="content_index")
* #Method("GET") */
public function indexAction(Request $request)
{
$contents = $this->getRepo()->findAll();
$this->denyAccessUnlessGranted('ROLE_USER', null, 'Unable to access this page!');
$formFilter = $this->createFormBuilder()
->add('_', EntityType::class,array(
'class' => 'loicFilterBundle:Filter',
'multiple' => true,
'expanded' => true,
'choice_label' => function($value) {
return ($value->getName());
},
))
->add('Appliquer filtres', SubmitType::class)
->getForm();
$formFilter->handleRequest($request);
$data = '';
if ($formFilter->isSubmitted() && $formFilter->isValid()) {
$data = $formFilter->getData();
$data = $data['_']->toArray();
$contents = $this->getRepo()->findAll();
}
else
{$contents = $this->getRepo()->findAll();
}
return $this->render('content/index.html.twig', array(
'contents' => $contents,'formFilter' => $formFilter->createView(),'request' => $request
));
}
public function contentAction($categoryId)
{
$contents= $this->getRepo()->findBy(
array('contentCategorycontentCategory' => $categoryId),
null,
4
);
return $contents;
}
/**
* Lists all software.
*
* #Route("/software", name="content_software")
*/
public function softwareAction(Request $request)
{
var_dump($request->request->all());
$bla = $request->request->get('page');
if ($request->request->get('page')){
$page_number = filter_var($_POST["page"], FILTER_SANITIZE_NUMBER_INT,
FILTER_FLAG_STRIP_HIGH);
$item_per_page = 4;
$position = (($page_number-1) * $item_per_page);
$contents = $this->getRepo()->findBy(array(),null,$item_per_page,$position);
}
else
{
$contents = "didn't work";
}
return $this->render('content/index.html.twig', array(
'contents' => $contents,'bla' => $bla
));
}
/**
* Lists all videos.
*
* #Route("/videos", name="content_videos")
* #Method("GET")
*/
public function videoAction()
{
$contents = $this->contentAction(5);
return $this->render('content/index.html.twig', array(
'contents' => $contents,
));
}
/**
* Lists all testimonies.
*
* #Route("/testimonies", name="content_testimonies")
* #Method("GET")
*/
public function testimoniesAction()
{
$contents = $this->contentAction(8);
return $this->render('content/index.html.twig', array(
'contents' => $contents,
));
}
/**
* Lists all whiteBooks.
*
* #Route("/whiteBooks", name="content_whiteBooks")
* #Method("GET")
*/
public function whiteBooksAction()
{
$contents = $this->contentAction(3);
return $this->render('content/index.html.twig', array(
'contents' => $contents,
));
}
/**
* Lists all actuality.
*
* #Route("/actuality", name="content_actuality")
* #Method("GET")
*/
public function actualityAction()
{
$contents = $this->contentAction(6);
return $this->render('content/index.html.twig', array(
'contents' => $contents,
));
}
/**
* Lists all webinar.
*
* #Route("/webinar", name="content_webinar")
* #Method("GET")
*/
public function webinarAction()
{
$contents = $this->contentAction(4);
return $this->render('content/index.html.twig', array(
'contents' => $contents,
));
}
/**
* Lists all blog posts.
*
* #Route("/blog", name="content_blog")
* #Method("GET")
*/
public function blogAction()
{
$contents = $this->contentAction(7);
return $this->render('content/index.html.twig', array(
'contents' => $contents,
));
}
/**
* Creates a new content entity.
*
* #Route("/new", name="content_new")
* #Method({"GET", "POST"})
*/
public function newAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$content = new Content();
$form = $this->createForm('loic\ContentBundle\Form\ContentType', $content);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($content);
$em->flush($content);
return $this->redirectToRoute('content_show', array('id' => $content->getIdcontent()));
}
return $this->render('content/new.html.twig', array(
'content' => $content,
'form' => $form->createView(),
));
}
/**
* Displays a form to edit an existing content entity.
*
* #Route("/{id}/edit", name="content_edit")
* #Method({"GET", "POST"})
*/
public function editAction(Request $request, Content $content)
{
$deleteForm = $this->createDeleteForm($content);
$editForm = $this->createForm('loic\ContentBundle\Form\ContentType', $content);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('content_edit', array('id' => $content->getIdcontent()));
}
return $this->render('content/edit.html.twig', array(
'content' => $content,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}
/**
* Deletes a content entity.
*
* #Route("/{id}", name="content_delete")
* #Method("DELETE")
*/
public function deleteAction(Request $request, Content $content)
{
$form = $this->createDeleteForm($content);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->remove($content);
$em->flush();
}
return $this->redirectToRoute('content_index');
}
/**
* Creates a form to delete a content entity.
*
* #param Content $content The content entity
*
* #return \Symfony\Component\Form\Form The form
*/
private function createDeleteForm(Content $content)
{
return $this->createFormBuilder()
->setAction($this->generateUrl('content_delete', array('id' => $content->getIdcontent())))
->setMethod('DELETE')
->getForm()
;
}
}
My Bundle/Resources/config/routing.yml file is empty.

get the values of custom options

I'm trying to alter a price based on some custom options set. Therefore I'm trying to get the value a customer has entered, not the default values set in the backend. To do this I'm using the event catalog_product_get_final_price used in Mage_Bundle_Model_Product_Price. I have registered the following observer:
public function observer_callback($evt_obs)
{
$event = $evt_obs->getEvent();
$data = $event->getData();
/* #var $collection Mage_Catalog_Model_Resource_Product_Collection */
$collection = $data['collection'];
$items = $collection->getItems();
/* #var $item Mage_Catalog_Model_Product */
foreach ($items as $item) {
if ( $item->getName() == 'Bundel Test2') {
$options = $item->getCustomOptions();
/* #var $option Mage_Catalog_Model_Product_Option */
foreach ($options as $option) {
// Here I'm trying to get the value given by the user/customer
var_dump($option->getData());
}
}
}
return $this;
}
It is a custom option from a bundle type. So the product can't be configurable.
I'm new to magento so I'm probably missing something.
Can anyone help me?
Hope this piece of code can help you:
public function productFinalPrice($observer){
$product = $observer->getEvent()->getProduct();
$productType=$product->getTypeID();
if($productType == 'your_product_type')
{
$option = $product->getCustomOptions();
$searchedOption = null;
//search for your option;
foreach ($product->getOptions() as $o) {
if($o->getTitle()=="your_attribute_title" && $o->getType()=="your_type_of_option(eg. area"){
$optionId = $o->getOptionId();//got your searched optionId
break;
}
}
foreach($option as $key => $o) {
if($key == "option_".$optionId) {
$searchedOption = $o;
//here you get the option object with the values in it
}
}
$articleNumber = $searchedOption->getData('value'); // getthe value of your option
//calculate final price like you need it
$product->setFinalPrice($finalPrice);
}
return $this;
}
best regards

Magento: UPDATE and DELETE programatically from frontend, not working?

i am trying to update the product from frontend but it does not let me update all the fields of the product,
Here is the code of index controller FOR UPDATING:
public function updateVproductAction(){
if($addvp = $this->getRequest()->getPost())
{
/*********************** Product Update ***************************/
/**
* Get the resource model
*/
$resource = Mage::getSingleton('core/resource');
/**
* Retrieve the write connection
*/
$writeConnection = $resource->getConnection('core_write');
/**
* Retrieve our table name
*/
$table = $resource->getTableName('catalog/product');
/**
* Set the product ID
*/
$productId = $addvp['product']['id'];
/**
* Set the new SKU
* It is assumed that you are hard coding the new SKU in
* If the input is not dynamic, consider using the
* Varien_Db_Select object to insert data
*/
//$newSku = 'new-sku';
$sku = $addvp['product']['sku'];
$name = $addvp['product']['name'];
$description = $addvp['product']['description'];
$short_description = $addvp['product']['short_description'];
$weight = $addvp['product']['weight'];
$news_from_date = $addvp['product']['news_from_date'];
$news_to_date = $addvp['product']['news_to_date'];
$status = $addvp['product']['status'];
$price = $addvp['product']['price'];
$special_price = $addvp['product']['special_price'];
$tax_class_id = $addvp['product']['tax_class_id'];
$meta_title = $addvp['product']['meta_title'];
$meta_keyword = $addvp['product']['meta_keyword'];
$meta_description = $addvp['product']['meta_description'];
$stock_data = array(
'is_in_stock' => 1,
'qty' => $addvp['product']['stock_data']['qty']
);
$query = "UPDATE {$table} SET sku = '{$sku}', name = '{$name}', description = '{$description}', short_description = '{$short_description}', weight = '{$weight}', news_from_date = '{$news_from_date}', news_to_date = '{$news_to_date}', status = '{$status}', price = '{$price}', special_price = '{$special_price}', tax_class_id = '{$tax_class_id}', meta_title = '{$meta_title}', meta_keyword = '{$meta_keyword}', meta_description = '{$meta_description}', stock_data = '{$stock_data}' WHERE entity_id = "
.(int) $productId;
try
{
/**
* Execute the query
*/
$writeConnection->query($query);
echo "successfully updated product with ID: ". $productId ."<br />";
}
catch (Exception $e)
{
echo "Could not update product with ID: ". $productId ."<br />";
}
}
}
Can anyone please help and correct me where i am wrong ?
For Deleting a product i am simply calling this function from Helper :
public function vDeleteproduct($vprod){
//print_r($vprod); exit;
try
{
$product = Mage::getSingleton('catalog/product')->load($vprod);
Mage::dispatchEvent('catalog_controller_product_delete', array('product' => $product));
$product->delete();
//$product = Mage::getModel('catalog/product')->load($vprod)->delete();
echo "successfully deleted product with ID: ". $vprod ."<br />";
}
catch (Exception $e)
{
echo "Could not delete product with ID: ". $vprod ."<br />";
}
return;
}
It too don't work :(
Plz help.
FOR DELETING PRODUCTS :
i have found the answer for this by just giving access to delete from non-admin area (i.e. frontend). And it did the magic for me.
http://inchoo.net/ecommerce/magento/programming-magento/how-to-delete-magento-product-from-frontend-template-code-from-view-files/
Above link helped me to find the solution.

Get Full Billing Address for Paypal Express [Magento]

The paypal module tries to map the billing information that is returned (usually nothing) from Paypal over the billing information entered by the user during the checkout process. I've fond the code that does this in NVP.php model.
/**
* Create billing and shipping addresses basing on response data
* #param array $data
*/
protected function _exportAddressses($data)
{
$address = new Varien_Object();
Varien_Object_Mapper::accumulateByMap($data, $address, $this->_billingAddressMap);
$address->setExportedKeys(array_values($this->_billingAddressMap));
$this->_applyStreetAndRegionWorkarounds($address);
$this->setExportedBillingAddress($address);
// assume there is shipping address if there is at least one field specific to shipping
if (isset($data['SHIPTONAME'])) {
$shippingAddress = clone $address;
Varien_Object_Mapper::accumulateByMap($data, $shippingAddress, $this->_shippingAddressMap);
$this->_applyStreetAndRegionWorkarounds($shippingAddress);
// PayPal doesn't provide detailed shipping name fields, so the name will be overwritten
$shippingAddress->addData(array(
'prefix' => null,
'firstname' => $data['SHIPTONAME'],
'middlename' => null,
'lastname' => null,
'suffix' => null,
));
$this->setExportedShippingAddress($shippingAddress);
}
}
/**
* Adopt specified address object to be compatible with Magento
*
* #param Varien_Object $address
*/
protected function _applyStreetAndRegionWorkarounds(Varien_Object $address)
{
// merge street addresses into 1
if ($address->hasStreet2()) {
$address->setStreet(implode("\n", array($address->getStreet(), $address->getStreet2())));
$address->unsStreet2();
}
// attempt to fetch region_id from directory
if ($address->getCountryId() && $address->getRegion()) {
$regions = Mage::getModel('directory/country')->loadByCode($address->getCountryId())->getRegionCollection()
->addRegionCodeFilter($address->getRegion())
->setPageSize(1)
;
foreach ($regions as $region) {
$address->setRegionId($region->getId());
$address->setExportedKeys(array_merge($address->getExportedKeys(), array('region_id')));
break;
}
}
}
Has anyone had any success modifying this process to get back fuller billing information. We need to be able to send "Paid" invoices to customers who pay with Paypal, so we need to capture this information.
i have hacked a workaround for that problem. It is not the cleanest way but i can save the billing address data in the order after paying with PayPal. I spent 2 days working on it and at the end i coded only a few lines. I marked my workaround with the comment #103.
Override method of class Mage_Paypal_Model_Api_Nvp:
protected function _importAddresses(array $to)
{
// Original Code
//$billingAddress = ($this->getBillingAddress()) ? $this->getBillingAddress() : $this->getAddress();
// Workaround #103
if ($this->getBillingAddress())
{
$billingAddress = $this->getBillingAddress();
}
else
{
$chkout = Mage::getSingleton('checkout/session');
$quote = $chkout->getQuote();
$billingAddress = $quote->getBillingAddress();
$billingAddress->setData($billingAddress->getOrigData());
$session = Mage::getSingleton("core/session", array("name"=>"frontend"));
$session->setData("syn_paypal_original_billing_address", serialize($billingAddress->getOrigData()));
}
$shippingAddress = $this->getAddress();
$to = Varien_Object_Mapper::accumulateByMap($billingAddress, $to, array_flip($this->_billingAddressMap));
if ($regionCode = $this->_lookupRegionCodeFromAddress($billingAddress)) {
$to['STATE'] = $regionCode;
}
if (!$this->getSuppressShipping()) {
$to = Varien_Object_Mapper::accumulateByMap($shippingAddress, $to, array_flip($this->_shippingAddressMap));
if ($regionCode = $this->_lookupRegionCodeFromAddress($shippingAddress)) {
$to['SHIPTOSTATE'] = $regionCode;
}
$this->_importStreetFromAddress($shippingAddress, $to, 'SHIPTOSTREET', 'SHIPTOSTREET2');
$this->_importStreetFromAddress($billingAddress, $to, 'STREET', 'STREET2');
$to['SHIPTONAME'] = $shippingAddress->getName();
}
$this->_applyCountryWorkarounds($to);
return $to;
}
And override method in Mage_Paypal_Model_Express_Checkout:
public function returnFromPaypal($token)
{
$this->_getApi();
$this->_api->setToken($token)
->callGetExpressCheckoutDetails();
// import billing address
$billingAddress = $this->_quote->getBillingAddress();
$exportedBillingAddress = $this->_api->getExportedBillingAddress();
// Workaround #103
$session = Mage::getSingleton("core/session", array("name"=>"frontend"));
$dataOrg = unserialize($session->getData("syn_paypal_original_billing_address"));
if (true === is_object($billingAddress))
{
foreach ($exportedBillingAddress->getExportedKeys() as $key) {
if (array_key_exists($key, $dataOrg))
{
$billingAddress->setData($key, $dataOrg[$key]);
}
}
$this->_quote->setBillingAddress($billingAddress);
}
// import shipping address
$exportedShippingAddress = $this->_api->getExportedShippingAddress();
if (!$this->_quote->getIsVirtual()) {
$shippingAddress = $this->_quote->getShippingAddress();
if ($shippingAddress) {
if ($exportedShippingAddress) {
foreach ($exportedShippingAddress->getExportedKeys() as $key) {
$shippingAddress->setDataUsingMethod($key, $exportedShippingAddress->getData($key));
}
$shippingAddress->setCollectShippingRates(true);
}
// import shipping method
$code = '';
if ($this->_api->getShippingRateCode()) {
if ($code = $this->_matchShippingMethodCode($shippingAddress, $this->_api->getShippingRateCode())) {
// possible bug of double collecting rates :-/
$shippingAddress->setShippingMethod($code)->setCollectShippingRates(true);
}
}
$this->_quote->getPayment()->setAdditionalInformation(self::PAYMENT_INFO_TRANSPORT_SHIPPING_METHOD, $code);
}
}
$this->_ignoreAddressValidation();
// import payment info
$payment = $this->_quote->getPayment();
$payment->setMethod($this->_methodType);
Mage::getSingleton('paypal/info')->importToPayment($this->_api, $payment);
$payment->setAdditionalInformation(self::PAYMENT_INFO_TRANSPORT_PAYER_ID, $this->_api->getPayerId())
->setAdditionalInformation(self::PAYMENT_INFO_TRANSPORT_TOKEN, $token)
;
$this->_quote->collectTotals()->save();
}

Resources