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.
Creating segment from the API
Over the coming days I will be explaining on how to manipulate segments using the API.
Today, I will explain to you on how to create segment using the API. When using the API to create segments, you will be able to create a more powerful filtering than using the standard UI in IEM.
require_once (SENDSTUDIO_API_DIRECTORY . '/segment.php');
$segment = new Segment_API();
// Owner ID of the new segment
$segment->ownerid = 1;
// Current time
$segment->createdate = AdjustTime();
// Segment name
$segment->segmentname = 'Test Segment';
// Segment search information
$segment->searchinfo = array(
// The list of which the segment will apply it's rule
// This is an array of list Ids
'Lists' => array(49),
// Segment rules
'Rules' => array(
array(
'type' => 'group',
'connector' => 'and', // Connector for the next group
'rules' => array(
// --- These are the rules that will be applied to the list
array(
'type' => 'rule',
'connector' => 'or', // Connector for the next rule
'rules' => array(
'ruleName' => 'email',
'ruleOperator' => 'like',
'ruleValues' => array('someemail@somecompany.com')
)
),
array(
'type' => 'rule',
'connector' => 'and', // Connector for the next rule
'rules' => array(
'ruleName' => 20, // Custom field ID
'ruleOperator' => 'like',
'ruleValues' => array('somename')
)
)
// ---
)
)
)
);
$status = $segment->Create();
if (!$status) {
print "Cannot create segment";
} else {
print "Segment created with the following id: {$segment->segmentid}";
}
Analysis of the code:(1) Include segment API
(3) Instantiate new Segment API
(5) Set up owner ID
(8) Specify current time (use AdjustTime() function to do this)
(11) Segment name
(14) Specify the segment search info
(17) Specify the list Ids 00 – These are arrays
(20) Specify the rules
The following rules apply:
- Rule Type can either be: “group” or “rule”
- Rule Connector can either be: “and” or “or”
-
“ruleName”: is the fields that the rule will be applied to... This can either be a special fields, or a custom field ID. Currently available special fields are:
- email: Filter by email
- format: Filter by format (“h” or “t”)
- status: Filter by format (“b”, “u”, or “a” translate to “bounced”, “unsubscribed”, “active”)
- confirmation: Filter by conformation status (“1” or “0”)
- confirmation: Filter by conformation status (“1” or “0”)
- subscribe: Filter by subscribe data (mm/dd/yyyy)
- link: Filter by have/have not clicked a particular link (it accept a link id)
- campaign: Filter by have/have not opened a particular campaign (it accept a newsletterid)
- subscriberid: Filter by subscriber id
-
”ruleOperator” value will depends on the context of the “ruleName”, so not all of the following rules will be available for each “ruleName”
- equalto
- notequalto
- like
- notlike
- between
- greaterthan
- lessthan
What's using my space
du -s * | sort -n | tailIf you run this in a directory it will print out the top files and directories under the current directory using up space. For example, yesterday I started playing with a Fuzzing tool for testing shopping cart. Unfortunately Xdebug was setup to save execution traces and profiling files on the server. The end result after about an hour of fuzzing was a directory with about 67 Gigs worth of files in it. That command let me track down the cause of the hard drive filling up quickly.
The CAN SPAM Act and what it means to us.
What is spam?
Spam is the unsolicited sending of electronic mail to anyone. This can be to individual addresses or to an entire email marketing list consisting of millions of contacts. Research has determined that over 50% of all email transactions taken place in the United States are considered to be spam. This is a very serious issue because not only does it slow down our days clearing out our inbox's of spam (potentially missing and deleting valid emails) it is also placing a lot of overhead on the network system as a whole.
With all the complaints about spam, receiving it, having your own valid emails picked up as it, worrying about the legal ramifications on it, I thought it a good idea to do some research for you and find out exactly what the deal was and what we can do to avoid being marked as a spammer and potentially having our server black listed.
Step up the CAN SPAM Act 2003
In an attempt to try to control spamming in some way the Federal Trade Commission of America created the CAN SPAM Act. This Act is quite simple in its nature and easy for you to abide by. It does not stop you from sending unsolicited email. It simply tries to regulate it.
What this means is that you can still send emails to people that have not explicitly signed up to your mailing list but you must abide by the laid out rules in order to avoid the penalties.
What are these rules?
The rules are simple and very easy to follow:
- Do not set false or misleading header information.
- That is your 'From' address, domain name etc must be correct. You cannot say you are from www.abc.com when you are in fact from www.xyz.com.
- This does not mean that you always have to send from the same domain that Interspire Email Marketer is located on, it just means that the domain and person that you are saying you are must be real. So you can still send on your clients behalf from your own installation, just make sure they are real.
- Do not set false or misleading subject lines
- This is simple. Make sure that your emails subject line and the content of the email are one and the same. Do not try to trick your contacts into opening the email.
- This does not mean that you are not allowed to be creative with your subject lines. They are the first point of contact with your contacts and as such must be catchy and make your contacts want to open the email. But being clever with your subject lines and being deceitful are completly different matters.
- Give your contacts an opt out method
- Once again, this is a pretty simple step. Make sure that you include in every email that you send out a way to unsubscribe.
- Interspire Email Marketer provides you with 2 different methods to do this. The first is simply including the unsubscribe link (%%unsubscribelink%%) in your email. The second method is to create an unsubscribe form and place this on your website. From there you can link to the form from your email campaigns. Both methods are equally as valid.
- You must identify yourself and your company clearly and let the contact know that this email is an advertisement.
- Your email campaign needs to clearly inform your contact that you are trying to sell them something (if you are) and provide them with your companies name and a valid physical postal address.
What penalties can you expect for these?
"Each violation of the above provisions is subject to fines of up to $11,000. Deceptive commercial email also is subject to laws banning false or misleading advertising."
What this means is that for every rule that you break in relation to this Act you could potentially be liable for $11,000. If you are found guilty of not allowing subscribers to unsubscribe as well as falsifying your from address so that your subscribers cannot get in touch with you then you could be liable for up to $22,000 in fines.
What does all this mean to me?
This can sound pretty full on but remember, if you do the right thing then you have nothing to worry about. Make sure you always have your details in your emails (Place them in your footer to be included), make sure you are honest with your contacts and you will be fine.
If you have any questions or comments at all about this please don't hesitate to post a comment bellow and I will get back to you.
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.
Buttons available in the WYSIWYG Editor
When creating a new toolbar, the Name of the button should be used. To create a toolbar:
$rows = array (
"Fullscreen,Undo,Redo,Paste,-,OrderedList,UnorderedList,-,JustifyLeft,JustifyCenter,JustifyRight,JustifyFull,-,Custominsert,Help",
"Spellcheck,-,RemoveFormat,-,Indent,Outdent,-,SubScript,SuperScript,CreateLink,CreateEmailLink,Anchor,-,Image,-,Table,Form,Pageproperties",
"Fontname,Fontsize,Formatblock,Fontcolor,Highlight,HR,Insertchars,Showborders",
);
$myDE->AddToolbar("Simple", implode(",|,", $rows));
| Image | Description | Name of the button |
|
Toggle absolute positioning on and off. | Toggleposition |
|
Centers the paragraph. | JustifyCenter |
|
Aligns the paragraph to the left. | JustifyLeft |
|
Aligns the paragraph to the right. | JustifyRight |
|
Creates an anchor element. | Anchor |
|
Bold font format. | Bold |
|
Inserts custom HTML on the cursor position. | Custominsert |
|
Inserts a special character. | Insertchars |
|
Clears the HTML code. | Clearcode |
|
Copies the selected text to the clipboard. | Copy |
|
Cuts the selected text to the clipboard. | Cut |
|
Decreases the indent of the text. | Outdent |
|
Creates a link to an email address. | CreateEmailLink |
|
Finds and/or replaces text in the editor. | Findreplace |
|
Flash Manager, to handle Flash objects. | Flash |
|
Changes the font color. | Fontcolor |
|
Selects the font face. | Fontname |
|
Selects the font size. | Fontsize |
|
Selects the element format to be used (paragraph, heading, etc). | Formatblock |
|
Inserts forms and various form elements. | Form |
|
Help for the WYSIWYG editor. | Help |
|
Changes the font highlight. | Highlight |
|
Inserts an horizontal line. | HR |
|
Image Manager, to insert images. | Image |
|
Increases the indent of the text. | Indent |
|
Italics font format. | Italic |
|
Justifies the paragraph. | JustifyFull |
|
Creates a hiperlink. | CreateLink |
|
Media Manager, to handle movies and sound. | Media |
|
Ordered list (list with numbers). | OrderedList |
|
Modifies the page properties - title, description and keyword. | Pageproperties |
|
Shows/hides paragraph endings and line breaks. | Paragraph |
|
Dropdown with paste options. | Paste |
|
Redoes the latest editing actions. | Redo |
|
Removes the style associated with the element. | RemoveFormat |
|
Shows/hides the borders around the tables. | Showborders |
|
Spell checks the document. | Spellcheck |
|
Strike through font format. | Strikethrough |
|
Selects a style to be attached to an element. | Styles |
|
Subscript font format. | SubScript |
|
Superscript font format. | SuperScript |
|
Creates a new table. | Table |
|
Inserts a text box. | Inserttextbox |
|
Underline font format. | Underline |
|
Undoes the latest editing actions. | Undo |
|
Unordered list (list with bullets). | UnorderedList |
How to check if the WYSIWYG Editor is finished loading
So checking if the editor is already loaded is merely checking if the designMode is ready. And better yet, there's no need to know how to reach through a designMode object as the WYSIWYG editor has functions that access it. If you need some action to take place right after the editor is loaded, this JavaScript snippet can help you. It assumes:
- the editor is instantiated as wysiwyg
- the maximum number of tries will be 50 - totalTries<50
- the time between tries will be 200ms - window.setTimeout('tryToSetFocus()', 200)
- you want to trigger a function called DoSomething() once the editor is available These options gives this script a maximum of 1000ms (50 tries times 200ms each) of wait for the editor to load - after the 1000ms DoSomething will not be triggered.
var totalTries = 0;
function tryToSetFocus() {
totalTries++;
try {
wysiwyg.getHTMLContent();
} catch (E) {
if (totalTries<50) {
tryToWrite = window.setTimeout('tryToSetFocus()', 200);
}
return;
}
DoSomething();
}
Adding categories support into Website Publisher
Adding the Categories functionality into Interspire Website Publisher was quite easy, this is how I did it.
The first thing I did was to create the class that interfaces between the user and Interspire Website Publisher. This class extends upon the API that already has most of the functionality I need (such as loading specific categories or saving them). This provides me with a quick way of getting things going. So, I call it iwp_admin_categories and save it as /admin/includes/classes/class.categories.php.
class iwp_admin_categories extends iwp_categories {
private $Instance;
// etc...
}
With this newly created class I can now begin adding functions as needed. For viewing the categories list, I add a function named View and give it no parameters. This is picked up automatically by Interspire Website Publisher, when the action in the URL is view. What I mean specifically, is the URL structure is dynamic, meaning you can add functions and then use those within the URL. Adding a function named View() will allow an action of view to be used.
public function View() {
echo "Within the View() function.";
}
Within my View function now, I need to designate the template file to be displayed, and set any content that needs to be displayed within it (such as the categories list). The template file will handle the loading and displaying of the categories, interfacing back into this categories object. We've designed the template engine to handle almost any use-case, the categories page is a special instance that we've had to accommodate for specifically. With the use of a foreach loop, and the wonders of recursion, we have a quick and easy way of building the category tree.
{foreach from=%admin_categories.GetCategoryList:'0' key=k item=row id=ViewListLoop}
// Formatting the output of each category here
// Recurse into any children
{recursion=$row.categoryid}
{/foreach}
Regarding the template code specifically, we will be releasing in-depth documentation for it with the release of Interspire Website Publisher and also within further posts in the near future.
All in all, this presents us with a category list that works wonders.
Questions? Comments? I'd love to hear them, just post below and I'll be sure to read and respond.
Submitting subscription forms transparantly to Interspire Email Marketer
There are two ways to achieve this:
- Using the API -- Which means:
- You need to understand how the API works
- When the subscription process change, your code will also be obsolete (and you need to re-write them)
- Using a wrapper that act as an intermediate between your subscription HTML form and the Interspire Email Marketer
On this occasion, I will talk about how to use a wrapper to achieve this. This is because it is fast, and does not require any knowledge of the Interspire Email Marketer inner workings, and in addition, it will shield your code from any changes that are made in the subscription process.
A quick illustration on how this will work is as follow:
+----------------+ +-------------------+ +-----------------------------+ | Your HTML Page | ==> | Your PHP page | ==> | Interspire Email Marketter | | | <== | | <== | | +----------------+ +-------------------+ +-----------------------------+
First of all, you need to create a subscription web-form.... Please de-select the option to use CAPTCHA (As we want to keep the subscription form as simple as possible). After the creation is complete, you need to take note of the form ID.
Now the next process is to create your subscription form:
<html>
<form action="forms_wrapper.php" method="POST">
Please enter your email address:
<input type="text" name="txtEmail" />
<input type="button" name="cmdSubscribe" value="Subscribe" />
</form>
</html>
And create a PHP wrapper file that will send the request to Interspire Email Marketer:
<?php
$data = array(
'format' => 'h',
'email' => $_POST['txtEmail']
);
$ch = curl_init(CONST_YOUR_IEM_URL . '/form.php?form=' . CONST_YOUR_FORMID);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$response = @curl_exec($ch);
curl_close($ch);
// Response will contains Interspire Email Marketer response to your subscription.
// It will either be success (Thank you page)
// Or it will contains the error page
?>A working example can be downloaded here
If you have any queries about this I will be glad to answer them. Simply leave me a comment bellow.
Introducing Interspire's CMS: Website Publisher
With all the front page news about Interspire Shopping Cart and Interspire Email Marketer (formerly SendStudio) you might be saying "Hey Interspire! You’ve forgotten about your coolest application, ArticleLive!". Well, my overly assumptious friend, you couldn't be more wrong (and try not to use so many exclamation marks next time). We're working hard on transforming ArticleLive into a fully fledged Content Management System. We're taking this transformation in a few steps.
The TransformationAs current customers will know by their product downloads, the first step has been to rename ArticleLive to "Interspire Website Publisher". We believe this more accurately depicts what the product does, rather than restricting it to just an "article manager". It also reflects the direction of the program as it will have a much larger range of practical application. You'll still be able to use it as an article manager, but you will also be able to use it for many other purposes such as a standalone blog script, a small business website, a community website and pretty much anything you can think of.
The second step in the migration to CMS land will be Interspire Website Publisher 5.0
which has a major overhaul of the code in addition to changing how things work
within the application. Interspire Website Publisher 5.0 will require PHP 5.1.2 or above. We're pushing this up as we expect most hosts
to migrate to PHP5 (a lot already have) as PHP4's end of life nears in August. In addition to this is PHP 5’s
superiority over PHP 4 which allows us to do so much more than we could before.
If your host does not have PHP 5.1.2 or above, you'll need to request they install it or find another host who has it. A lot of hosts currently have the option to use either PHP 5 or PHP 4. You can check what version your host has by checking your control panel or asking them. The current version of Interspire Website Publisher (4.5) will work with PHP 5, so if PHP is upgraded preparation for Interspire Website Publisher 5.0 your website will still function properly in the mean time.
Over the coming weeks I'll be going into detail about the new features and functions of Interspire Website Publisher 5.0. Future posts will cover the new template system, modules and lists/menu generation. In this post I'll give you a peek at what we are currently calling "Content Types".
Before I jump into any details, please note that the application is still under *heavy* development, so any (or all) of the features mentioned or pictured here may change significantly before the final release.
Content Types
'Content Types' is our current term for a set of rules that define what a group of content items is made up of. The best way to think of it is to use the current state of Interspire Website Publisher where there are articles, news items, pages and blogs. Think of each of those as a “content type”, where they each have different fields and options in addition to being displayed differently. In Interspire Website Publisher 5.0 these content types will be dynamic, i.e. you’ll be able to create your own content type or edit an existing one.
Creating/Editing a content type consists of choosing which fields the content type will have, then selection options for each field. For example, you might just want a simple ‘pages’ content type which just has a title, content and meta content. For that you’d select your fields and toggle the options for each one. Title won’t have options, but for content you can select to toggle whether to use a text area or a WYSIWYG editor. For the meta content you can choose to automatically generate it or allow the option to enter it manually.
I’ve got here a screenshot of where we are at. This is of course an early shot, so may or may not look like this in the final release. As you can see adding or removing fields consists of dragging or dropping them into a list where you can also group fields and even put them into additional tabs. This constructs what fields the content piece has, as well as what structure the create/edit page will take.
Once the Content Type is saved, then you will be able to create a piece of content within that Content Type. Using the ‘views’ system we have in Interspire Shopping Cart, you’ll be able to view all content pieces of a certain type, that match a search term or other criteria. Managing a website will have never been easier.
All current features will exist in the new release, so when upgrading you won’t be losing anything. When upgrading from the current ArticleLive/Interspire Website Publisher 4.5, the process will automatically recreate pages, blogs, articles and news items (unless empty) using the content types system. From there you will be able to edit each of those content types to add or remove fields. Want categories for blogs? Drag the categories into a group in the blogs content type page and you’re done!
When installing from scratch you’ll have the option for advanced mode or simple mode, where simple mode asks you what you will use your website for and constructs the content types automatically for you based on your selection.
I hope you’ve enjoyed this sneak peek into Interspire Website Publisher 5.0. Next week I’ll have more information on another section of this major overhaul. If you have any comments or questions I’ll be happy to answer them in the comments section.
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)
Profiling your Application - A quick introduction
Profiling your application
Profiling your application can be difficult to do. You can either do it by guesswork (not a very efficient way of doing it), or by using some tools to help you. Sometimes you'll be surprised at where a bottleneck occurs, which is why guessing doesn't work - you could be looking in the completely wrong spot!
One of the best tools when working with a PHP application is XDebug. I'm assuming you have root access to your server and you are working on a development machine - don't try this on in a production environment ;)
Installing it is quite easy, the instructions on their website are pretty straight forward and can be foundhere.
Once it's installed, you need to enable it in your php.ini file. To do that, open it up and jump to the bottom. Add the following line:
zend_extension=/path/to/xdebug.so
Where /path/to/xdebug.so is where it was installed.
If you are doing this for web applications, you'll need to restart your webserver. You'll also need to put the following in a .htaccess file so profiling is enabled for your app:
xdebug.profiler_enable=1
xdebug.profiler_output_dir=/tmp
xdebug.profiler_output_name=cachegrind.out.%t.%p
For command line scripts, place those directives in the php.ini file (after loading the zend_extension), they can't be done on the fly.
Once you have that going (check your web server logs for errors if necessary), you can fire up your application.
After you view a page, you'll see a file in the /tmp folder called 'cachegrind.out.timestamp.pid', eg 'cachegrind.out.1211853650.6921'. The timestamp & pid will change per page view, so if you're on a busy or shared development server, change the profile_output_dir to another writable folder (eg your application has a writable cache/ folder).
Now you have a profile script, how do you use it?
The best tool to do that is KCacheGrind, however that is for Linux based systems only. If you're on a Windows desktop, you can use WinCacheGrind - however it hasn't been updated in a while and doesn't show as much information as KCacheGrind.
Here's a screenshot of part of the profile of running an Email Marketer cron job (which sent an email to 50,000 subscribers while the app was in "test mode"). The graph tells us that most of the time is spent in the SS_Email_API->Send method. Inside that, most of the time is spent in _ReplaceCustomFields (which in turn shows most of the time is spent doing the replacements).
Using this method of profiling, we were able to find a problem with sending an email campaign with lots of trackable links. We significantly improved the way that link tracking occurred to enhance sending speed and still provide the same functionality.
Dynamic List Searching
If you have a large number of Contact Lists then you may have experienced some frustration selecting the List you want to send a Campaign to, export Contacts from, or search for Contacts in. Now, instead of finding a needle in a haystack, you can let your fingers do the finding!


If you want to select multiple Lists that have very different names, you can simply enter a second search word. For example, if you search for "blue red" then it will display all Lists that have "blue" somewhere in their name and all Lists that have "red" in their name.
To get back to your original listing of all Contact Lists, simply clear your search string.
Dynamic List Searching is a very handy feature to speed up tedious list selections. Depending on how people are using it, we may tweak its behavior or roll it out into other areas of Interspire Email Marketer. We might even include it into other Interspire products if it proves popular.
If you have any questions about this or would like to just comment on it please reply below and I will answer you 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.
Displaying error messages on a custom error page
On the error page of a form the field is used as a placeholder for the error message. This allows you to choose where the error message will be displayed. When you use a custom error page you cannot use the placeholder and instead the error message is passed to the error page in the Errors parameter. For example the URL for your error page might look something like this:
http://www.domain.com/error.html?Error=error+string+here
In order to print the error message on your custom error page you will need to get the Error parameter from the URL. If you're using a server-side scripting language for your custom error page printing the error message is as simple as printing the value of the Errors parameter. For example if you're using PHP:
An error occurred and your subscription was not completed:
If a server-side language is not available you can still print the error message using JavaScript. This is a bit more complicated since you will need to parse the URL in the JavaScript. A regular expression is ideal for this:
An error occurred and your subscription was not completed:
The format of CSV File for importing articles to Interspire Knowledge Manager
The csv file contains of 7 columns of articles details.
First column: Title
The title of your article
Format: Alphabetic
Second column: Article Content
The content of your article
Format: Alphabetic
Third column: Meta Keywords
The Meta Keywords of your article
Format: Alphabetic
Fourth column: Meta Description
The Meta Description of your article
Format: Alphabetic
Fifth column: Sort Order
The Sort Order of your article, The higher the number, the further to the top of the list this article will appear.
Format: Numeric
Sixth column: Visibility
The Visibility of your article
Format: 1 or 0 (1 refers to YES and 0 refers to NO)
Seventh column: Auto Detect Related Articles
Should this article detect related Articles automatically?
Format: 1 or 0 (1 refers to YES and 0 refers to NO)
If you have any questions about this please ask me in the comments section.
Exporting only the files changed between 2 revisions in Subversion
Using the below command you just need to change the two revision numbers (1403 and 1438 in the example) and the path to your repository (http://server/svn/project/trunk in the example below) and the command will export all the files that have changed between the two revisions.
I've only tested the command in bash but it should work in zsh too. Not sure about any other shells. Also make sure that you have the trailing / in the sed part of the command otherwise it won't work properly.
for i in $(svn diff --summarize -r 1403:1438 http://server/svn/project/trunk | awk '{ print $2 }'); do p=$(echo $i | sed -e 's{http://server/svn/project/trunk/{{'); mkdir -p $(dirname $p); svn export $i $p; done
If you just wanted to get a list of the changed files you could run the command
svn diff --summarize -r 1403:1438 http://server/svn/project/trunk | awk '{ print $2 }' | sed -e 's{http://server/svn/project/trunk/{{'
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.

