Interspire Shopping Cart Blog
Modifying Shopping Cart: "Sort by Letter" in Categories
In the Interspire Shopping Cart Control Panel, we allow you to break down products and customers by those beginning with a specific letter (A-Z). Today we'll be looking at implementing a modification that allows you to bring this functionality to the front end of your store when viewing a list of products in a particular category. Essentially you'll be able to click a letter in a predefined list and view a listing of all products beginning with that specific letter.
The end result of our modification will be similar to the following:

We'll be diving right in to the Interspire Shopping Cart code to make these modifications, so there's an assumption that you have a small about of technical knowledge of PHP/HTML, but even if you don't have the experience, you should be able to progress.
The first thing to do is make sure you have a backup copy of the files we'll be editing in case anything goes wrong.
Open: includes/classes/class.category.php
Find:
$query = " SELECT p.*, floor(prodratingtotal/prodnumratings) AS prodavgrating, imageisthumb, imagefile, ".GetProdCustomerGroupPriceSQL()." FROM idn_products p INNER JOIN idn_categoryassociations ca ON (p.productid = ca.productid) LEFT JOIN idn_product_images pi ON (p.productid=pi.imageprodid) WHERE ca.categoryid='".(int)$this->GetId()."' AND p.prodvisible='1' AND (imageisthumb=1 OR ISNULL(imageisthumb)) ORDER BY ".$this->GetSortField().", p.prodname ASC ";
Replace it with:
$query = "
SELECT p.*, floor(prodratingtotal/prodnumratings) AS prodavgrating, imageisthumb, imagefile, ".GetProdCustomerGroupPriceSQL()."
FROM idn_products p
INNER JOIN idn_categoryassociations ca ON (p.productid = ca.productid)
LEFT JOIN idn_product_images pi ON (p.productid=pi.imageprodid)
WHERE ca.categoryid='".(int)$this->GetId()."' AND p.prodvisible='1' AND (imageisthumb=1 OR ISNULL(imageisthumb))
";
if(isset($_REQUEST['letter']) && $_REQUEST['letter'] != '') {
$letter = chr(ord($_REQUEST['letter']));
if($_REQUEST['letter'] == '0-9') {
$query .= " AND p.prodname NOT REGEXP('^[a-zA-Z]')";
}
else if(isc_strlen($letter) == 1) {
$query .= " AND p.prodname LIKE '".$GLOBALS['ISC_CLASS_DB']->Quote($letter)."%'";
}
}
$query .= "ORDER BY ".$this->GetSortField().", p.prodname ASC";
Find:
$query = " SELECT COUNT(p.productid) AS numproducts FROM idn_products p INNER JOIN idn_categoryassociations ca ON (p.productid = ca.productid) WHERE ca.categoryid='".(int)$this->GetId()."' and p.prodvisible='1' ";
Replace it with:
$query = "
SELECT COUNT(p.productid) AS numproducts
FROM idn_products p
INNER JOIN idn_categoryassociations ca ON (p.productid = ca.productid)
WHERE ca.categoryid='".(int)$this->GetId()."' and p.prodvisible='1'
";
if(isset($_REQUEST['letter']) && $_REQUEST['letter'] != '') {
$letter = chr(ord($_REQUEST['letter']));
if($_REQUEST['letter'] == '0-9') {
$query .= " AND p.prodname NOT REGEXP('^[a-zA-Z]') ";
}
else if(isc_strlen($letter) == 1) {
$query .= " AND p.prodname LIKE '".$GLOBALS['ISC_CLASS_DB']->Quote($letter)."%' ";
}
}
Now we need to modify a few of the panels that handle the next/previous and sorting options to ensure that the selected letter is also included. We'll also add some code to build our letter list and highlight the active item (fun times).
Open: includes/display/CategoryContent.php
Find:
// Should we hide the comparison button?
if(GetConfig('EnableProductComparisons') == 0 || $GLOBALS['ISC_CLASS_CATEGORY']->GetNumProducts() < 2) {
$GLOBALS['HideCompareItems'] = "none";
}
Under it add:
if($GLOBALS['ISC_CLASS_CATEGORY']->GetNumProducts() == 0 && !isset($_REQUEST['letter'])) {
$GLOBALS['HideLetterList'] = 'display: none';
}
else {
$letters = '0-9,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z';
$letters = explode(',', $letters);
$GLOBALS['LetterList'] = '';
if(!isset($_REQUEST['letter'])) {
$GLOBALS['AllActive'] = 'ActiveLetter';
}
$GLOBALS['AllLettersLink'] = CatLink($GLOBALS['CatId'], $GLOBALS['ISC_CLASS_CATEGORY']->GetName(), false);
foreach($letters as $letter) {
$active = '';
if(isset($_REQUEST['letter']) && $_REQUEST['letter'] == $letter) {
$active = 'ActiveLetter';
}
$link = CatLink($GLOBALS['CatId'], $GLOBALS['ISC_CLASS_CATEGORY']->GetName(), false, array(
'letter' => $letter
));
$GLOBALS['LetterList'] .= '<li class="'.$active.'"><a href="'.$link.'">'.isc_html_escape($letter).'</a></li>';
}
}
Find:
$start = max($GLOBALS['ISC_CLASS_CATEGORY']->GetPage()-$num_pages_either_side_of_current,1); $end = min($GLOBALS['ISC_CLASS_CATEGORY']->GetPage()+$num_pages_either_side_of_current, $GLOBALS['ISC_CLASS_CATEGORY']->GetNumPages());
Under it add:
$linkOptions = array(
'sort' => $GLOBALS['ISC_CLASS_CATEGORY']->GetSort()
);
if(isset($_REQUEST['letter']) && (isc_strlen($_REQUEST['letter']) == 1 || $_REQUEST['letter'] == '0-9')) {
$linkOptions['letter'] = $_REQUEST['letter'];
}
Open: includes/display/CategoryContent.php
Find:
$GLOBALS['PageLink'] = CatLink($GLOBALS['CatId'], $GLOBALS['ISC_CLASS_CATEGORY']->GetName(), false, array("page" => $page, "sort" => $GLOBALS['ISC_CLASS_CATEGORY']->GetSort()));
Replace with:
$GLOBALS['PageLink'] = CatLink($GLOBALS['CatId'], $GLOBALS['ISC_CLASS_CATEGORY']->GetName(), false, array_merge($linkOption, array('page' => $page)));
Find:
$GLOBALS['PrevLink'] = CatLink($GLOBALS['CatId'], $GLOBALS['ISC_CLASS_CATEGORY']->GetName(), false, array("page" => $GLOBALS['ISC_CLASS_CATEGORY']->GetPage()-1, "sort" => $GLOBALS['ISC_CLASS_CATEGORY']->GetSort()));
Replace with:
$GLOBALS['PrevLink'] = CatLink($GLOBALS['CatId'], $GLOBALS['ISC_CLASS_CATEGORY']->GetName(), false, array_merge($linkOption, array('page' => $GLOBALS['ISC_CLASS_CATEGORY']->GetPage()-1)));
Find:
$GLOBALS['NextLink'] = CatLink($GLOBALS['CatId'], $GLOBALS['ISC_CLASS_CATEGORY']->GetName(), false, array("page" => $GLOBALS['ISC_CLASS_CATEGORY']->GetPage()+1, "sort" => $GLOBALS['ISC_CLASS_CATEGORY']->GetSort()));
Replace with:
$GLOBALS['NextLink'] = CatLink($GLOBALS['CatId'], $GLOBALS['ISC_CLASS_CATEGORY']->GetName(), false, array_merge($linkOption, array('page' => $GLOBALS['ISC_CLASS_CATEGORY']->GetPage()+1)));
Open: includes/display/CategoryHeading.php
if($GLOBALS['EnableSEOUrls'] == 1) {
$GLOBALS['URL'] = CatLink($GLOBALS['CatId'], $GLOBALS['ISC_CLASS_CATEGORY']->GetName(), false);
}
else {
$GLOBALS['URL'] = $GLOBALS['ShopPath']."/categories.php";
$GLOBALS['HiddenSortField'] = "<input type=\"hidden\" name=\"category\" value=\"".$catPath."\" />";
}</pre>
Replace it with:
<pre name="code" class="php">if($GLOBALS['EnableSEOUrls'] == 1) {
$linkOptions = array();
if(isset($_REQUEST['letter']) && (isc_strlen($_REQUEST['letter']) == 1 || $_REQUEST['letter'] == '0-9')) {
$linkOptions['letter'] = $_REQUEST['letter'];
}
$GLOBALS['URL'] = CatLink($GLOBALS['CatId'], $GLOBALS['ISC_CLASS_CATEGORY']->GetName(), false);
}
else {
$GLOBALS['URL'] = $GLOBALS['ShopPath']."/categories.php";
$GLOBALS['HiddenSortField'] = "<input type=\"hidden\" name=\"category\" value=\"".$catPath."\" />";
if(isset($_REQUEST['letter']) && (isc_strlen($_REQUEST['letter']) == 1 || $_REQUEST['letter'] == '0-9')) {
$GLOBALS['HiddenSortField'] .= "<input type=\"hidden\" name=\"letter\" value=\"".isc_html_escape($_REQUEST['letter'])."\" />";
}
}
That's the end of the modifications we need to make to the PHP to get this all happening. Next up, we'll be implementing the modification in to your store design.
Open: templates/[name of the template you're using]/Panels/CategoryContent.html
Insert the HTML below where you'd like the list to appear:
<ul class="LetterSort" style="%%GLOBAL_HideLetterList%%"> <li class="%%GLOBAL_AllActive%%"><a href="%%GLOBAL_AllLettersLink%%">All</a></li> %%GLOBAL_LetterList%% </ul>
Open: templates/[name of the template you're using]/Styles/styles.css:
Add the following to the bottom of the stylesheet. (Adjust the layout, colours etc so it appears how you'd like it to)
.LetterSort, .LetterSort li {
list-style: none;
margin: 0;
padding: 0;
}
.LetterSort {
border: 1px solid #b9cfda;
background: #dcf0f5;
padding: 4px;
text-align: center;
}
.LetterSort li {
display: inline;
}
.LetterSort a {
padding: 0 3px;
}
.LetterSort li.ActiveLetter a {
font-weight: bold;
text-decoration: none;
}
If you save all of the changes we've made, and upload the new files to your store you should have a listing of the letters A through Z and the 'All' and '0-9' links on the category views. Clicking these letters will show you products beginning with the selected letter.
If there are any questions, or you can't get this to work correctly on your store simply leave a comment.
20 Payment Methods Strong - More to Come!

We've just reached 20 built in payment methods in Interspire Shopping Cart and we're still looking to build support for plenty more in to future releases, tackling a few for each release. We've been working hard to ensure that no matter where you're running your store from there's some type of built in payment method that'll work for your location.
The upcoming Interspire Shopping Cart 3.5 release contains several new providers, all of which have been highly demanded by customers:
- Moneris eSelectPlus DirectPost
- PayPal PayFlow Pro
- iDeal (Rabo Bank)
- Google Checkout (Full level 2 integration)
If you have any suggestions for additional payment providers that you'd like built in, simply leave a comment below or submit a feture request via the Interspire Client Area.
Customer Shipping Estimates in Interspire Shopping Cart 3.5
Customers can now choose to calculate the shipping cost of the current contents of their shoping cart from the "View Cart" page. Clicking on "Calculate Shipping & Handling" will present a small form that allows the customer to select their country, state and post code and then click "Calculate Shipping" to view a list of possible shipping methods and costs for their order.

A speedy AJAX request off to the server and in moments the customer will receive a list of shipping methods that can be applied to their order. From the list, a customer can then choose a shipping method and click "Update Shipping Cost" to view the adjusted subtotal of their order with this shipping method applied.

The address that the customer selected is then remembered so that when the contents of their cart changes and they wish to recalculate shipping again, they don't need to enter their details - it's all been pre-filled.
That just about covers all of the new shipping related features in the upcoming release of Interspire Shopping Cart 3.5, though there have also been a few other small tweaks/additions:
- Digital orders can now have a handling fee applied to them. This is particularly useful if you need to cover bandwidth costs for large digital product files.
- Portions of the shipping modules have been rewritten and cleaned up to meet the latest module documentation standards (which will be available on the Interspire Developers Network shortly)
Ideas for new Addons
The addons system in Interspire Shopping Cart has felt to me like it's been a bit underused so I've started making some simple addons that will hopefully help customers try and diagnose any problems they are having (and fix them) on the spot.
The first one I created was just a basic permissions checker so that you can make sure that all the things that need write access have it. Our installer and upgrade wizard check this as well but this addon will be useful if you move your site to another site, when you go live for example, to make sure everything is going to work ok.
The next addon I'm planning on writing is one that will check the hash of all the files in shopping cart to make sure that the file that is there is the one that is meant to be there. The most obvious use of this is to make sure that after you do an upgrade that you have uploaded all the files. This won't be able to account for changes to files that have been made intentionally but at least it will give customers a quick way to check what files are modified.
These two (assuming I finish the second one in time otherwise it will only be one) will be included for free with the next release of Interspire Shopping Cart.
Those are the two most obvious ideas to me for little addons that can quickly check parts of the store. I've got 1 more idea for an addon but that is a more ambitious project that I'm going to keep under my hat until I've had some time to test it out.
I'd be interested to see if anyone else has any other ideas for addons like this so if you have an idea that I might be able to quickly turn into an addon (I'm mainly thinking of ones that can check parts of your store to make sure everything is ok but all ideas are welcome) then please reply to this blog post.
Shipping Zones in Interspire Shopping Cart 3.5

In the next release of Interspire Shopping Cart, the entire shipping system has been redeveloped to include shipping zones (different shipping settings based on the location), table rates (highly configurable shipping) and much more. These are features that a lot of our customers have been requesting so I've tried to create a very flexible system but keep it easy to configure at the same time too.
A shipping zone is a list of regions (based on either one or more countries, states or postcodes) that your store can ship to and within each of these zones there can be many different shipping methods each with their own independent settings that will be used to calculate shipping for customers who fall in to that zone.
When creating or editing a shipping zone, there are various options that you can choose to configure this zone with:
- Zone Type
The type of shipping zone you wish this zone to be. Here you can choose a zone based one or more countries, a zone based on one or more states in different countries, or enter a list of postcodes that apply to a particular country (with wildcards too, so you don't need to enter every single post code) - Free Shipping
Free shipping can now be enabled or disabled per shipping zone and the order total to qualify for free shipping can also be adjusted per zone. - Handling
You can choose to not apply any handling fee for this shipping zone, choose to apply a common handling fee for all shipping methods that are in this zone or specify a handling fee for each of the different methods in this zone. - Shipping Methods
An unlimited amount of shipping methods can be created and customised for each shipping zone. (More information on this below)

Shipping zones are automatically calculated for each customer during the checkout process based on the best/closes matching zone. This allows you to create very powerful shipping rules/calculations based on the destination. The way this works is as follows:
- If the customer's address matches a postcode in a postcode based zone, this shipping zone will be used. If there is a full match of the postcode rather than a wildcard match, the full match will be used over the wildcard based match.
- If the customer's address matches a state in a state based zone, this shipping zone will be used.
- If the customer's address matches a country in a country based zone, this shipping zone will be used.
- If there are no matches at all, the customer will fall back in to the "Default Shipping Zone" that applies to every other location.

When editing a zone, the "Shipping Methods" tab allows you to manage the shipping methods for this zone. If there are multiple shipping methods in a zone, the customer is given the choice of which shipping method to apply to their order. This allows you to offer services such as express shipping, registered shipping or standard shipping and allow the customer to choose which applies to their order.
Creating a shipping method within a zone is just as simple as configuring shipping before, you select the shipping module that this method will use, you can optionally change the display name of this shipping method, and then you configure the shipping method properties/settings.

As you can see - there's quite a lot to the new shipping zones/methods functionality that's vastly improved over our old shipping support in Interspire Shopping Cart. Any feedback is always welcomed. Shortly I'll have an overview of the new customer shipping estimates feature on the front end of the store and two features that everyone's been waiting for: single page & guest checkout.
Accepting coupon codes with the merchant-calculation-callback API in Google Checkout
It looks like there is a bug with the PHP library for Google Checkout supplied by Google with causes all merchant calculation callback requests for gift certificates to return invalid xml.
Despite what the comments in the bug report say, it looks like that bug made it into the 1.2.5c.zip which is currently available.
Welcome to the Interspire Developer Network
As well as articles, we have a tech-orientated blog setup for each of our products where Interspire developers will post details on upcoming features as they're being built. You can of course leave a comment at the end of every post with your questions or comments.
Thanks, and welcome to the Interspire Developer Network!
Mitchell Harper
Product Development Manager
Interspire Pty. Ltd.
