Advanced Search
Such as "Interspire Email Marketer API" or "Private Label Guide"
Home Page
Developer Forum NEW!

Articles

Email Marketer
Shopping Cart
Knowledge Manager
Website Publisher
Other Products
Miscellaneous

Blogs

Email Marketer
Shopping Cart
Knowledge Manager
Website Publisher
Other Products
Miscellaneous

Recent Blogs

Modifying Shopping Cart: "Sort by Letter" in Categories
Posted 10/6/2008 by Chris Boulton
Creating segment from the API
Posted 09/24/2008 by Hendri Kurniawan
What's using my space
Posted 06/11/2008 by Rodney Amato
The CAN SPAM Act and what it means to us.
Posted 06/2/2008 by Scott Tedmanson
20 Payment Methods Strong - More to Come!
Posted 05/31/2008 by Chris Boulton

RSS 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

Here's a quick shell command to work out where your disk space is being used.
du -s * | sort -n | tail
If 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)
We're looking to tackle another set of popular payment providers (such as PayPal Website Payments Pro + PayPal Express Checkout) in the release following Interspire Shopping Cart 3.5.

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

These are the buttons available in DevEdit (the WYSIWYG Editor) at the current time.

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

When the WYSIWYG Editor is loaded into a page, it builds its menu automatically and gets all functions ready to go. The last thing it does is set up the designMode, which is the area where all WYSIWYG actions take place and the WYSIWYG contents are actually handled by the browser.

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.

Quite a few users have asked about methods to subscribe a user transparently from their website (ie. When they purchased a product from your website, and have opt-in to subscribe to your mailing list), and don't want to get the "Thank you" page displayed after the subscription process. Or when you want to simplify your subscription process by just asking only to fill their email address, and you pre-fill the form for them (ie. Set the format to HTML, etc).

There are two ways to achieve this:
  1. 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)
  2. 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 Transformation

As 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.

PHP 5The 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

With the introduction of Shipping Zones in Interspire Shopping Cart 3.5, it's no longer possible for the "View Cart" to calculate the shipping cost of orders automatically as it would have previously if you were using the flat-rate shipping methods (by order total, order weight, per item, etc). The reason behind this is because now that shipping methods are location based, we need to know the location of the customer (where they'll possibly be shipping their order to) before we can calculate anything to do with shipping.

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)
As always, feedback and suggestions are always welcome.

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

The release of Interspire Email Marketer 5 has seen the addition of a lot of new features. Some of them (like segmenting) are major new additions that many people are excited about, but it may not be the major features that you find most useful in this release. We've been putting a lot of work into making Interspire Email Marketer as easy to use as possible and have snuck in a host of mini-features and improvements to make things clearer, easier and quicker. My favourite new mini-feature is 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!


Simply type a few characters of the Contact List name and the list will shorten itself to just those entries that match your search criteria – as you type!


Now my list of 500+ Contact Lists has been reduced to a much more manageable size where I can easily select the List I'm after.

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.
As I previously mentioned, in each shipping zone you can configure as many different combinations of shipping methods as you like. This is a huge improvement over the current shipping in Interspire Shopping Cart 3.1 where you couldn't enable a combination of fixed & real time shipping quotes and couldn't have multiple combinations of a single shipping method (for example, you couldn't have two "Ship by Order Total" methods enabled with differen names such as Express Shipping or Standard Shipping).


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:


If you wish to import articles into Interspire Knowledge Manager you will need to do so buy using a CSV file. This file must be in a particular format for this to work. The format is as follows:

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.
Say you are just about ready to do a release of your project and you want to work out what files have changed for creating a changelog. Or maybe you want to have all the files in 1 directory for creating a zip to send to someone, or to copy over a test copy to test upgrading etc.

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/{{'

While working on the Google Checkout module for Interspire Shopping Cart, I came across a bug with the PHP library supplied by Google.

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

We've setup the Interspire Developer Network as a place where Interspire customers and partners can come to download not only installation and private label guides, but also technically orientated material including API documentation and integration manuals, articles and videos.

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.


No blogs found.