Jump to content



Latest News: (loading..)

* * * * * 6 votes

A Store Speed Optimization in Progress


  • Please log in to reply
905 replies to this topic

#21   RavenWulf

RavenWulf
  • Members
  • 297 posts
  • Real Name:Douglas
  • Location:Germany

Posted 01 November 2004 - 08:56 PM

bruyndoncx, on Nov 1 2004, 08:44 PM, said:

I would but I have 2 excuses
- being a developer, so it wouldn't prove 3.
- got enable/disable categories installed

But you can have my code when you release the contribution :)

<{POST_SNAPBACK}>



what does have enable/disable categories contribution have to do with it? I ask because I also have this installed.

RW
A man who works with his hands is a laborer
A man who works with his hands and his brain is a craftsman
But a man who works with his hands and his brain and his heart is an artist. - Charles Dickens

#22   Chemo

Chemo
  • Banned
  • 2,486 posts
  • Real Name:Bobby
  • Location:/usa/kentucky/richmond/

Posted 01 November 2004 - 09:12 PM

bruyndoncx, on Nov 1 2004, 03:44 PM, said:

I would but I have 2 excuses
- being a developer, so it wouldn't prove 3.
- got enable/disable categories installed

But you can have my code when you release the contribution :)

<{POST_SNAPBACK}>

If you're a developer you'll no doubt recognize some of the basic things I'm doing here.  This about it like this: if you can save 84 queries per page how many would that save in a year?  Assuming 1,000 visitors per day (spiders included) and 3 pages viewed per visit it would save 9,198,000 queries per year.  

If you're like me and have a dedicated server with several (50+) osCommerce sites installed that would mean almost half a billion queries!!  I'm doing this purely to save my MySQL server...it's cheaper than getting a dedicated MySQL server.

BTW, even if you have enable/disable categories installed (which I do on several sites) the improvements here will give the same benefits and are completely compatible.  

RavenWulf, on Nov 1 2004, 03:56 PM, said:

what does have enable/disable categories contribution have to do with it? I ask because I also have this installed.

RW

<{POST_SNAPBACK}>

The only way that it will be affected is if you install the page cache.  This is because the page is cached and will be served until it expires.  You can set the timelife in the admin control panel and can be anywhere from 1 minute to 1 year.

Edited by Chemo, 01 November 2004 - 09:14 PM.


#23   RavenWulf

RavenWulf
  • Members
  • 297 posts
  • Real Name:Douglas
  • Location:Germany

Posted 01 November 2004 - 09:42 PM

Chemo, on Nov 1 2004, 09:12 PM, said:

If you're a developer you'll no doubt recognize some of the basic things I'm doing here.  This about it like this: if you can save 84 queries per page how many would that save in a year?  Assuming 1,000 visitors per day (spiders included) and 3 pages viewed per visit it would save 9,198,000 queries per year. 

If you're like me and have a dedicated server with several (50+) osCommerce sites installed that would mean almost half a billion queries!!  I'm doing this purely to save my MySQL server...it's cheaper than getting a dedicated MySQL server.

BTW, even if you have enable/disable categories installed (which I do on several sites) the improvements here will give the same benefits and are completely compatible. 
The only way that it will be affected is if you install the page cache.  This is because the page is cached and will be served until it expires.  You can set the timelife in the admin control panel and can be anywhere from 1 minute to 1 year.

<{POST_SNAPBACK}>



so even if I have the enable/disable installed all I would need to do is anytime I change what catagories are there "flush" the cache and recreate it?   Is there an admin function to page cache to do this? or is it simple set it to expire in a min, wait, then reenable it for however long I want?

Thank
RW
A man who works with his hands is a laborer
A man who works with his hands and his brain is a craftsman
But a man who works with his hands and his brain and his heart is an artist. - Charles Dickens

#24   Chemo

Chemo
  • Banned
  • 2,486 posts
  • Real Name:Bobby
  • Location:/usa/kentucky/richmond/

Posted 01 November 2004 - 10:04 PM

RavenWulf, on Nov 1 2004, 04:42 PM, said:

so even if I have the enable/disable installed all I would need to do is anytime I change what catagories are there "flush" the cache and recreate it?   Is there an admin function to page cache to do this? or is it simple set it to expire in a min, wait, then reenable it for however long I want?

Thank
RW

<{POST_SNAPBACK}>

There is an auto cache flush feature built in....go to the testing server and then to the admin directory.  I don't have it password protected as I have faith in the osC community to not do malicious things.

Under the Configuration setting group find the Page Cache Settings.  Next, find the delete cache files option.  Set it to "true" and update.  On the very first page call after that the files will be deleted and the value in the database set back to false.

So, it is an automatic function to delete the cache files.

Go try it out :)

#25   Fallout

Fallout
  • Members
  • 15 posts
  • Real Name:Mark M.

Posted 02 November 2004 - 12:17 AM

Hello, I also want to install this nice Contribution.


However, have a problem with Install?

STEP 3 (catalog,includes,funktions, general.php)

I found the Code not in my File.
 function tep_db_query($query, $link = 'db_link') {
      global $$link;
      
      if (defined('STORE_DB_TRANSACTIONS') && (STORE_DB_TRANSACTIONS == 'true')) {
      error_log('QUERY ' . $query . "\n", 3, STORE_PAGE_PARSE_TIME_LOG);
      }
      
      $result = mysql_query($query, $$link) or tep_db_error($query, mysql_errno(), mysql_error());
      
      if (defined('STORE_DB_TRANSACTIONS') && (STORE_DB_TRANSACTIONS == 'true')) {
      $result_error = mysql_error();
      error_log('RESULT ' . $result . ' ' . $result_error . "\n", 3, STORE_PAGE_PARSE_TIME_LOG);
      }
      
      return $result;
      }

Can you help me with the Installation?

LG

#26   wizardsandwars

wizardsandwars
  • Members
  • 4,476 posts
  • Real Name:Chris Bradley

Posted 02 November 2004 - 01:47 AM

Just wanted to chime in here. I've had a long running site up (over 2 years now), and I thought I had optimized it about as far as I could.

The only thing *new* to me in this thread thus far was the 'page cache'. I gotta admit, I'm impressed.  My site went form loading in about 38 seconds on a 56k (graphics heavy) to loading in about 19 seconds.

That's a pretty hefty imporvement, and I like how you can control how long the case page last for. Terrific improvements. Thanks for the heads up.
-------------------------------------------------------------------------------------------------------------------------
NOTE: As of Oct 2006, I'm not as active in this forum as I used to be, but I still work with osC quite a bit.
If you have a question about any of my posts here, your best bet is to contact me though either Email or PM in my profile, and I'll be happy to help.

#27   booster

booster
  • Members
  • 71 posts
  • Real Name:Dave

Posted 02 November 2004 - 02:48 AM

Chemo, on Oct 31 2004, 04:38 PM, said:


Excellent - thanks a lot  :D

I'm looking forward to this thread developing to the same high standard.
(I'm very new to osC so can't contribute much.)

Dave.

#28   Chemo

Chemo
  • Banned
  • 2,486 posts
  • Real Name:Bobby
  • Location:/usa/kentucky/richmond/

Posted 02 November 2004 - 03:12 AM

Fallout, on Nov 1 2004, 07:17 PM, said:

Hello, I also want to install this nice Contribution.
However, have a problem with Install?

STEP 3 (catalog,includes,funktions, general.php)

I found the Code not in my File.
 function tep_db_query($query, $link = 'db_link') {
 < SNIP >

Can you help me with the Installation?

LG

<{POST_SNAPBACK}>

That function is actually located in includes/functions/database.php around line37 or so.

I apologize and will correct the install file now.

Download the latest query debug here: Debug Query Output

wizardsandwars, on Nov 1 2004, 08:47 PM, said:

Just wanted to chime in here. I've had a long running site up (over 2 years now), and I thought I had optimized it about as far as I could.

The only thing *new* to me in this thread thus far was the 'page cache'. I gotta admit, I'm impressed.  My site went form loading in about 38 seconds on a 56k (graphics heavy) to loading in about 19 seconds.

That's a pretty hefty imporvement, and I like how you can control how long the case page last for. Terrific improvements. Thanks for the heads up.

<{POST_SNAPBACK}>

You've been around the block so know there is always a downside to everything.  The page cache also keep the traditionally dynamic portions cached (like specials, featured products, best sellers, etc.).  The only thing left to generate on each page request is the customer or guests cart.  Also, once they register or log in the cache system turns itself off (for safety).  It's only active for guests not logged in.

I figure if they log in they should get the full work of the server :)

booster, on Nov 1 2004, 09:48 PM, said:

Excellent - thanks a lot  :D

I'm looking forward to this thread developing to the same high standard.
(I'm very new to osC so can't contribute much.)

Dave.

<{POST_SNAPBACK}>

Stay tuned...there's more to come.  The next steps are:
1. Add popular contributions (discount coupons, featured, specials, etc)
2. Optimize again
3. Search Engine Optmization ala Chemo style

As a side note to #3: it's going to be like the "archive" feature of vBulletin discussion forum software.  It will go beyond all_products and take SEO / sitemaps to another level for osCommerce.  Enough of me blowing my own horn...back to coding :)

#29   Chemo

Chemo
  • Banned
  • 2,486 posts
  • Real Name:Bobby
  • Location:/usa/kentucky/richmond/

Posted 02 November 2004 - 03:19 AM

booster, on Nov 1 2004, 09:48 PM, said:

Excellent - thanks a lot  :D

I'm looking forward to this thread developing to the same high standard.
(I'm very new to osC so can't contribute much.)

Dave.

<{POST_SNAPBACK}>

Almost forgot...you can also define a longer period on a per page basis from the admin control panel setting.

For instance,  the default code at the top of each page looks like this:
require(DIR_WS_CLASSES . 'page_cache.php');
$page_cache = new page_cache($cart_cache);
$page_cache->cache_this_page();

Change it to something like this:
require(DIR_WS_CLASSES . 'page_cache.php');
$page_cache = new page_cache($cart_cache, 1440, false);
$page_cache->cache_this_page();

Here's the function and arguments
cache_this_page($cart_info, $cachelifetime=5, $debugmode=false)

So, for pages like all_products, printable_catalog, etc. where there is not much traffic other than search engine spiders I can set the cache lifetime much higher (or, lower).

I try to make the code as versatile as I can :)

#30   RavenWulf

RavenWulf
  • Members
  • 297 posts
  • Real Name:Douglas
  • Location:Germany

Posted 02 November 2004 - 05:17 PM

Chemo, on Nov 2 2004, 03:19 AM, said:

Almost forgot...you can also define a longer period on a per page basis from the admin control panel setting.

For instance,  the default code at the top of each page looks like this:
require(DIR_WS_CLASSES . 'page_cache.php');
$page_cache = new page_cache($cart_cache);
$page_cache->cache_this_page();

Change it to something like this:
require(DIR_WS_CLASSES . 'page_cache.php');
$page_cache = new page_cache($cart_cache, 1440, false);
$page_cache->cache_this_page();

Here's the function and arguments
cache_this_page($cart_info, $cachelifetime=5, $debugmode=false)

So, for pages like all_products, printable_catalog, etc. where there is not much traffic other than search engine spiders I can set the cache lifetime much higher (or, lower).

I try to make the code as versatile as I can :)

<{POST_SNAPBACK}>



Hey Chemo,

I am installing the m3 tax class for ms2 contribution, but hit a snag. in step3 second part is says replace tep_get_tax_description() with this code, but I don't have a function for teg_get_tax_description.  I am using 2.2 ms 2 (latest download from oscommerce site)..any ideas?

Thanks!
RW
A man who works with his hands is a laborer
A man who works with his hands and his brain is a craftsman
But a man who works with his hands and his brain and his heart is an artist. - Charles Dickens

#31   Chemo

Chemo
  • Banned
  • 2,486 posts
  • Real Name:Bobby
  • Location:/usa/kentucky/richmond/

Posted 02 November 2004 - 05:25 PM

RavenWulf, on Nov 2 2004, 12:17 PM, said:

Hey Chemo,

I am installing the m3 tax class for ms2 contribution, but hit a snag. in step3 second part is says replace tep_get_tax_description() with this code, but I don't have a function for teg_get_tax_description.  I am using 2.2 ms 2 (latest download from oscommerce site)..any ideas?

Thanks!
RW

<{POST_SNAPBACK}>

The method tep_get_tax_rate() is located in includes/functions/general.php and was a typo.  I've corrected it in the latest install (I think).

Check on my dev server for lastest files and pre-releases: osCommerce contritutions on my server

Also, you can go right to the install file here: MS3 Tax Class for MS2

#32   wizardsandwars

wizardsandwars
  • Members
  • 4,476 posts
  • Real Name:Chris Bradley

Posted 02 November 2004 - 05:31 PM

Before you move on, I'd like to ask a quick question about the page cashe.

If you have this enabled, won't all of your guests have the same exact session ID? That seems to be the case.
-------------------------------------------------------------------------------------------------------------------------
NOTE: As of Oct 2006, I'm not as active in this forum as I used to be, but I still work with osC quite a bit.
If you have a question about any of my posts here, your best bet is to contact me though either Email or PM in my profile, and I'll be happy to help.

#33   Chemo

Chemo
  • Banned
  • 2,486 posts
  • Real Name:Bobby
  • Location:/usa/kentucky/richmond/

Posted 02 November 2004 - 05:39 PM

That may be the case if your store has the session id's appended to each URL.  However, most do not and only use them in the URL when going from / to SSL / NONSSL.

It hasn't been problem on my sites but that is something to report and should be easily fixed.

#34   wizardsandwars

wizardsandwars
  • Members
  • 4,476 posts
  • Real Name:Chris Bradley

Posted 02 November 2004 - 05:44 PM

Not sure what you mean.

The sid will be attached to the URL if cookies are not enabled in the customer's browser, and 'force cookie' use is not set to true.

Since IE is now being distributed with cookies NOT enabled, I would think that this would actually represent the majority of users very soon.

And the session is not only used when going form SSL to nonSSL, but also what you have in your cart. Not to mention that if one person purchases using that session, and enters their details to create an account, and then another person comes along and uses that session, they will have access to all of the first person's account details.

That could be a very serious issue.

Edited by wizardsandwars, 02 November 2004 - 05:48 PM.

-------------------------------------------------------------------------------------------------------------------------
NOTE: As of Oct 2006, I'm not as active in this forum as I used to be, but I still work with osC quite a bit.
If you have a question about any of my posts here, your best bet is to contact me though either Email or PM in my profile, and I'll be happy to help.

#35   wizardsandwars

wizardsandwars
  • Members
  • 4,476 posts
  • Real Name:Chris Bradley

Posted 02 November 2004 - 05:52 PM

Another point recently brought to my attention is that if a search engine parses that cashed page, they will also get that very same session id. And the last thing you want to do is have search engine links with session ids in them.
-------------------------------------------------------------------------------------------------------------------------
NOTE: As of Oct 2006, I'm not as active in this forum as I used to be, but I still work with osC quite a bit.
If you have a question about any of my posts here, your best bet is to contact me though either Email or PM in my profile, and I'll be happy to help.

#36   RavenWulf

RavenWulf
  • Members
  • 297 posts
  • Real Name:Douglas
  • Location:Germany

Posted 02 November 2004 - 06:00 PM

Chemo, on Nov 2 2004, 05:25 PM, said:

The method tep_get_tax_rate() is located in includes/functions/general.php and was a typo.  I've corrected it in the latest install (I think).

Check on my dev server for lastest files and pre-releases: osCommerce contritutions on my server

Also, you can go right to the install file here: MS3 Tax Class for MS2

<{POST_SNAPBACK}>



Heya, thanks for the quick reply.
I went to your server and dl the v1.1 for the m3 tax class for oscommerce.. however in the install instructions it still says:
STEP 3 - Edit includes/functions/general.php
REPLACE tep_get_tax_rate() it with this code:
function tep_get_tax_rate($class_id, $country_id = -1, $zone_id = -1) {
global $customer_zone_id, $customer_country_id, $osC_Tax;
return $osC_Tax->getTaxRate($class_id, $country_id, $zone_id);
}

REPLACE tep_get_tax_description() it with this code:
function tep_get_tax_description($class_id, $country_id, $zone_id) {
global $osC_Tax;
return $osC_Tax->getTaxRateDescription($class_id, $country_id, $zone_id);
}

I have done the 1st one (tep_get_tax_rate), but I don't have tep_get_tax_description in my general.php file in the catalog/includes/functions folder.. any ideas?  below is a copy of my general.php file:

<?php
/*
  $Id: general.php,v 1.160 2003/07/12 08:32:47 hpdl Exp $
  ++++ modified for USPS Methods 2.5 08/02/03 by Brad Waite and Fritz Clapp ++++
  osCommerce, Open Source E-Commerce Solutions
  http://www.oscommerce.com

  Copyright © 2003 osCommerce

  Released under the GNU General Public License
*/


////
// Redirect to another page or site
  function tep_redirect($url) {
    global $logger;

    header('Location: ' . $url);

    if (STORE_PAGE_PARSE_TIME == 'true') {
      if (!is_object($logger)) $logger = new logger;
      $logger->timer_stop();
    }

    exit;
  }

////
// Parse the data used in the html tags to ensure the tags will not break
  function tep_parse_input_field_data($data, $parse) {
    return strtr(trim($data), $parse);
  }

  function tep_output_string($string, $translate = false, $protected = false) {
    if ($protected == true) {
      return htmlspecialchars($string);
    } else {
      if ($translate == false) {
        return tep_parse_input_field_data($string, array('"' => '&quot;'));
      } else {
        return tep_parse_input_field_data($string, $translate);
      }
    }
  }

  function tep_output_string_protected($string) {
    return tep_output_string($string, false, true);
  }

  function tep_sanitize_string($string) {
    $string = ereg_replace(' +', ' ', $string);

    return preg_replace("/[<>]/", '_', $string);
  }

  function tep_customers_name($customers_id) {
    $customers = tep_db_query("select customers_firstname, customers_lastname from " . TABLE_CUSTOMERS . " where customers_id = '" . (int)$customers_id . "'");
    $customers_values = tep_db_fetch_array($customers);

    return $customers_values['customers_firstname'] . ' ' . $customers_values['customers_lastname'];
  }

  function tep_get_path($current_category_id = '') {
    global $cPath_array;

    if ($current_category_id == '') {
      $cPath_new = implode('_', $cPath_array);
    } else {
      if (sizeof($cPath_array) == 0) {
        $cPath_new = $current_category_id;
      } else {
        $cPath_new = '';
        $last_category_query = tep_db_query("select parent_id from " . TABLE_CATEGORIES . " where categories_id = '" . (int)$cPath_array[(sizeof($cPath_array)-1)] . "'");
        $last_category = tep_db_fetch_array($last_category_query);

        $current_category_query = tep_db_query("select parent_id from " . TABLE_CATEGORIES . " where categories_id = '" . (int)$current_category_id . "'");
        $current_category = tep_db_fetch_array($current_category_query);

        if ($last_category['parent_id'] == $current_category['parent_id']) {
          for ($i = 0, $n = sizeof($cPath_array) - 1; $i < $n; $i++) {
            $cPath_new .= '_' . $cPath_array[$i];
          }
        } else {
          for ($i = 0, $n = sizeof($cPath_array); $i < $n; $i++) {
            $cPath_new .= '_' . $cPath_array[$i];
          }
        }

        $cPath_new .= '_' . $current_category_id;

        if (substr($cPath_new, 0, 1) == '_') {
          $cPath_new = substr($cPath_new, 1);
        }
      }
    }

    return 'cPath=' . $cPath_new;
  }

  function tep_get_all_get_params($exclude_array = '') {
    global $HTTP_GET_VARS;

    if ($exclude_array == '') $exclude_array = array();

    $get_url = '';

    reset($HTTP_GET_VARS);
    while (list($key, $value) = each($HTTP_GET_VARS)) {
      if (($key != tep_session_name()) && ($key != 'error') && (!in_array($key, $exclude_array))) $get_url .= $key . '=' . $value . '&';
    }

    return $get_url;
  }

  function tep_date_long($raw_date) {
    if ( ($raw_date == '0000-00-00 00:00:00') || ($raw_date == '') ) return false;

    $year = (int)substr($raw_date, 0, 4);
    $month = (int)substr($raw_date, 5, 2);
    $day = (int)substr($raw_date, 8, 2);
    $hour = (int)substr($raw_date, 11, 2);
    $minute = (int)substr($raw_date, 14, 2);
    $second = (int)substr($raw_date, 17, 2);

    return strftime(DATE_FORMAT_LONG, mktime($hour, $minute, $second, $month, $day, $year));
  }

////
// Output a raw date string in the selected locale date format
// $raw_date needs to be in this format: YYYY-MM-DD HH:MM:SS
// NOTE: Includes a workaround for dates before 01/01/1970 that fail on windows servers
  function tep_date_short($raw_date) {
    if ( ($raw_date == '0000-00-00 00:00:00') || ($raw_date == '') ) return false;

    $year = substr($raw_date, 0, 4);
    $month = (int)substr($raw_date, 5, 2);
    $day = (int)substr($raw_date, 8, 2);
    $hour = (int)substr($raw_date, 11, 2);
    $minute = (int)substr($raw_date, 14, 2);
    $second = (int)substr($raw_date, 17, 2);

    if (@date('Y', mktime($hour, $minute, $second, $month, $day, $year)) == $year) {
      return date(DATE_FORMAT, mktime($hour, $minute, $second, $month, $day, $year));
    } else {
      return ereg_replace('2037' . '$', $year, date(DATE_FORMAT, mktime($hour, $minute, $second, $month, $day, 2037)));
    }

  }

  function tep_datetime_short($raw_datetime) {
    if ( ($raw_datetime == '0000-00-00 00:00:00') || ($raw_datetime == '') ) return false;

    $year = (int)substr($raw_datetime, 0, 4);
    $month = (int)substr($raw_datetime, 5, 2);
    $day = (int)substr($raw_datetime, 8, 2);
    $hour = (int)substr($raw_datetime, 11, 2);
    $minute = (int)substr($raw_datetime, 14, 2);
    $second = (int)substr($raw_datetime, 17, 2);

    return strftime(DATE_TIME_FORMAT, mktime($hour, $minute, $second, $month, $day, $year));
  }

  function tep_get_category_tree($parent_id = '0', $spacing = '', $exclude = '', $category_tree_array = '', $include_itself = false) {
    global $languages_id;

    if (!is_array($category_tree_array)) $category_tree_array = array();
    if ( (sizeof($category_tree_array) < 1) && ($exclude != '0') ) $category_tree_array[] = array('id' => '0', 'text' => TEXT_TOP);

    if ($include_itself) {
      $category_query = tep_db_query("select cd.categories_name from " . TABLE_CATEGORIES_DESCRIPTION . " cd where cd.language_id = '" . (int)$languages_id . "' and cd.categories_id = '" . (int)$parent_id . "'");
      $category = tep_db_fetch_array($category_query);
      $category_tree_array[] = array('id' => $parent_id, 'text' => $category['categories_name']);
    }

    $categories_query = tep_db_query("select c.categories_id, cd.categories_name, c.parent_id from " . TABLE_CATEGORIES . " c, " . TABLE_CATEGORIES_DESCRIPTION . " cd where c.categories_id = cd.categories_id and cd.language_id = '" . (int)$languages_id . "' and c.parent_id = '" . (int)$parent_id . "' order by c.sort_order, cd.categories_name");
    while ($categories = tep_db_fetch_array($categories_query)) {
      if ($exclude != $categories['categories_id']) $category_tree_array[] = array('id' => $categories['categories_id'], 'text' => $spacing . $categories['categories_name']);
      $category_tree_array = tep_get_category_tree($categories['categories_id'], $spacing . '&nbsp;&nbsp;&nbsp;', $exclude, $category_tree_array);
    }

    return $category_tree_array;
  }

  function tep_draw_products_pull_down($name, $parameters = '', $exclude = '') {
    global $currencies, $languages_id;

    if ($exclude == '') {
      $exclude = array();
    }

    $select_string = '<select name="' . $name . '"';

    if ($parameters) {
      $select_string .= ' ' . $parameters;
    }

    $select_string .= '>';

    $products_query = tep_db_query("select p.products_id, pd.products_name, p.products_price from " . TABLE_PRODUCTS . " p, " . TABLE_PRODUCTS_DESCRIPTION . " pd where p.products_id = pd.products_id and pd.language_id = '" . (int)$languages_id . "' order by products_name");
    while ($products = tep_db_fetch_array($products_query)) {
      if (!in_array($products['products_id'], $exclude)) {
        $select_string .= '<option value="' . $products['products_id'] . '">' . $products['products_name'] . ' (' . $currencies->format($products['products_price']) . ')</option>';
      }
    }

    $select_string .= '</select>';

    return $select_string;
  }

  function tep_options_name($options_id) {
    global $languages_id;

    $options = tep_db_query("select products_options_name from " . TABLE_PRODUCTS_OPTIONS . " where products_options_id = '" . (int)$options_id . "' and language_id = '" . (int)$languages_id . "'");
    $options_values = tep_db_fetch_array($options);

    return $options_values['products_options_name'];
  }

  function tep_values_name($values_id) {
    global $languages_id;

    $values = tep_db_query("select products_options_values_name from " . TABLE_PRODUCTS_OPTIONS_VALUES . " where products_options_values_id = '" . (int)$values_id . "' and language_id = '" . (int)$languages_id . "'");
    $values_values = tep_db_fetch_array($values);

    return $values_values['products_options_values_name'];
  }

  function tep_info_image($image, $alt, $width = '', $height = '') {
    if (tep_not_null($image) && (file_exists(DIR_FS_CATALOG_IMAGES . $image)) ) {
      $image = tep_image(DIR_WS_CATALOG_IMAGES . $image, $alt, $width, $height);
    } else {
      $image = TEXT_IMAGE_NONEXISTENT;
    }

    return $image;
  }

  function tep_break_string($string, $len, $break_char = '-') {
    $l = 0;
    $output = '';
    for ($i=0, $n=strlen($string); $i<$n; $i++) {
      $char = substr($string, $i, 1);
      if ($char != ' ') {
        $l++;
      } else {
        $l = 0;
      }
      if ($l > $len) {
        $l = 1;
        $output .= $break_char;
      }
      $output .= $char;
    }

    return $output;
  }

  function tep_get_country_name($country_id) {
    $country_query = tep_db_query("select countries_name from " . TABLE_COUNTRIES . " where countries_id = '" . (int)$country_id . "'");

    if (!tep_db_num_rows($country_query)) {
      return $country_id;
    } else {
      $country = tep_db_fetch_array($country_query);
      return $country['countries_name'];
    }
  }

  function tep_get_zone_name($country_id, $zone_id, $default_zone) {
    $zone_query = tep_db_query("select zone_name from " . TABLE_ZONES . " where zone_country_id = '" . (int)$country_id . "' and zone_id = '" . (int)$zone_id . "'");
    if (tep_db_num_rows($zone_query)) {
      $zone = tep_db_fetch_array($zone_query);
      return $zone['zone_name'];
    } else {
      return $default_zone;
    }
  }

  function tep_not_null($value) {
    if (is_array($value)) {
      if (sizeof($value) > 0) {
        return true;
      } else {
        return false;
      }
    } else {
      if ( (is_string($value) || is_int($value)) && ($value != '') && ($value != 'NULL') && (strlen(trim($value)) > 0)) {
        return true;
      } else {
        return false;
      }
    }
  }

  function tep_browser_detect($component) {
    global $HTTP_USER_AGENT;

    return stristr($HTTP_USER_AGENT, $component);
  }

  function tep_tax_classes_pull_down($parameters, $selected = '') {
    $select_string = '<select ' . $parameters . '>';
    $classes_query = tep_db_query("select tax_class_id, tax_class_title from " . TABLE_TAX_CLASS . " order by tax_class_title");
    while ($classes = tep_db_fetch_array($classes_query)) {
      $select_string .= '<option value="' . $classes['tax_class_id'] . '"';
      if ($selected == $classes['tax_class_id']) $select_string .= ' SELECTED';
      $select_string .= '>' . $classes['tax_class_title'] . '</option>';
    }
    $select_string .= '</select>';

    return $select_string;
  }

  function tep_geo_zones_pull_down($parameters, $selected = '') {
    $select_string = '<select ' . $parameters . '>';
    $zones_query = tep_db_query("select geo_zone_id, geo_zone_name from " . TABLE_GEO_ZONES . " order by geo_zone_name");
    while ($zones = tep_db_fetch_array($zones_query)) {
      $select_string .= '<option value="' . $zones['geo_zone_id'] . '"';
      if ($selected == $zones['geo_zone_id']) $select_string .= ' SELECTED';
      $select_string .= '>' . $zones['geo_zone_name'] . '</option>';
    }
    $select_string .= '</select>';

    return $select_string;
  }

  function tep_get_geo_zone_name($geo_zone_id) {
    $zones_query = tep_db_query("select geo_zone_name from " . TABLE_GEO_ZONES . " where geo_zone_id = '" . (int)$geo_zone_id . "'");

    if (!tep_db_num_rows($zones_query)) {
      $geo_zone_name = $geo_zone_id;
    } else {
      $zones = tep_db_fetch_array($zones_query);
      $geo_zone_name = $zones['geo_zone_name'];
    }

    return $geo_zone_name;
  }

  function tep_address_format($address_format_id, $address, $html, $boln, $eoln) {
    $address_format_query = tep_db_query("select address_format as format from " . TABLE_ADDRESS_FORMAT . " where address_format_id = '" . (int)$address_format_id . "'");
    $address_format = tep_db_fetch_array($address_format_query);

    $company = tep_output_string_protected($address['company']);
    if (isset($address['firstname']) && tep_not_null($address['firstname'])) {
      $firstname = tep_output_string_protected($address['firstname']);
      $lastname = tep_output_string_protected($address['lastname']);
    } elseif (isset($address['name']) && tep_not_null($address['name'])) {
      $firstname = tep_output_string_protected($address['name']);
      $lastname = '';
    } else {
      $firstname = '';
      $lastname = '';
    }
    $street = tep_output_string_protected($address['street_address']);
    $suburb = tep_output_string_protected($address['suburb']);
    $city = tep_output_string_protected($address['city']);
    $state = tep_output_string_protected($address['state']);
    if (isset($address['country_id']) && tep_not_null($address['country_id'])) {
      $country = tep_get_country_name($address['country_id']);

      if (isset($address['zone_id']) && tep_not_null($address['zone_id'])) {
        $state = tep_get_zone_code($address['country_id'], $address['zone_id'], $state);
      }
    } elseif (isset($address['country']) && tep_not_null($address['country'])) {
      $country = tep_output_string_protected($address['country']);
    } else {
      $country = '';
    }
    $postcode = tep_output_string_protected($address['postcode']);
    $zip = $postcode;

    if ($html) {
// HTML Mode
      $HR = '<hr>';
      $hr = '<hr>';
      if ( ($boln == '') && ($eoln == "\n") ) { // Values not specified, use rational defaults
        $CR = '<br>';
        $cr = '<br>';
        $eoln = $cr;
      } else { // Use values supplied
        $CR = $eoln . $boln;
        $cr = $CR;
      }
    } else {
// Text Mode
      $CR = $eoln;
      $cr = $CR;
      $HR = '----------------------------------------';
      $hr = '----------------------------------------';
    }

    $statecomma = '';
    $streets = $street;
    if ($suburb != '') $streets = $street . $cr . $suburb;
    if ($country == '') $country = tep_output_string_protected($address['country']);
    if ($state != '') $statecomma = $state . ', ';

    $fmt = $address_format['format'];
    eval("\$address = \"$fmt\";");

    if ( (ACCOUNT_COMPANY == 'true') && (tep_not_null($company)) ) {
      $address = $company . $cr . $address;
    }

    return $address;
  }

  ////////////////////////////////////////////////////////////////////////////////////////////////
  //
  // Function    : tep_get_zone_code
  //
  // Arguments   : country           country code string
  //               zone              state/province zone_id
  //               def_state         default string if zone==0
  //
  // Return      : state_prov_code   state/province code
  //
  // Description : Function to retrieve the state/province code (as in FL for Florida etc)
  //
  ////////////////////////////////////////////////////////////////////////////////////////////////
  function tep_get_zone_code($country, $zone, $def_state) {

    $state_prov_query = tep_db_query("select zone_code from " . TABLE_ZONES . " where zone_country_id = '" . (int)$country . "' and zone_id = '" . (int)$zone . "'");

    if (!tep_db_num_rows($state_prov_query)) {
      $state_prov_code = $def_state;
    }
    else {
      $state_prov_values = tep_db_fetch_array($state_prov_query);
      $state_prov_code = $state_prov_values['zone_code'];
    }
    
    return $state_prov_code;
  }

  function tep_get_uprid($prid, $params) {
    $uprid = $prid;
    if ( (is_array($params)) && (!strstr($prid, '{')) ) {
      while (list($option, $value) = each($params)) {
        $uprid = $uprid . '{' . $option . '}' . $value;
      }
    }

    return $uprid;
  }

  function tep_get_prid($uprid) {
    $pieces = explode('{', $uprid);

    return $pieces[0];
  }

  function tep_get_languages() {
    $languages_query = tep_db_query("select languages_id, name, code, image, directory from " . TABLE_LANGUAGES . " order by sort_order");
    while ($languages = tep_db_fetch_array($languages_query)) {
      $languages_array[] = array('id' => $languages['languages_id'],
                                 'name' => $languages['name'],
                                 'code' => $languages['code'],
                                 'image' => $languages['image'],
                                 'directory' => $languages['directory']);
    }

    return $languages_array;
  }

  function tep_get_category_name($category_id, $language_id) {
    $category_query = tep_db_query("select categories_name from " . TABLE_CATEGORIES_DESCRIPTION . " where categories_id = '" . (int)$category_id . "' and language_id = '" . (int)$language_id . "'");
    $category = tep_db_fetch_array($category_query);

    return $category['categories_name'];
  }

  function tep_get_orders_status_name($orders_status_id, $language_id = '') {
    global $languages_id;

    if (!$language_id) $language_id = $languages_id;
    $orders_status_query = tep_db_query("select orders_status_name from " . TABLE_ORDERS_STATUS . " where orders_status_id = '" . (int)$orders_status_id . "' and language_id = '" . (int)$language_id . "'");
    $orders_status = tep_db_fetch_array($orders_status_query);

    return $orders_status['orders_status_name'];
  }

  function tep_get_orders_status() {
    global $languages_id;

    $orders_status_array = array();
    $orders_status_query = tep_db_query("select orders_status_id, orders_status_name from " . TABLE_ORDERS_STATUS . " where language_id = '" . (int)$languages_id . "' order by orders_status_id");
    while ($orders_status = tep_db_fetch_array($orders_status_query)) {
      $orders_status_array[] = array('id' => $orders_status['orders_status_id'],
                                     'text' => $orders_status['orders_status_name']);
    }

    return $orders_status_array;
  }

  function tep_get_products_name($product_id, $language_id = 0) {
    global $languages_id;

    if ($language_id == 0) $language_id = $languages_id;
    $product_query = tep_db_query("select products_name from " . TABLE_PRODUCTS_DESCRIPTION . " where products_id = '" . (int)$product_id . "' and language_id = '" . (int)$language_id . "'");
    $product = tep_db_fetch_array($product_query);

    return $product['products_name'];
  }

  function tep_get_products_description($product_id, $language_id) {
    $product_query = tep_db_query("select products_description from " . TABLE_PRODUCTS_DESCRIPTION . " where products_id = '" . (int)$product_id . "' and language_id = '" . (int)$language_id . "'");
    $product = tep_db_fetch_array($product_query);

    return $product['products_description'];
  }

// BoF Header Tag Controller
function tep_get_products_head_title_tag($product_id, $language_id) {
    $product_query = tep_db_query("select products_head_title_tag from " . TABLE_PRODUCTS_DESCRIPTION . " where products_id = '" . (int)$product_id . "' and language_id = '" . (int)$language_id . "'");
    $product = tep_db_fetch_array($product_query);

    return $product['products_head_title_tag'];
  }

  function tep_get_products_head_desc_tag($product_id, $language_id) {
    $product_query = tep_db_query("select products_head_desc_tag from " . TABLE_PRODUCTS_DESCRIPTION . " where products_id = '" . (int)$product_id . "' and language_id = '" . (int)$language_id . "'");
    $product = tep_db_fetch_array($product_query);

    return $product['products_head_desc_tag'];
  }

  function tep_get_products_head_keywords_tag($product_id, $language_id) {
    $product_query = tep_db_query("select products_head_keywords_tag from " . TABLE_PRODUCTS_DESCRIPTION . " where products_id = '" . (int)$product_id . "' and language_id = '" . (int)$language_id . "'");
    $product = tep_db_fetch_array($product_query);

    return $product['products_head_keywords_tag'];
  }
// EoF Header Tag Controller

  
  function tep_get_products_url($product_id, $language_id) {
    $product_query = tep_db_query("select products_url from " . TABLE_PRODUCTS_DESCRIPTION . " where products_id = '" . (int)$product_id . "' and language_id = '" . (int)$language_id . "'");
    $product = tep_db_fetch_array($product_query);

    return $product['products_url'];
  }

////
// Return the manufacturers URL in the needed language
// TABLES: manufacturers_info
  function tep_get_manufacturer_url($manufacturer_id, $language_id) {
    $manufacturer_query = tep_db_query("select manufacturers_url from " . TABLE_MANUFACTURERS_INFO . " where manufacturers_id = '" . (int)$manufacturer_id . "' and languages_id = '" . (int)$language_id . "'");
    $manufacturer = tep_db_fetch_array($manufacturer_query);

    return $manufacturer['manufacturers_url'];
  }

////
// Wrapper for class_exists() function
// This function is not available in all PHP versions so we test it before using it.
  function tep_class_exists($class_name) {
    if (function_exists('class_exists')) {
      return class_exists($class_name);
    } else {
      return true;
    }
  }

////
// Count how many products exist in a category
// TABLES: products, products_to_categories, categories
  function tep_products_in_category_count($categories_id, $include_deactivated = false) {
    $products_count = 0;

    if ($include_deactivated) {
      $products_query = tep_db_query("select count(*) as total from " . TABLE_PRODUCTS . " p, " . TABLE_PRODUCTS_TO_CATEGORIES . " p2c where p.products_id = p2c.products_id and p2c.categories_id = '" . (int)$categories_id . "'");
    } else {
      $products_query = tep_db_query("select count(*) as total from " . TABLE_PRODUCTS . " p, " . TABLE_PRODUCTS_TO_CATEGORIES . " p2c where p.products_id = p2c.products_id and p.products_status = '1' and p2c.categories_id = '" . (int)$categories_id . "'");
    }

    $products = tep_db_fetch_array($products_query);

    $products_count += $products['total'];

    $childs_query = tep_db_query("select categories_id from " . TABLE_CATEGORIES . " where parent_id = '" . (int)$categories_id . "'");
    if (tep_db_num_rows($childs_query)) {
      while ($childs = tep_db_fetch_array($childs_query)) {
        $products_count += tep_products_in_category_count($childs['categories_id'], $include_deactivated);
      }
    }

    return $products_count;
  }

////
// Count how many subcategories exist in a category
// TABLES: categories
  function tep_childs_in_category_count($categories_id) {
    $categories_count = 0;

    $categories_query = tep_db_query("select categories_id from " . TABLE_CATEGORIES . " where parent_id = '" . (int)$categories_id . "'");
    while ($categories = tep_db_fetch_array($categories_query)) {
      $categories_count++;
      $categories_count += tep_childs_in_category_count($categories['categories_id']);
    }

    return $categories_count;
  }

////
// Returns an array with countries
// TABLES: countries
  function tep_get_countries($default = '') {
    $countries_array = array();
    if ($default) {
      $countries_array[] = array('id' => '',
                                 'text' => $default);
    }
    $countries_query = tep_db_query("select countries_id, countries_name from " . TABLE_COUNTRIES . " order by countries_name");
    while ($countries = tep_db_fetch_array($countries_query)) {
      $countries_array[] = array('id' => $countries['countries_id'],
                                 'text' => $countries['countries_name']);
    }

    return $countries_array;
  }

////
// return an array with country zones
  function tep_get_country_zones($country_id) {
    $zones_array = array();
    $zones_query = tep_db_query("select zone_id, zone_name from " . TABLE_ZONES . " where zone_country_id = '" . (int)$country_id . "' order by zone_name");
    while ($zones = tep_db_fetch_array($zones_query)) {
      $zones_array[] = array('id' => $zones['zone_id'],
                             'text' => $zones['zone_name']);
    }

    return $zones_array;
  }

  function tep_prepare_country_zones_pull_down($country_id = '') {
// preset the width of the drop-down for Netscape
    $pre = '';
    if ( (!tep_browser_detect('MSIE')) && (tep_browser_detect('Mozilla/4')) ) {
      for ($i=0; $i<45; $i++) $pre .= '&nbsp;';
    }

    $zones = tep_get_country_zones($country_id);

    if (sizeof($zones) > 0) {
      $zones_select = array(array('id' => '', 'text' => PLEASE_SELECT));
      $zones = array_merge($zones_select, $zones);
    } else {
      $zones = array(array('id' => '', 'text' => TYPE_BELOW));
// create dummy options for Netscape to preset the height of the drop-down
      if ( (!tep_browser_detect('MSIE')) && (tep_browser_detect('Mozilla/4')) ) {
        for ($i=0; $i<9; $i++) {
          $zones[] = array('id' => '', 'text' => $pre);
        }
      }
    }

    return $zones;
  }

////
// Get list of address_format_id's
  function tep_get_address_formats() {
    $address_format_query = tep_db_query("select address_format_id from " . TABLE_ADDRESS_FORMAT . " order by address_format_id");
    $address_format_array = array();
    while ($address_format_values = tep_db_fetch_array($address_format_query)) {
      $address_format_array[] = array('id' => $address_format_values['address_format_id'],
                                      'text' => $address_format_values['address_format_id']);
    }
    return $address_format_array;
  }

////
// Alias function for Store configuration values in the Administration Tool
  function tep_cfg_pull_down_country_list($country_id) {
    return tep_draw_pull_down_menu('configuration_value', tep_get_countries(), $country_id);
  }

  function tep_cfg_pull_down_zone_list($zone_id) {
    return tep_draw_pull_down_menu('configuration_value', tep_get_country_zones(STORE_COUNTRY), $zone_id);
  }

  function tep_cfg_pull_down_tax_classes($tax_class_id, $key = '') {
    $name = (($key) ? 'configuration[' . $key . ']' : 'configuration_value');

    $tax_class_array = array(array('id' => '0', 'text' => TEXT_NONE));
    $tax_class_query = tep_db_query("select tax_class_id, tax_class_title from " . TABLE_TAX_CLASS . " order by tax_class_title");
    while ($tax_class = tep_db_fetch_array($tax_class_query)) {
      $tax_class_array[] = array('id' => $tax_class['tax_class_id'],
                                 'text' => $tax_class['tax_class_title']);
    }

    return tep_draw_pull_down_menu($name, $tax_class_array, $tax_class_id);
  }

////
// Function to read in text area in admin
function tep_cfg_textarea($text) {
    return tep_draw_textarea_field('configuration_value', false, 35, 5, $text);
  }

  function tep_cfg_get_zone_name($zone_id) {
    $zone_query = tep_db_query("select zone_name from " . TABLE_ZONES . " where zone_id = '" . (int)$zone_id . "'");

    if (!tep_db_num_rows($zone_query)) {
      return $zone_id;
    } else {
      $zone = tep_db_fetch_array($zone_query);
      return $zone['zone_name'];
    }
  }

////
// Sets the status of a banner
  function tep_set_banner_status($banners_id, $status) {
    if ($status == '1') {
      return tep_db_query("update " . TABLE_BANNERS . " set status = '1', expires_impressions = NULL, expires_date = NULL, date_status_change = NULL where banners_id = '" . $banners_id . "'");
    } elseif ($status == '0') {
      return tep_db_query("update " . TABLE_BANNERS . " set status = '0', date_status_change = now() where banners_id = '" . $banners_id . "'");
    } else {
      return -1;
    }
  }

////
// Sets the sort order of a product
  function tep_set_product_sort_order($products_id, $sort_order) {
    return tep_db_query("update " . TABLE_PRODUCTS . " set products_sort_order = '" . $sort_order . "', products_last_modified = now() where products_id = '" . (int)$products_id . "'");
  }


////
// Sets the status of a product
  function tep_set_product_status($products_id, $status) {
    if ($status == '1') {
      return tep_db_query("update " . TABLE_PRODUCTS . " set products_status = '1', products_last_modified = now() where products_id = '" . (int)$products_id . "'");
    } elseif ($status == '0') {
      return tep_db_query("update " . TABLE_PRODUCTS . " set products_status = '0', products_last_modified = now() where products_id = '" . (int)$products_id . "'");
    } else {
      return -1;
    }
  }

////
// Sets the status of a product on special
  function tep_set_specials_status($specials_id, $status) {
    if ($status == '1') {
      return tep_db_query("update " . TABLE_SPECIALS . " set status = '1', expires_date = NULL, date_status_change = NULL where specials_id = '" . (int)$specials_id . "'");
    } elseif ($status == '0') {
      return tep_db_query("update " . TABLE_SPECIALS . " set status = '0', date_status_change = now() where specials_id = '" . (int)$specials_id . "'");
    } else {
      return -1;
    }
  }

////
// Sets timeout for the current script.
// Cant be used in safe mode.
  function tep_set_time_limit($limit) {
    if (!get_cfg_var('safe_mode')) {
      set_time_limit($limit);
    }
  }

////
// Alias function for Store configuration values in the Administration Tool
  function tep_cfg_select_option($select_array, $key_value, $key = '') {
    $string = '';

    for ($i=0, $n=sizeof($select_array); $i<$n; $i++) {
      $name = ((tep_not_null($key)) ? 'configuration[' . $key . ']' : 'configuration_value');

      $string .= '<br><input type="radio" name="' . $name . '" value="' . $select_array[$i] . '"';

      if ($key_value == $select_array[$i]) $string .= ' CHECKED';

      $string .= '> ' . $select_array[$i];
    }

    return $string;
  }

////
// Alias function for module configuration keys
  function tep_mod_select_option($select_array, $key_name, $key_value) {
    reset($select_array);
    while (list($key, $value) = each($select_array)) {
      if (is_int($key)) $key = $value;
      $string .= '<br><input type="radio" name="configuration[' . $key_name . ']" value="' . $key . '"';
      if ($key_value == $key) $string .= ' CHECKED';
      $string .= '> ' . $value;
    }

    return $string;
  }
// USPS Methods 2.5
// Alias function for Store configuration values in the Administration Tool
  function tep_cfg_select_multioption($select_array, $key_value, $key = '') {
    for ($i=0; $i<sizeof($select_array); $i++) {
      $name = (($key) ? 'configuration[' . $key . '][]' : 'configuration_value');
      $string .= '<br><input type="checkbox" name="' . $name . '" value="' . $select_array[$i] . '"';
      $key_values = explode( ", ", $key_value);
      if ( in_array($select_array[$i], $key_values) ) $string .= ' CHECKED';
      $string .= '> ' . $select_array[$i];
    }
    $string .= '<input type="hidden" name="' . $name . '" value="--none--">';
    return $string;
  }
////
// Retreive server information
  function tep_get_system_information() {
    global $HTTP_SERVER_VARS;

    $db_query = tep_db_query("select now() as datetime");
    $db = tep_db_fetch_array($db_query);

    list($system, $host, $kernel) = preg_split('/[\s,]+/', @exec('uname -a'), 5);

    return array('date' => tep_datetime_short(date('Y-m-d H:i:s')),
                 'system' => $system,
                 'kernel' => $kernel,
                 'host' => $host,
                 'ip' => gethostbyname($host),
                 'uptime' => @exec('uptime'),
                 'http_server' => $HTTP_SERVER_VARS['SERVER_SOFTWARE'],
                 'php' => PHP_VERSION,
                 'zend' => (function_exists('zend_version') ? zend_version() : ''),
                 'db_server' => DB_SERVER,
                 'db_ip' => gethostbyname(DB_SERVER),
                 'db_version' => 'MySQL ' . (function_exists('mysql_get_server_info') ? mysql_get_server_info() : ''),
                 'db_date' => tep_datetime_short($db['datetime']));
  }

  function tep_generate_category_path($id, $from = 'category', $categories_array = '', $index = 0) {
    global $languages_id;

    if (!is_array($categories_array)) $categories_array = array();

    if ($from == 'product') {
      $categories_query = tep_db_query("select categories_id from " . TABLE_PRODUCTS_TO_CATEGORIES . " where products_id = '" . (int)$id . "'");
      while ($categories = tep_db_fetch_array($categories_query)) {
        if ($categories['categories_id'] == '0') {
          $categories_array[$index][] = array('id' => '0', 'text' => TEXT_TOP);
        } else {
          $category_query = tep_db_query("select cd.categories_name, c.parent_id from " . TABLE_CATEGORIES . " c, " . TABLE_CATEGORIES_DESCRIPTION . " cd where c.categories_id = '" . (int)$categories['categories_id'] . "' and c.categories_id = cd.categories_id and cd.language_id = '" . (int)$languages_id . "'");
          $category = tep_db_fetch_array($category_query);
          $categories_array[$index][] = array('id' => $categories['categories_id'], 'text' => $category['categories_name']);
          if ( (tep_not_null($category['parent_id'])) && ($category['parent_id'] != '0') ) $categories_array = tep_generate_category_path($category['parent_id'], 'category', $categories_array, $index);
          $categories_array[$index] = array_reverse($categories_array[$index]);
        }
        $index++;
      }
    } elseif ($from == 'category') {
      $category_query = tep_db_query("select cd.categories_name, c.parent_id from " . TABLE_CATEGORIES . " c, " . TABLE_CATEGORIES_DESCRIPTION . " cd where c.categories_id = '" . (int)$id . "' and c.categories_id = cd.categories_id and cd.language_id = '" . (int)$languages_id . "'");
      $category = tep_db_fetch_array($category_query);
      $categories_array[$index][] = array('id' => $id, 'text' => $category['categories_name']);
      if ( (tep_not_null($category['parent_id'])) && ($category['parent_id'] != '0') ) $categories_array = tep_generate_category_path($category['parent_id'], 'category', $categories_array, $index);
    }

    return $categories_array;
  }

  function tep_output_generated_category_path($id, $from = 'category') {
    $calculated_category_path_string = '';
    $calculated_category_path = tep_generate_category_path($id, $from);
    for ($i=0, $n=sizeof($calculated_category_path); $i<$n; $i++) {
      for ($j=0, $k=sizeof($calculated_category_path[$i]); $j<$k; $j++) {
        $calculated_category_path_string .= $calculated_category_path[$i][$j]['text'] . '&nbsp;&gt;&nbsp;';
      }
      $calculated_category_path_string = substr($calculated_category_path_string, 0, -16) . '<br>';
    }
    $calculated_category_path_string = substr($calculated_category_path_string, 0, -4);

    if (strlen($calculated_category_path_string) < 1) $calculated_category_path_string = TEXT_TOP;

    return $calculated_category_path_string;
  }

  function tep_get_generated_category_path_ids($id, $from = 'category') {
    $calculated_category_path_string = '';
    $calculated_category_path = tep_generate_category_path($id, $from);
    for ($i=0, $n=sizeof($calculated_category_path); $i<$n; $i++) {
      for ($j=0, $k=sizeof($calculated_category_path[$i]); $j<$k; $j++) {
        $calculated_category_path_string .= $calculated_category_path[$i][$j]['id'] . '_';
      }
      $calculated_category_path_string = substr($calculated_category_path_string, 0, -1) . '<br>';
    }
    $calculated_category_path_string = substr($calculated_category_path_string, 0, -4);

    if (strlen($calculated_category_path_string) < 1) $calculated_category_path_string = TEXT_TOP;

    return $calculated_category_path_string;
  }

  function tep_remove_category($category_id) {
    $category_image_query = tep_db_query("select categories_image from " . TABLE_CATEGORIES . " where categories_id = '" . (int)$category_id . "'");
    $category_image = tep_db_fetch_array($category_image_query);

    $duplicate_image_query = tep_db_query("select count(*) as total from " . TABLE_CATEGORIES . " where categories_image = '" . tep_db_input($category_image['categories_image']) . "'");
    $duplicate_image = tep_db_fetch_array($duplicate_image_query);

    if ($duplicate_image['total'] < 2) {
      if (file_exists(DIR_FS_CATALOG_IMAGES . $category_image['categories_image'])) {
        @unlink(DIR_FS_CATALOG_IMAGES . $category_image['categories_image']);
      }
    }

    tep_db_query("delete from " . TABLE_CATEGORIES . " where categories_id = '" . (int)$category_id . "'");
    tep_db_query("delete from " . TABLE_CATEGORIES_DESCRIPTION . " where categories_id = '" . (int)$category_id . "'");
    tep_db_query("delete from " . TABLE_PRODUCTS_TO_CATEGORIES . " where categories_id = '" . (int)$category_id . "'");

    if (USE_CACHE == 'true') {
      tep_reset_cache_block('categories');
      tep_reset_cache_block('also_purchased');
    }
  }

  function tep_remove_product($product_id) {
    $product_image_query = tep_db_query("select products_image from " . TABLE_PRODUCTS . " where products_id = '" . (int)$product_id . "'");
    $product_image = tep_db_fetch_array($product_image_query);

    $duplicate_image_query = tep_db_query("select count(*) as total from " . TABLE_PRODUCTS . " where products_image = '" . tep_db_input($product_image['products_image']) . "'");
    $duplicate_image = tep_db_fetch_array($duplicate_image_query);

    if ($duplicate_image['total'] < 2) {
      if (file_exists(DIR_FS_CATALOG_IMAGES . $product_image['products_image'])) {
        @unlink(DIR_FS_CATALOG_IMAGES . $product_image['products_image']);
      }
    }

    tep_db_query("delete from " . TABLE_SPECIALS . " where products_id = '" . (int)$product_id . "'");
    tep_db_query("delete from " . TABLE_PRODUCTS . " where products_id = '" . (int)$product_id . "'");
    tep_db_query("delete from " . TABLE_PRODUCTS_TO_CATEGORIES . " where products_id = '" . (int)$product_id . "'");
    tep_db_query("delete from " . TABLE_PRODUCTS_DESCRIPTION . " where products_id = '" . (int)$product_id . "'");
    tep_db_query("delete from " . TABLE_PRODUCTS_ATTRIBUTES . " where products_id = '" . (int)$product_id . "'");
    tep_db_query("delete from " . TABLE_CUSTOMERS_BASKET . " where products_id = '" . (int)$product_id . "'");
    tep_db_query("delete from " . TABLE_CUSTOMERS_BASKET_ATTRIBUTES . " where products_id = '" . (int)$product_id . "'");

    $product_reviews_query = tep_db_query("select reviews_id from " . TABLE_REVIEWS . " where products_id = '" . (int)$product_id . "'");
    while ($product_reviews = tep_db_fetch_array($product_reviews_query)) {
      tep_db_query("delete from " . TABLE_REVIEWS_DESCRIPTION . " where reviews_id = '" . (int)$product_reviews['reviews_id'] . "'");
    }
    tep_db_query("delete from " . TABLE_REVIEWS . " where products_id = '" . (int)$product_id . "'");

    if (USE_CACHE == 'true') {
      tep_reset_cache_block('categories');
      tep_reset_cache_block('also_purchased');
    }
  }

  function tep_remove_order($order_id, $restock = false) {
    if ($restock == 'on') {
      $order_query = tep_db_query("select products_id, products_quantity from " . TABLE_ORDERS_PRODUCTS . " where orders_id = '" . (int)$order_id . "'");
      while ($order = tep_db_fetch_array($order_query)) {
        tep_db_query("update " . TABLE_PRODUCTS . " set products_quantity = products_quantity + " . $order['products_quantity'] . ", products_ordered = products_ordered - " . $order['products_quantity'] . " where products_id = '" . (int)$order['products_id'] . "'");
      }
    }

    tep_db_query("delete from " . TABLE_ORDERS . " where orders_id = '" . (int)$order_id . "'");
    tep_db_query("delete from " . TABLE_ORDERS_PRODUCTS . " where orders_id = '" . (int)$order_id . "'");
    tep_db_query("delete from " . TABLE_ORDERS_PRODUCTS_ATTRIBUTES . " where orders_id = '" . (int)$order_id . "'");
    tep_db_query("delete from " . TABLE_ORDERS_STATUS_HISTORY . " where orders_id = '" . (int)$order_id . "'");
    tep_db_query("delete from " . TABLE_ORDERS_TOTAL . " where orders_id = '" . (int)$order_id . "'");
  }

  function tep_reset_cache_block($cache_block) {
    global $cache_blocks;

    for ($i=0, $n=sizeof($cache_blocks); $i<$n; $i++) {
      if ($cache_blocks[$i]['code'] == $cache_block) {
        if ($cache_blocks[$i]['multiple']) {
          if ($dir = @opendir(DIR_FS_CACHE)) {
            while ($cache_file = readdir($dir)) {
              $cached_file = $cache_blocks[$i]['file'];
              $languages = tep_get_languages();
              for ($j=0, $k=sizeof($languages); $j<$k; $j++) {
                $cached_file_unlink = ereg_replace('-language', '-' . $languages[$j]['directory'], $cached_file);
                if (ereg('^' . $cached_file_unlink, $cache_file)) {
                  @unlink(DIR_FS_CACHE . $cache_file);
                }
              }
            }
            closedir($dir);
          }
        } else {
          $cached_file = $cache_blocks[$i]['file'];
          $languages = tep_get_languages();
          for ($i=0, $n=sizeof($languages); $i<$n; $i++) {
            $cached_file = ereg_replace('-language', '-' . $languages[$i]['directory'], $cached_file);
            @unlink(DIR_FS_CACHE . $cached_file);
          }
        }
        break;
      }
    }
  }

  function tep_get_file_permissions($mode) {
// determine type
    if ( ($mode & 0xC000) == 0xC000) { // unix domain socket
      $type = 's';
    } elseif ( ($mode & 0x4000) == 0x4000) { // directory
      $type = 'd';
    } elseif ( ($mode & 0xA000) == 0xA000) { // symbolic link
      $type = 'l';
    } elseif ( ($mode & 0x8000) == 0x8000) { // regular file
      $type = '-';
    } elseif ( ($mode & 0x6000) == 0x6000) { //bBlock special file
      $type = 'b';
    } elseif ( ($mode & 0x2000) == 0x2000) { // character special file
      $type = 'c';
    } elseif ( ($mode & 0x1000) == 0x1000) { // named pipe
      $type = 'p';
    } else { // unknown
      $type = '?';
    }

// determine permissions
    $owner['read']    = ($mode & 00400) ? 'r' : '-';
    $owner['write']   = ($mode & 00200) ? 'w' : '-';
    $owner['execute'] = ($mode & 00100) ? 'x' : '-';
    $group['read']    = ($mode & 00040) ? 'r' : '-';
    $group['write']   = ($mode & 00020) ? 'w' : '-';
    $group['execute'] = ($mode & 00010) ? 'x' : '-';
    $world['read']    = ($mode & 00004) ? 'r' : '-';
    $world['write']   = ($mode & 00002) ? 'w' : '-';
    $world['execute'] = ($mode & 00001) ? 'x' : '-';

// adjust for SUID, SGID and sticky bit
    if ($mode & 0x800 ) $owner['execute'] = ($owner['execute'] == 'x') ? 's' : 'S';
    if ($mode & 0x400 ) $group['execute'] = ($group['execute'] == 'x') ? 's' : 'S';
    if ($mode & 0x200 ) $world['execute'] = ($world['execute'] == 'x') ? 't' : 'T';

    return $type .
           $owner['read'] . $owner['write'] . $owner['execute'] .
           $group['read'] . $group['write'] . $group['execute'] .
           $world['read'] . $world['write'] . $world['execute'];
  }

  function tep_remove($source) {
    global $messageStack, $tep_remove_error;

    if (isset($tep_remove_error)) $tep_remove_error = false;

    if (is_dir($source)) {
      $dir = dir($source);
      while ($file = $dir->read()) {
        if ( ($file != '.') && ($file != '..') ) {
          if (is_writeable($source . '/' . $file)) {
            tep_remove($source . '/' . $file);
          } else {
            $messageStack->add(sprintf(ERROR_FILE_NOT_REMOVEABLE, $source . '/' . $file), 'error');
            $tep_remove_error = true;
          }
        }
      }
      $dir->close();

      if (is_writeable($source)) {
        rmdir($source);
      } else {
        $messageStack->add(sprintf(ERROR_DIRECTORY_NOT_REMOVEABLE, $source), 'error');
        $tep_remove_error = true;
      }
    } else {
      if (is_writeable($source)) {
        unlink($source);
      } else {
        $messageStack->add(sprintf(ERROR_FILE_NOT_REMOVEABLE, $source), 'error');
        $tep_remove_error = true;
      }
    }
  }

////
// Output the tax percentage with optional padded decimals
  function tep_display_tax_value($value, $padding = TAX_DECIMAL_PLACES) {
    if (strpos($value, '.')) {
      $loop = true;
      while ($loop) {
        if (substr($value, -1) == '0') {
          $value = substr($value, 0, -1);
        } else {
          $loop = false;
          if (substr($value, -1) == '.') {
            $value = substr($value, 0, -1);
          }
        }
      }
    }

    if ($padding > 0) {
      if ($decimal_pos = strpos($value, '.')) {
        $decimals = strlen(substr($value, ($decimal_pos+1)));
        for ($i=$decimals; $i<$padding; $i++) {
          $value .= '0';
        }
      } else {
A man who works with his hands is a laborer
A man who works with his hands and his brain is a craftsman
But a man who works with his hands and his brain and his heart is an artist. - Charles Dickens

#37   Chemo

Chemo
  • Banned
  • 2,486 posts
  • Real Name:Bobby
  • Location:/usa/kentucky/richmond/

Posted 02 November 2004 - 06:14 PM

wizardsandwars, on Nov 2 2004, 12:44 PM, said:

Not sure what you mean.

The sid will be attached to the URL if cookies are not enabled in the customer's browser, and 'force cookie' use is not set to true.

Since IE is now being distributed with cookies NOT enabled, I would think that this would actually represent the majority of users very soon.

And the session is not only used when going form SSL to nonSSL, but also what you have in your cart. Not to mention that if one person purchases using that session, and enters their details to create an account, and then another person comes along and uses that session, they will have access to all of the first person's account details.

That could be a very serious issue.

<{POST_SNAPBACK}>

YOur site is cacheing the osCid....not good.

wizardsandwars, on Nov 2 2004, 12:52 PM, said:

Another point recently brought to my attention is that if a search engine parses that cashed page, they will also get that very same session id. And the last thing you want to do is have search engine links with session ids in them.

<{POST_SNAPBACK}>

You are correct...we're going to have to find a way to strip the session id's

RavenWulf, on Nov 2 2004, 01:00 PM, said:

Heya, thanks for the quick reply.
I went to your server and dl the v1.1 for the m3 tax class for oscommerce.. however in the install instructions it still says:
STEP 3 - Edit includes/functions/general.php
REPLACE tep_get_tax_rate() it with this code:
function tep_get_tax_rate($class_id, $country_id = -1, $zone_id = -1) {
global $customer_zone_id, $customer_country_id, $osC_Tax;
return $osC_Tax->getTaxRate($class_id, $country_id, $zone_id);
}

REPLACE tep_get_tax_description() it with this code:
function tep_get_tax_description($class_id, $country_id, $zone_id) {
global $osC_Tax;
return $osC_Tax->getTaxRateDescription($class_id, $country_id, $zone_id);
}

I have done the 1st one (tep_get_tax_rate), but I don't have tep_get_tax_description in my general.php file in the catalog/includes/functions folder.. any ideas?  below is a copy of my general.php file:
< SNIP >

<{POST_SNAPBACK}>

I don't even see the tep_get_tax_rate() method...are you sure you're modifying *catalog*/includes/functions/general.php??  It should NOT be the one in the admin folder...

#38   booster

booster
  • Members
  • 71 posts
  • Real Name:Dave

Posted 02 November 2004 - 06:27 PM

Chemo, on Nov 2 2004, 04:12 AM, said:

Stay tuned...there's more to come.  The next steps are:
1. Add popular contributions (discount coupons, featured, specials, etc)
2. Optimize again
3. Search Engine Optmization ala Chemo style

As a side note to #3: it's going to be like the "archive" feature of vBulletin discussion forum software.  It will go beyond all_products and take SEO / sitemaps to another level for osCommerce.  Enough of me blowing my own horn...back to coding :)

<{POST_SNAPBACK}>


SEO is very high on my wannadoo lists so I'm certainly hooked now  :thumbsup:

I've dabbled with some of the SEO advice/contributions here but without any joy, for example I've tried turning on SEO URLs option but for whatever reason that just screws up my sites display - dunno if thats due to some contribution additions I've made but hey ho - it's not been a success so far!

Anyway don't want to jump the gun here, I will await the developments as you get round to them.

Dave.

#39   Chemo

Chemo
  • Banned
  • 2,486 posts
  • Real Name:Bobby
  • Location:/usa/kentucky/richmond/

Posted 02 November 2004 - 06:41 PM

wizardsandwars, on Nov 2 2004, 12:44 PM, said:

Not sure what you mean.

The sid will be attached to the URL if cookies are not enabled in the customer's browser, and 'force cookie' use is not set to true.

Since IE is now being distributed with cookies NOT enabled, I would think that this would actually represent the majority of users very soon.

And the session is not only used when going form SSL to nonSSL, but also what you have in your cart. Not to mention that if one person purchases using that session, and enters their details to create an account, and then another person comes along and uses that session, they will have access to all of the first person's account details.

That could be a very serious issue.

<{POST_SNAPBACK}>

Here are the options:
1. Disable default appending session_id's to URL's setting in tep_href_link()
2. Create code to strip the session id's from the cache code

Here is what my tep_href_link() looks like:
  function tep_href_link($page = '', $parameters = '', $connection = 'NONSSL', $add_session_id = false, $search_engine_safe = true) {
Now, even if the session_id's are not appended to the URL they will still be created for guests uniquely.  Once they log in they get another or resume a previous.  Once they put something in their cart the session_id is recorded in session and that is good for the life of their visit.  If they log in they will get a more permanent session_id.

Edited by Chemo, 02 November 2004 - 06:41 PM.


#40   RavenWulf

RavenWulf
  • Members
  • 297 posts
  • Real Name:Douglas
  • Location:Germany

Posted 02 November 2004 - 07:02 PM

Chemo, on Nov 2 2004, 06:14 PM, said:

YOur site is cacheing the osCid....not good.
You are correct...we're going to have to find a way to strip the session id's
I don't even see the tep_get_tax_rate() method...are you sure you're modifying *catalog*/includes/functions/general.php??  It should NOT be the one in the admin folder...

<{POST_SNAPBACK}>



ok I feel really dumb, sorry...LOL I HAVE got to learn the difference between admin and catalog LOL!!!
:blush:
THANKS!!!
RW
A man who works with his hands is a laborer
A man who works with his hands and his brain is a craftsman
But a man who works with his hands and his brain and his heart is an artist. - Charles Dickens