Ok, I guess the first question is, “why would you?”
Well, as it happens, there are affiliate marketers that will launch the Magento site in their iFrame. One such example is Payment Wall (http://www.paymentwall.com).
So the problem here is that IE (pretty much all versions) won’t pass the Magento session cookie back to the server if the site’s running in an iFrame. No session cookie, no login, no how.
The fix is pretty simple – though it took me a long time to find it!
Add the following to your .htaccess file and, poof, IE sends your cookies back and everybody’s happy!
# Added the following header to enable cookies coming to the site while being hosted in an iframe Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR NID CUR ADM DEV OUR BUS\""
Latching onto magento events is very simple – however, after a lot of searching I couldn’t find a lot of resources documenting how to do it.
I’ll more than likely research magento events a bit more in a later post, but for reference here is a cheatsheet of dispatched magento events Credit: Branko Ajzele of Active Codeline. (a little outdated – from version 1.3)
Let’s keep it simple…
The namespace for my module will be “Ifuel”
The name of my module will be “Aftercheckout”
I will want to latch onto one of the many dispatched events during the checkout process (to name a few: checkout_controller_onepage_save_shipping_method, sales_order_place_after, checkout_type_onepage_save_order_after, checkout_onepage_controller_success_action). For reference about checkout events, check out Yireo’s Events with Magento Checkout. For this module, I want to execute my class after the order has been completely checked out – so I will latch onto the “checkout_onepage_controller_success_action” event.
Here is the bare bones of my config.xml file located at /app/code/local/Ifuel/Aftercheckout/etc/config.xml. Make sure to read the comments in the code, they’re helpful. P.s. don’t forget to declare your module in /app/etc/modules/
<?xml version="1.0"?>
<config>
<modules>
<Ifuel_Aftercheckout>
<version>0.1.0</version>
</Ifuel_Aftercheckout>
</modules>
<frontend>
<routers>
<aftercheckout>
<use>standard</use>
<args>
<module>Ifuel_Aftercheckout</module>
<frontName>aftercheckout</frontName>
</args>
</aftercheckout>
</routers>
</frontend>
<global>
<events>
<checkout_onepage_controller_success_action>
<!-- The name of the event you're latching
onto, all lowercase -->
<observers>
<sendaftercheckout>
<!-- Some description for your event
listener, doesn't matter what it is
as long as there are no spaces and
it's all lowercase -->
<type>singleton</type>
<class>aftercheckout/doaftercheckout</class>
<!-- the <frontName> of your module
(aftercheckout) slash the name of your Event
Listener (doaftercheckout), case sensitive.
In this case it will be located at
/app/code/local/Ifuel/Aftercheckout/Model
/Doaftercheckout.php -->
<method>sendSomethingAfterCheckout</method>
<!-- the public function name within your
class that will listen for this event,
case sensitive -->
</sendaftercheckout>
</observers>
</checkout_onepage_controller_success_action>
</events>
</global>
</config>
Now you’ll want to create your Listener. In my case it will be located at /app/code/local/Ifuel/Aftercheckout/Model/Doaftercheckout.php. Make sure to create a public function within your new class that you’ve defined in the <method> node of your config.xml file. The contents of my listener will look something like:
<?php
class Ifuel_Aftercheckout_Model_Doaftercheckout {
public function sendSomethingAfterCheckout() {
Mage::log("I am now able to execute something after checkout");
}
}
And that’s it. That’s the bare bones of latching onto a magento event. It’s that simple. If you have any questions, feel free to ask them in a comment.
Locate: /app/design/frontend/base/default/layout/googleanalytics.xml
Replace With:
<default> <!-- Mage_GoogleAnalytics --> <reference name="before_body_end"> <block type="googleanalytics/ga" name="google_analytics" as="google_analytics" /> </reference> </default> </layout>
^ This will move the Google Analytics code to before the closing of the HTML </body> tag (for faster page load times)
Locate: /app/code/core/Mage/GoogleAnalytics/Block/ga.php
Replace at around LINE 171:
<!-- BEGIN GOOGLE ANALYTICS CODE -->
<script type="text/javascript">
//<![CDATA[
var _gaq = _gaq || [];
_gaq.push(["_setAccount", "' . $this->getAccount() . '"]);
_gaq.push(["_trackPageview", "'.$this->getPageName().'"]);
(function() {
var ga = document.createElement(\'script\'); ga.type = \'text/javascript\'; ga.async = true;
ga.src = (\'https:\' == document.location.protocol ? \'https://ssl\' : \'http://www\') + \'.google-analytics.com/ga.js\';
(document.getElementsByTagName(\'head\')[0] || document.getElementsByTagName(\'body\')[0]).appendChild(ga);
})();
//]]>
</script>
<!-- END GOOGLE ANALYTICS CODE -->
While working on some ASP.NET form validation today, I came across a great regex tester / builder called Regex Hero. The main feature is their .NET Regex Tester, a Silverlight app, and while it requires registration (Google, Yahoo, AOL, OpenID, etc.), this gives you the ability save your regular expressions to your personal library on the site. You can also use the site’s public library, but contributions are a little anemic at this point. The app has a few other nice features, including real-time expression matching, a built-in regular expression reference, .NET code generation, and performance benchmarking.
The app is free for basic use, but it will nag you every 5 minutes to buy the paid version. The full version is $20 and comes with the nice addition of Intellisense-like code completion and a desktop version of the app. Future upgrades are free for the full version.
Things you Should Know Before Developing With Magento
—————————————————————-
Now being more experienced with Magento we definitely feel the need to share our findings with the masses. We only wish we knew these things prior to trying to the hack the crap out of magento to make it do what we want.
Turning on Template Path Hints
If you’re new to Magento you will realize that it is quite a hassle to understand the logic behind the file structure and block structure of a magento layout when trying to customize your storefront. There is a very under-publicized built-in magento feature that we’d like to share with you that should help you on your journey. Magento actually has the ability to display hints showing where the different files of your layout is contained so that you can edit it.
To achieve this, Follow these simple steps…
Now when you navigate to your storefront you’ll see a bunch of red boxes displaying the underlying structure of the pages regarding templates and blocks.
Note: This should only be used in a development environment, considering this will make your storefront look hideous with big red blocks everywhere.
—————————————————————-
Never edit core files
Problem: When developing with Magento you will find it necesary to edit core files to achieve certain functionality. However, By editting a core file you are basically blocking yourself into a corner. DON’T DO IT! If you ever wish to upgrade at any point in the future you will not be able to because any changes that you have made in the core files will be overwritten. Never fear, there is a way around this.
Solution: The “local” folder (\app\code\local\) is your saviour. Say you need to edit the “Shipping.php” file located at “\app\code\core\Mage\Shipping\Model\Shipping.php” you can create the same basic directory structure in the local folder and copy and paste the Shipping.php file into the new directory. So, your new “local” Shipping.php file will be located at \app\code\local\Mage\Shipping\Model\Shipping.php. You will be able to safely edit anything and everything within that local file and never have to worry about it being overwritten during an upgrade. This trick works because Magento will look for files in a local directory before looking for a file in the core directory.
Note: The above solution will only overpower files that exist within the \app\code\core\ or \app\code\community\ directories. Also note that any files within your magento theme (\app\design\frontend\default\YOURTHEME\) or (\skin\frontend\default\YOURTHEME\) are not considered core files and you are free to edit them as you please.
These are just to name a few… Follow our blog to read more!
In this day and age of many online scams it is necessary for every successful eCommerce storefront to have SSL Authentication for online purchases. These SSL Certificates are usually only good for one specific URL and are very picky as to whether or not the URL has to have “www.” attached to it. Here is a simple solution that we’ve created to force appending “www.” to the URL when trying to access a “https://” (http secure) address.
Some things you must know:
First, Create an .htaccess file if you don’t already have one. Then enter one of the following solutions based on your needs.
# Redirect non-www to www
Options +FollowSymLinks
# turn mod_rewrite on
RewriteEngine On
# If https send to https://www.
RewriteCond %{HTTP_HOST} ^YOURDOMAIN.COM$ [NC]
RewriteCond %{HTTPS} on
RewriteRule ^(.*)$ https://www.YOURDOMAIN.COM/$1 [R=301,L]
# Redirect non-www to www
Options +FollowSymLinks
# turn mod_rewrite on
RewriteEngine On
# If https send to https://www.
RewriteCond %{HTTP_HOST} ^YOURDOMAIN.COM$ [NC]
RewriteCond %{HTTPS} on
RewriteRule ^(.*)$ https://www.YOURDOMAIN.COM/$1 [R=301,L]
# If http send to http://www.
RewriteCond %{HTTP_HOST} ^YOURDOMAIN.COM$ [NC]
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ http://www.YOURDOMAIN.COM/$1 [R=301,L]
With these examples it is necesary for you to make sure that you replace “YOURDOMAIN.COM” to reflect the actual domain name of the website you will be using this on. Once you have done this, save your .htaccess file and upload it to the base directory of your website.
Believe it or not a few search engines distinguish your website as two different websites http://www.yourdomain.com and http://yourdomain.com. It is often considered good practice to add this htaccess rewrite to any website you create so that you can rest assured that your website is not being indexed as two different websites.
If you’re running Magento on OpenSuse (or any platform for that matter), you can really benefit from turning on http compression, especially to remote areas or from hosts that have limited bandwidth.
There are a lot of references out there on this, but you have to sort of pull it all together on your own for this particular combination.
Verify that mod_deflate has been installed in Apache:
1. Open /etc/sysconfig/apache2.
2. Find the line that starts:
APACHE_MODULES=…
3. Verify that “deflate” is one of the modules listed (may not be in alphabetical order).
Now, enable compression in the virtual host file for your site:
1. Open /etc/apache2/vhosts.d/.conf.
2. Add the following lines inside the tag:
<IfModule mod_deflate.c>
DeflateBufferSize 32768
DeflateCompressionLevel 5
<Location />
SetOutputFilter DEFLATE
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|tiff)$ no-gzip dont-vary
# Header append Vary User-Agent env=!dont-vary
</Location>
</IfModule>
Now restart Apache using:
sudo /etc/init.d/apache2 restart
and you should be in business!
Validate that compression is working
A great, handy online site that you can use to validate that compression is working is http://www.gidnetwork.com/tools/gzip-test.php.
=============================
“e-commerce done right”
http://www.ifuelinteractive.com
On many occasions we’ve needed to clear out the test data from a Magento site in preparation for going live. It’s not as trivial as you’d think, but I found a nice sql script thanks to Elias Interactive that was just what I needed. I’ve removed a bit as I didn’t need to reset all the order numbers, etc.
updated 6/30/2010 for Magento 1.4
-- Reset Magento TEST Data SET FOREIGN_KEY_CHECKS=0; -- reset dashboard search queries TRUNCATE `catalogsearch_query`; ALTER TABLE `catalogsearch_query` AUTO_INCREMENT=1; -- reset sales order info TRUNCATE `sales_order`; TRUNCATE `sales_order_datetime`; TRUNCATE `sales_order_decimal`; TRUNCATE `sales_order_entity`; TRUNCATE `sales_order_entity_datetime`; TRUNCATE `sales_order_entity_decimal`; TRUNCATE `sales_order_entity_int`; TRUNCATE `sales_order_entity_text`; TRUNCATE `sales_order_entity_varchar`; TRUNCATE `sales_order_int`; TRUNCATE `sales_order_text`; TRUNCATE `sales_order_varchar`; TRUNCATE `sales_flat_quote`; TRUNCATE `sales_flat_quote_address`; TRUNCATE `sales_flat_quote_address_item`; TRUNCATE `sales_flat_quote_item`; TRUNCATE `sales_flat_quote_item_option`; TRUNCATE `sales_flat_order_item`; TRUNCATE `sendfriend_log`; TRUNCATE `tag`; TRUNCATE `tag_relation`; TRUNCATE `tag_summary`; TRUNCATE `wishlist`; TRUNCATE `log_quote`; TRUNCATE `report_event`; TRUNCATE `sales_flat_quote_payment`; TRUNCATE `sales_flat_quote_shipping_rate`; TRUNCATE `log_url`; TRUNCATE `log_url_info`; TRUNCATE `log_visitor`; TRUNCATE `log_visitor_info`; ALTER TABLE `sales_order` AUTO_INCREMENT=1; ALTER TABLE `sales_order_datetime` AUTO_INCREMENT=1; ALTER TABLE `sales_order_decimal` AUTO_INCREMENT=1; ALTER TABLE `sales_order_entity` AUTO_INCREMENT=1; ALTER TABLE `sales_order_entity_datetime` AUTO_INCREMENT=1; ALTER TABLE `sales_order_entity_decimal` AUTO_INCREMENT=1; ALTER TABLE `sales_order_entity_int` AUTO_INCREMENT=1; ALTER TABLE `sales_order_entity_text` AUTO_INCREMENT=1; ALTER TABLE `sales_order_entity_varchar` AUTO_INCREMENT=1; ALTER TABLE `sales_order_int` AUTO_INCREMENT=1; ALTER TABLE `sales_order_text` AUTO_INCREMENT=1; ALTER TABLE `sales_order_varchar` AUTO_INCREMENT=1; ALTER TABLE `sales_flat_quote` AUTO_INCREMENT=1; ALTER TABLE `sales_flat_quote_address` AUTO_INCREMENT=1; ALTER TABLE `sales_flat_quote_address_item` AUTO_INCREMENT=1; ALTER TABLE `sales_flat_quote_item` AUTO_INCREMENT=1; ALTER TABLE `sales_flat_quote_item_option` AUTO_INCREMENT=1; ALTER TABLE `sales_flat_order_item` AUTO_INCREMENT=1; ALTER TABLE `sendfriend_log` AUTO_INCREMENT=1; ALTER TABLE `tag` AUTO_INCREMENT=1; ALTER TABLE `tag_relation` AUTO_INCREMENT=1; ALTER TABLE `tag_summary` AUTO_INCREMENT=1; ALTER TABLE `wishlist` AUTO_INCREMENT=1; ALTER TABLE `log_quote` AUTO_INCREMENT=1; ALTER TABLE `report_event` AUTO_INCREMENT=1;
Just run this script against the Magento database (make a backup first!).
=============================
“e-commerce done right”
http://www.ifuelinteractive.com
I’ve been looking for a way to log all the sql that Magento is running for debugging purposes. There are a number of logging mechanisms built in to Magento, but none that would allow you to log all the sql that’s being run. Finally, I’ve found a simple change that can be made to a core file (I know, not ideal because it will get overwritten when you upgrade Magento – but it’s only a few lines in one file).
1. Open the file <magentoroot>/lib/Varien/Db/Adapter/Pdo/Mysql.php.
2. Add the following lines:
$code = 'SQL: ' . $sql . "\r\n";
if ($bind) {
$code .= 'BIND: ' . print_r($bind, true) . "\r\n";
}
$this->_debugWriteToFile("[".date('Y-m-d H:i:s')."] ".$code);
Add it to the “query” function as shown below:
public function query($sql, $bind = array())
{
$this->_debugTimer();
try {
$sql = (string)$sql;
if (strpos($sql, ':') !== false || strpos($sql, '?') !== false) {
$this->_bindParams = $bind;
$sql = preg_replace_callback('#(([\'"])((\\2)|((.*?[^\\\\])\\2)))#', array($this, 'proccessBindCallback'), $sql);
$bind = $this->_bindParams;
}
$code = 'SQL: ' . $sql . "\r\n";
if ($bind) {
$code .= 'BIND: ' . print_r($bind, true) . "\r\n";
}
$this->_debugWriteToFile("[".date('Y-m-d H:i:s')."] ".$code);
$result = parent::query($sql, $bind);
}
catch (Exception $e) {
$this->_debugStat(self::DEBUG_QUERY, $sql, $bind);
$this->_debugException($e);
}
$this->_debugStat(self::DEBUG_QUERY, $sql, $bind, $result);
return $result;
}
My previous post on adding to the cart with ajax in Magento has generated enough interest – and pointed out enough flaws in my overly complex code – that I’ve decided to put together a simplified version, so here goes:
Step 1: Create the server side script.
My sample script is called “addToCartTest.php” and I put it in a /scripts folder in the root of my Magento installation.
< ?php
include_once '../app/Mage.php';
Mage::app();
try{
// usage /scripts/addToCartTest.php?product_id=838&amp;amp;amp;amp;qty=1
$product_id = '';
// get query string
if (!isset($_GET['product_id'])) { $product_id = ''; } else { $product_id = $_GET['product_id']; }
if (!isset($_GET['qty'])) { $qty = '1'; } else { $qty = $_GET['qty']; }
$product = Mage::getModel('catalog/product')->load($product_id);
$session = Mage::getSingleton('core/session', array('name'=>'frontend'));
$cart = Mage::helper('checkout/cart')->getCart();
$cart->addProduct($product, $qty);
$session->setLastAddedProductId($product->getId());
$session->setCartWasUpdated(true);
$cart->save();
$result = "{'result':'success'}";
echo $result;
} catch (Exception $e) {
$result = "{'result':'error'";
$result .= ", 'message': '".$e->getMessage()."'}";
echo $result;
}