Thursday, May 8, 2014

Proprietary Analytics

If you want to go beyond what you can get from Google Analytics and start recording information from customers such as browser version, operating system and the use of mobile or full desktop template on a per-order level, you may want to consider programming that capability into your website.
Following is an example on how you can do that.

1) Create a table in the database to store that information:

CREATE TABLE `add_analytics` (
             `analytics_id` int(11) NOT NULL AUTO_INCREMENT,
             `orders_id` int(11) NOT NULL,
             `ismobile` tinyint(1) NOT NULL,
             `browser` varchar(32) CHARACTER SET utf8 NOT NULL,
             `version` varchar(16) NOT NULL,
             `platform` varchar(32) CHARACTER SET utf8 NOT NULL,
             `template` varchar(16) CHARACTER SET utf8 NOT NULL,
             `user_agent` varchar(128) CHARACTER SET utf8 NOT NULL,
             `datetime` datetime NOT NULL,
       PRIMARY KEY (`analytics_id`),

       KEY `orders_id` (`orders_id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1

2) Register that table with Zen Cart:
File

./includes/extra_datafiles/added_tables_definitions.php

Add:
define("TABLE_ANALYTICS","add_analytics");

3) Create the object that will be used to produce the information. The following code can be placed in a php file inside the ./includes/extra_configures/ folder:
class tracking{
  function analytics_capture($oID){
    global $db, $mobile, $template_dir;
    if(isset($oID) and $oID>0){
      $browser = $this->browser($_SERVER['HTTP_USER_AGENT']);
      $platform = $this->platform($_SERVER['HTTP_USER_AGENT']);
      $db->Execute("INSERT INTO `".TABLE_ANALYTICS."` (`orders_id`, `ismobile`, `browser`, `version`,
                                                                `platform`, `template`, `user_agent`, `datetime`)
                    VALUES ('".(int)$oID."', '".$mobile->ismobile()."', '".$browser['name']."', '".$browser['version']."',
                            '".$platform."','".$template_dir."', '".$_SERVER['HTTP_USER_AGENT']."','".date("Y-m-d h:i:s")."')");
    }
  }
  
  function browser($agent){
    $browsers = array('firefox', 'msie', 'opera', 'chrome', 'safari', 'mozilla', 'seamonkey', 'konqueror', 'netscape',
    'gecko', 'navigator', 'mosaic', 'lynx', 'amaya', 'omniweb', 'avant', 'camino', 'flock', 'aol');
    $user_agent = strtolower($agent);
    
    $browser = array('name'=>'', 'version'=>'');
    if (isset($_SERVER['HTTP_USER_AGENT'])) {
      foreach($browsers as $b){
        if(preg_match("/($b)[\/ ]?([0-9.]*)/", $user_agent, $match)){
          $browser = array('name'   =>$match[1],
                           'version'=>$match[2]);
          if($match[1]=='msie')
            $browser['name']="Internet Explorer";
          else
            $browser['name']=ucfirst($browser['name']);
          
          
          return $browser;
        }
      }
    }
    return $browser;
  }
  
  function platform($agent){
    $os_platform    =   "Unknown OS Platform";
    $os_array       =   array(
                            '/windows nt 6.2/i'     =>  'Windows 8',
                            '/windows nt 6.1/i'     =>  'Windows 7',
                            '/windows nt 6.0/i'     =>  'Windows Vista',
                            '/windows nt 5.2/i'     =>  'Windows Server 2003/XP x64',
                            '/windows nt 5.1/i'     =>  'Windows XP',
                            '/windows xp/i'         =>  'Windows XP',
                            '/windows nt 5.0/i'     =>  'Windows 2000',
                            '/windows me/i'         =>  'Windows ME',
                            '/win98/i'              =>  'Windows 98',
                            '/win95/i'              =>  'Windows 95',
                            '/win16/i'              =>  'Windows 3.11',
                            '/macintosh|mac os x/i' =>  'Mac OS X',
                            '/mac_powerpc/i'        =>  'Mac OS 9',
                            '/linux/i'              =>  'Linux',
                            '/ubuntu/i'             =>  'Ubuntu',
                            '/iphone/i'             =>  'iPhone',
                            '/ipod/i'               =>  'iPod',
                            '/ipad/i'               =>  'iPad',
                            '/android/i'            =>  'Android',
                            '/blackberry/i'         =>  'BlackBerry',
                            '/webos/i'              =>  'Mobile'
                        );
    foreach ($os_array as $regex => $value) { 
      if (preg_match($regex, $agent))
        $os_platform = $value;
    }   
    return $os_platform;
  }

}

4) Call that object from the orders class to record the data whenever a new order is placed:
File:
./includes/classes/order.php

Add:
$tracking = new tracking;

$tracking->analytics_capture($insert_id);

Before:
return($insert_id);

Thursday, May 13, 2010

Password Encryption: how Zen Cart handles customer validation

How Zen Cart encrypts passwords:
1. Generate a random number with 100 digits:
e.g.: 1848751306155909551019133583582126298457157391230411931806601506158142207720389212964433351079126741

2. Using md5, encrypt that random number and get the first two characters of that encrypted number:
e.g: 8f

3. Append the two characters at the beginning of user password and encrypt the new text string:
e.g.: using "pwd" as my  password...
md5(8fpwd) = 39a9484d3aea5888d6683aa82fc64ba5


4. Recorded the encrypted password, colon, and the two characters generated earlier (the salt):
39a9484d3aea5888d6683aa82fc64ba5:8f

How Zen Cart "decrypts" passwords:
1. Based on user name (email address), get the encrypted password recorded in the database. Separate the encrypted password from the "salt" appended at the end.

2. Append the "salt" to the beginning of the password provided by the client:
8fpwd

3. Encrypt the new string and compare the result with the encrypted password from the database:
e.g.: If md5(8fpwd) equals “39a9484d3aea5888d6683aa82fc64ba5”, allow access.

Note: the password is never decrypted.

Friday, July 17, 2009

Listing all files added to your website

If you want to upgrade your Zen Cart based website but have too many proprietary files you may want to consider downloading a list of file names you will need to move to the new platform. Here is a simple script that will compare the backup copy of your current website with the original install. Before you run the script make sure you have:
1) A fresh backup copy of your website in a local folder (e.g.: c:\temp\backup);
2) The original version of Zen Cart that is powering your website in another folder (e.g.: c:\temp\zencart).

You can copy the following script, paste in a new document, save as a php file and run from a web server. Just make sure to rename the path to the backup and original if diferent than those listed in the script.



<?php
if($_POST['ftype']!=""){
$backup = "c:\\temp\\backup"; //path to the lattest backup of the website $original = "C:\\temp\\zencart"; //path to the original version of zen cart $v=""; //will pass $backup to function $o=""; //will pass $original to function
$t=$_POST['ftype']; //will pass the desired filetype (from dropdown) to function $s=strlen($_POST['ftype']); //will pass the length of the string for the desired file type to function $l=strlen($backup)+1;
function compareSites($v,$o,$t,$s,$l){
$item = array_diff(scandir($v), Array( ".", ".." )); //generates an array with all elements of the current directory except dot and double-dot foreach( $item as $i ){ //loops through each element of the array if( is_dir($v."\\".$i) ){ //if the element is a directory (not a file) if($i!="images" and $i!="CVS"){ //if the directory is not the images directory if(!file_exists($o."\\".$i)){
if($t=="ALL"){
echo "<strong>[".substr($v."\\".$i,$l)."]</strong><br>\n";
}
}else{
compareSites( $v."\\".$i,$o."\\".$i,$t,$s,$l); //run the function against that directory
}
}
}else{ //the current element is a file $ft=strtoupper(substr($i,-$s,$s)); //$ft receives filetype from file at backup site if($ft==$t or $t=="ALL"){ //compares $ft with $t which holds the filetype requested (e.g.: .txt) if(!file_exists($o."\\".$i)){ echo substr($v."\\".$i,$l)."<br>\n"; } //if the file does not exist in original install of zen cart display file with path
}
}
}
} //end function
if($_POST['btnSubmit']){
compareSites($backup,$original,$t,$s,$l);
}
}
?>
<div style="font-family:Verdana, Arial, Helvetica, sans-serif; font-size:14px; margin:10px;">Select the file type you want to compare to get a list of files from your website's backup copy that are not available in the original install of the shopping cart:
<form name="frmCompare" id="frmCompare" action="index.php" method="post">
<select name="ftype">
<option></option>
<option value=".PHP">php</option>
<option value=".HTM">htm</option>
<option value=".HTML">html</option>
<option value=".JS">js</option>
<option value=".JPG">jpg</option>
<option value=".GIF">gif</option>
<option value=".PNG">png</option>
<option value=".TXT">txt</option>
<option value="ALL">ALL: Files and Folders</option>
</select>
<input type="submit" name="btnSubmit" id="btnSubmit" value="Check"
</form>
</div>

Wednesday, May 27, 2009

Pop-up disclaimers at checkout

Online companies must inform customers about shipping, returns, and other policies. Instead of having disclaimers at checkout you may rather have a "Read Me" link that will open a pop-up window with details about your company's policies. Here is how you program a link at the first checkout page (where customers choose shipping methods):

File name and location (1/3):includes/languages/english/checkout_shipping.php
Modification:
Replace:
define('TEXT_CHOOSE_SHIPPING_METHOD', 'Please select the preferred shipping method to use on this order.');

With:define('TEXT_CHOOSE_SHIPPING_METHOD', '<span style="font-family:Verdana;font-size:85%;">Please select the preferred shipping method to use on this order.</span> <p>Read Me: <a href="javascript:openWindow(\'height=350,width=500,status=no,toolbar=no,menubar=no,location=no,resizable=yes,scrollbars=yes\')">Shipping Policy</a></p>');
Create Page (2/3):shipping_policy.html
Upload to:Store's root folder.

Add the following Script (3/3):<script language="javascript" type="text/javascript"> function openWindow(url, name, attrs) { popWin = window.open(url, name, attrs); popWin.focus(); }</script>

Location:...\includes\templates\template_default\common\html_header.phpbefore </head> tag.

You may test it now.

SMTP Source Code Error

If you are trying to install Zen Cart for the first time you may get following message when trying to create a test account:

Email Error: Could not instantiate mail function. Please check Admin->Configuration->Email Options->Email Transport.

The problem can be easily fixed in the source code. Open the following file for editing:
...\includes\classes\class.smtp.php

Find the following line of code:
if($this-Protocol != '') $host = $this->Protocol."://".$host;

Add a "<" sign as follows:
if($this<-Protocol != '') $host = $this->Protocol."://".$host;

You should be good to go.

Wednesday, May 20, 2009

Changing keyword search box size

By default, Zen Cart's keyword search box at the header is configured to display with 100 pixels wide. The default will also limit customer’s keywords to 30 characters. To change the default you must edit the following file:
...\includes\templates\template_default\sideboxes\tpl_search_header.php

Note: if you are using another template you should replace “template_default” with the name of your template.

All you need to do is to edit the two function calls to zen_draw_input_field(...).


Change the parameter maxlength=30 to any number of characters you want to give users more space for keywords.
To change the size of your search box you need to edit the style="width: 100px". Resize the text box according to the requirements of your design.

Monday, May 18, 2009

Display a message pointing out the last keyword typed on failed results

In order to avoid users running the same keyword search over and over again you may want to display a feedback pointing to the last keyword used. Here is how you can display a more appropriate response to users when the result set returns an empty list:
File Location:..\includes\languages\english\advanced_search_result.php
Replace:
define('TEXT_NO_PRODUCTS', 'There is no product that matches the search criteria.');
With:
define('TEXT_NO_PRODUCTS', '<font size=2 face="Verdana" color="#000077">No products found matching </font><font font size=2 face="Verdana" color="#770000"><u>'.$_GET['keyword'].'</u></font><font size=2 face="Verdana" color="#000077"> search keywords</font>');

Friday, May 15, 2009

Feeding database with keywords from search engine

In order to improve your understanding of visitor’s needs and wants you may want to collect keywords used in your search engine and periodically analyze the data produced. The following example explains how to collect keywords, submission date and time, as well as the user’s remote address and flag if the search was successful or not.

1. Create a new table in the database. For illustration purposes I am going to use the following convention:
Table Name: search_log
Attributes (columns):
- ID: int(32), unsigned, auto_increment, primary key
- log_data: varchar(30)
- stp_date: date
- stp_time: time
- remote_ip: varchar(16)
- search_success: tinyint(1)

2. Open the following script:
\includes\modules\pages\advanced_search_result\header_php.php

Add the following code: $entry = $_GET['keyword'];
if ($result->number_of_rows > 0) {
mysql_query ("INSERT INTO `search_log` (`Log_Data`,`stp_date`,`stp_time`,`remote_ip`,`search_success`) VALUES ('$entry',now(),now(),'".$_SERVER['REMOTE_ADDR']."','1')");
}else{
mysql_query ("INSERT INTO `search_log` (`Log_Data`,`stp_date`,`stp_time`,`remote_ip`,`search_success`) VALUES ('$entry',now(),now(),'".$_SERVER['REMOTE_ADDR']."','0')");
}

Before the following line:
if ($result->number_of_rows == 0) {

Thursday, May 14, 2009

Restricting admin login by remote IP

Zen Cart is a powerful open source e-commerce application. As any publicly available web application containing sensitive information, Zen Cart requires that you pay close attention to its data integrity and security.
Besides encryption and the use of strong passwords users are advised to rename the admin folder making it harder to an outside user to find the administrative interface where sensitive data can be displayed.
Another level of security can be achieved by restricting login access by IP address.
Note: if you host Zen Cart in a remote server and access the Internet with a dynamic IP address the following solution will not work. The solution will work if you either access admin from your local area network or if you always access the Internet with the same IP (if you have a static IP).

How to restrict access to Zen Cart's admin area by IP address:1. Find your static public IP address. For illustration purposes I am going to use the loopback IP (127.0.0.1) in my example;
2. Open the following file: .../admin/login.php
3. Add the following code Before “if ($message == false) {“:

if($_SERVER['REMOTE_ADDR']!="127.0.0.1"){
$message = true;
$pass_message = ERROR_WRONG_LOGIN;
}

After uploading the file your application will start denying access to anyone except you.