[phpBB Debug] PHP Warning: in file [ROOT]/includes/crs/crs_misc_functions.php on line 37: mime_content_type(): Empty filename or path
Zen Cart 源代码 seo.url.php

Zen Cart 源代码 seo.url.php




下载文件

文件名: seo.url.php
文件类型: PHP文件
文件大小: 56.17 KiB
MD5: a19585f8928bfaf4ba748424e7ed0ae4

seo.url.php - 打开高亮
<?php
/*
    +----------------------------------------------------------------------+
    |    Ultimate SEO URLs For Zen Cart, version 2.101                      |
    +----------------------------------------------------------------------+
    |                                                                      |
    |    Derrived from Ultimate SEO URLs v2.1 for osCommerce by Chemo       |
    |                                                                      |
    |    Portions Copyright 2011, Andrew Ballanger                          |
    |                                                                      |
    |    Portions Copyright 2005, Joshua Dechant                            |
    |                                                                      |
    |    Portions Copyright 2005, Bobby Easland                             |
    |                                                                      |
    |    Portions Copyright 2003 The zen-cart developers                    |
    |                                                                      |
    +----------------------------------------------------------------------+
    | This source file is subject to version 2.0 of the GPL license,       |
    | that is bundled with this package in the file LICENSE, and is        |
    | available through the world-wide-web at the following url:           |
    | http://www.zen-cart.com/license/2_0.txt.                             |
    | If you did not receive a copy of the zen-cart license and are unable |
    | to obtain it through the world-wide-web, please send a note to       |
    | license@zen-cart.com so we can mail you a copy immediately.          |
    +----------------------------------------------------------------------+
*/

    require_once(dirname(__FILE__) . '/seo.install.php');

    class SEO_URL{
        var $cache;
        var $languages_id;
        var $attributes;
        var $base_url;
        var $base_url_ssl;
        var $reg_anchors;
        var $cache_query;
        var $cache_file;
        var $data;
        var $need_redirect;
        var $is_seopage;
        var $uri;
        var $real_uri;
        var $uri_parsed;
        var $db;
        var $installer;
        // START SEO_URLS_FILTER_PCRE PATCH
        protected static $unicodeEnabled;
        // END SEO_URLS_FILTER_PCRE PATCH

        function SEO_URL($languages_id=''){
            global $session_started;

            $this->installer = &new SEO_URL_INSTALLER();

            $this->db = &$GLOBALS['db'];

            if ($languages_id == '') $languages_id = $_SESSION['languages_id'];

            $this->languages_id = (int)$languages_id;

            $this->data = array();

            $seo_pages = array(
                FILENAME_DEFAULT,
                FILENAME_PRODUCT_INFO,
                'product_free_shipping_info', 
                'product_music_info', 
                'document_product_info', 
                'document_general_info', 
                FILENAME_POPUP_IMAGE,
                FILENAME_PRODUCT_REVIEWS,
                FILENAME_PRODUCT_REVIEWS_INFO,
                FILENAME_EZPAGES,
            );

            // News & Article Manager SEO support
            if (defined('FILENAME_NEWS_INDEX')) $seo_pages[] = FILENAME_NEWS_INDEX;
            if (defined('FILENAME_NEWS_ARTICLE')) $seo_pages[] = FILENAME_NEWS_ARTICLE;
            if (defined('FILENAME_NEWS_COMMENTS')) $seo_pages[] = FILENAME_NEWS_COMMENTS;
            if (defined('FILENAME_NEWS_ARCHIVE')) $seo_pages[] = FILENAME_NEWS_ARCHIVE;
            if (defined('FILENAME_NEWS_RSS')) $seo_pages[] = FILENAME_NEWS_RSS;

            // Info Manager (Open Operations)
            if (defined('FILENAME_INFO_MANAGER')) $seo_pages[] = FILENAME_INFO_MANAGER;

            $this->attributes = array(
                'PHP_VERSION' => PHP_VERSION,
                'SESSION_STARTED' => $session_started,
                'SID' => (defined('SID') && $this->not_null(SID) ? SID : ''),
                'SEO_ENABLED' => defined('SEO_ENABLED') ? SEO_ENABLED : 'false',
                'SEO_ADD_CPATH_TO_PRODUCT_URLS' => defined('SEO_ADD_CPATH_TO_PRODUCT_URLS') ? SEO_ADD_CPATH_TO_PRODUCT_URLS : 'false',
                // START SEO-ADD-PRODUCT-CAT PATCH
                'SEO_ADD_CAT_PARENT' => defined('SEO_ADD_CAT_PARENT') ? SEO_ADD_CAT_PARENT : 'true',
                // END SEO-ADD-PRODUCT-CAT PATCH
                'SEO_ADD_PRODUCT_CAT' => defined('SEO_ADD_PRODUCT_CAT') ? SEO_ADD_PRODUCT_CAT : 'true',
                'SEO_URLS_USE_W3C_VALID' => defined('SEO_URLS_USE_W3C_VALID') ? SEO_URLS_USE_W3C_VALID : 'true',
                'USE_SEO_CACHE_GLOBAL' => defined('USE_SEO_CACHE_GLOBAL') ? USE_SEO_CACHE_GLOBAL : 'false',
                'USE_SEO_CACHE_PRODUCTS' => defined('USE_SEO_CACHE_PRODUCTS') ? USE_SEO_CACHE_PRODUCTS : 'false',
                'USE_SEO_CACHE_CATEGORIES' => defined('USE_SEO_CACHE_CATEGORIES') ? USE_SEO_CACHE_CATEGORIES : 'false',
                'USE_SEO_CACHE_MANUFACTURERS' => defined('USE_SEO_CACHE_MANUFACTURERS') ? USE_SEO_CACHE_MANUFACTURERS : 'false',
                'USE_SEO_CACHE_ARTICLES' => defined('USE_SEO_CACHE_ARTICLES') ? USE_SEO_CACHE_ARTICLES : 'false',
                'USE_SEO_CACHE_INFO_PAGES' => defined('USE_SEO_CACHE_INFO_PAGES') ? USE_SEO_CACHE_INFO_PAGES : 'false',
                'USE_SEO_CACHE_EZ_PAGES' => defined('USE_SEO_CACHE_EZ_PAGES') ? USE_SEO_CACHE_EZ_PAGES : 'false',
                'USE_SEO_REDIRECT' => defined('USE_SEO_REDIRECT') ? USE_SEO_REDIRECT : 'false',
                'SEO_REWRITE_TYPE' => defined('SEO_REWRITE_TYPE') ? SEO_REWRITE_TYPE : 'false',
                'SEO_URLS_FILTER_SHORT_WORDS' => defined('SEO_URLS_FILTER_SHORT_WORDS') ? SEO_URLS_FILTER_SHORT_WORDS : 'false',
                // START SEO_URLS_FILTER_PCRE PATCH
                'SEO_URLS_FILTER_PCRE' => defined('SEO_URLS_FILTER_PCRE') ? $this->expand(SEO_URLS_FILTER_PCRE, true) : 'false',
                // END SEO_URLS_FILTER_PCRE PATCH
                'SEO_CHAR_CONVERT_SET' => defined('SEO_CHAR_CONVERT_SET') ? $this->expand(SEO_CHAR_CONVERT_SET) : 'false',
                'SEO_REMOVE_ALL_SPEC_CHARS' => defined('SEO_REMOVE_ALL_SPEC_CHARS') ? SEO_REMOVE_ALL_SPEC_CHARS : 'false',
                'SEO_PAGES' => $seo_pages,
                'SEO_INSTALLER' => $this->installer->attributes
            );

            $this->base_url = HTTP_SERVER;
            $this->base_url_ssl = HTTPS_SERVER;
            $this->cache = array();

            $this->reg_anchors = array(
                'products_id' => '-p-',
                'cPath' => '-c-',
                'manufacturers_id' => '-m-',
                'pID' => '-pi-',
                'products_id_review' => '-pr-',
                'products_id_review_info' => '-pri-',

                // News & Article Manager SEO support
                'news_article_id' => '-a-',
                'news_comments_article_id' => '-a-',
                'news_dates' => '/',
                'news_archive_dates' => '/archive/',
                'news_rss_feed' => '/rss',

                // Info Manager (Open Operations)
                'info_manager_page_id' => '-i-',

                // EZ-Pages SEO support
                'id' => '-ezp-',
            );

            if ($this->attributes['USE_SEO_CACHE_GLOBAL'] == 'true'){
                $this->cache_file = 'seo_urls_v2_';
                $this->cache_gc();
                if ( $this->attributes['USE_SEO_CACHE_PRODUCTS'] == 'true' ) $this->generate_products_cache();
                if ( $this->attributes['USE_SEO_CACHE_CATEGORIES'] == 'true' ) $this->generate_categories_cache();
                if ( $this->attributes['USE_SEO_CACHE_MANUFACTURERS'] == 'true' ) $this->generate_manufacturers_cache();
                if ( $this->attributes['USE_SEO_CACHE_ARTICLES'] == 'true' && defined('TABLE_NEWS_ARTICLES_TEXT')) $this->generate_news_articles_cache();
                if ( $this->attributes['USE_SEO_CACHE_INFO_PAGES'] == 'true' && defined('TABLE_INFO_MANAGER')) $this->generate_info_manager_cache();
                if ( $this->attributes['USE_SEO_CACHE_EZ_PAGES'] == 'true' ) $this->generate_ezpages_cache();
            }

            if ($this->attributes['USE_SEO_REDIRECT'] == 'true'){
                $this->check_redirect();
            } # end if

            if (null === self::$unicodeEnabled) {
                self::$unicodeEnabled = (@preg_match('/\pL/u', 'a')) ? true : false;
            }
        } # end constructor

        function href_link($page = '', $parameters = '', $connection = 'NONSSL', $add_session_id = true, $static = false, $use_dir_ws_catalog = true) {
            // don't rewrite when disabled
            // don't rewrite images, css, js, xml, real html files, etc
            if ( ($this->attributes['SEO_ENABLED'] == 'false') || (preg_match('/(.+)\.(html?|xml|css|js|png|jpe?g|gif|bmp|tiff?|ico|gz|zip|rar)$/i', $page)) ) {
                return $this->stock_href_link($page, $parameters, $connection, $add_session_id, true, $static, $use_dir_ws_catalog);
            }

            // don't rewrite the paypal IPN notify url
            if ($page == 'ipn_main_handler.php') {
                return $this->stock_href_link($page, $parameters, $connection, $add_session_id, true, $static, $use_dir_ws_catalog);
            }
            // don't rewrite the zhifubao ATN notify url
            if ($page == 'atn_main_handler.php') {
                return $this->stock_href_link($page, $parameters, $connection, $add_session_id, true, $static, $use_dir_ws_catalog);
            }

            // don't rewrite the IPS ATN notify url
            if ($page == 'ips_main_handler.php') {
                return $this->stock_href_link($page, $parameters, $connection, $add_session_id, true, $static, $use_dir_ws_catalog);
            }

            if ((!in_array($page, $this->attributes['SEO_PAGES'])) || (($page == FILENAME_DEFAULT) && (!preg_match('/(cpath|manufacturers_id)/i', $parameters)))) {
                if ($page == FILENAME_DEFAULT) {
                    $page = '';
                } else {
                    $page = $page . '.html';
                }
            }

            if ($connection == 'NONSSL') {
                $link = $this->base_url;
            } elseif ($connection == 'SSL') {
                if (ENABLE_SSL == 'true') {
                    $link = $this->base_url_ssl ;
                } else {
                    $link = $this->base_url;
                }
            }

            if ($use_dir_ws_catalog) {
                if ($connection == 'SSL' && ENABLE_SSL == 'true') {
                    $link .= DIR_WS_HTTPS_CATALOG;
                } else {
                    $link .= DIR_WS_CATALOG;
                }
            }

            if (strstr($page, '?')) {
                $separator = '&';
            } else {
                $separator = '?';
            }

            if ($this->not_null($parameters)) {
                $link .= $this->parse_parameters($page, $parameters, $separator);
            } else {
                // support SEO pages with no parameters
                switch ($page) {
                    case FILENAME_NEWS_RSS:
                        $link .= $this->make_url($page, FILENAME_NEWS_INDEX, 'news_rss_feed', '', '.xml', $separator);
                        break;
                    case FILENAME_NEWS_ARCHIVE:
                        $link .= $this->make_url($page, FILENAME_NEWS_INDEX, 'news_archive_dates', '', '', $separator);
                        break;
                    case FILENAME_NEWS_INDEX:
                        $link .= $this->make_url($page, FILENAME_NEWS_INDEX, 'news_dates', '', '', $separator);
                        break;

                    default:
                        $link .= $page;
                        break;
                }
            }

            $link = $this->add_sid($link, $add_session_id, $connection, $separator);

            switch($this->attributes['SEO_URLS_USE_W3C_VALID']){
                case 'true':
                    if (!isset($_SESSION['customer_id']) && defined('ENABLE_PAGE_CACHE') && ENABLE_PAGE_CACHE == 'true' && class_exists('page_cache')){
                        return $link;
                    } else {
                        return htmlspecialchars(utf8_encode($link));
                    }
                    break;
                case 'false':
                default:
                    return $link;
                    break;
            }
        } # end function

        function stock_href_link($page = '', $parameters = '', $connection = 'NONSSL', $add_session_id = true, $search_engine_safe = true, $static = false, $use_dir_ws_catalog = true) {
            global $request_type, $session_started, $http_domain, $https_domain;

            if (!$this->not_null($page)) {
                die('</td></tr></table></td></tr></table><br /><br /><strong class="note">Error!<br /><br />Unable to determine the page link!</strong><br /><br />');
            }

            if ($connection == 'NONSSL') {
                $link = HTTP_SERVER;
            } elseif ($connection == 'SSL') {
                if (ENABLE_SSL == 'true') {
                    $link = HTTPS_SERVER ;
                } else {
                    $link = HTTP_SERVER;
                }
            } else {
                die('</td></tr></table></td></tr></table><br /><br /><strong class="note">Error!<br /><br />Unable to determine connection method on a link!<br /><br />Known methods: NONSSL SSL</strong><br /><br />');
            }

    if ($use_dir_ws_catalog) {
      if ($connection == 'SSL') {
        $link .= DIR_WS_HTTPS_CATALOG;
      } else {
        $link .= DIR_WS_CATALOG;
      }
    }

            if (!$static) {
                if ($this->not_null($parameters)) {
                    $link .= 'index.php?main_page='. $page . "&" . $this->output_string($parameters);
                } else {
                    $link .= 'index.php?main_page=' . $page;
                }
            } else {
                if ($this->not_null($parameters)) {
                    $link .= $page . "?" . $this->output_string($parameters);
                } else {
                    $link .= $page;
                }
            }

            $separator = '&';

            while ( (substr($link, -1) == '&') || (substr($link, -1) == '?') ) $link = substr($link, 0, -1);

            if ( ($add_session_id == true) && ($session_started == true) && (SESSION_FORCE_COOKIE_USE == 'False') ) {
                if ($this->not_null($this->attributes['SID'])) {
                    $_sid = $this->attributes['SID'];
                } elseif ( ( ($request_type == 'NONSSL') && ($connection == 'SSL') && (ENABLE_SSL == 'true') ) || ( ($request_type == 'SSL') && ($connection == 'NONSSL') ) ) {
                    if ($http_domain != $https_domain) {
                        $_sid = session_name() . '=' . session_id();
                    }
                }
            }

            // clean up the link before processing
            while (strstr($link, '&&')) $link = str_replace('&&', '&', $link);
            while (strstr($link, '&amp;&amp;')) $link = str_replace('&amp;&amp;', '&amp;', $link);

            switch(true){
                case (!isset($_SESSION['customer_id']) && defined('ENABLE_PAGE_CACHE') && ENABLE_PAGE_CACHE == 'true' && class_exists('page_cache')):
                    $page_cache = true;
                    $return = $link . $separator . '<zensid>';
                    break;
                case (isset($_sid)):
                    $page_cache = false;
                    $return = $link . $separator . $_sid;
                    break;
                default:
                    $page_cache = false;
                    $return = $link;
                    break;
            }

            $this->cache['STANDARD_URLS'][] = $link;

            switch(true){
                case ($this->attributes['SEO_URLS_USE_W3C_VALID'] == 'true' && !$page_cache):
                    return htmlspecialchars(utf8_encode($return));
                    break;
                default:
                    return $return;
                    break;
            }# end swtich
        } # end default tep_href function

        function add_sid($link, $add_session_id, $connection, $separator) {
            global $request_type, $http_domain, $https_domain;

            if ( ($add_session_id == true) && ($this->attributes['SESSION_STARTED']) && (SESSION_FORCE_COOKIE_USE == 'False') ) {
                if ($this->not_null($this->attributes['SID'])) {
                    $_sid = $this->attributes['SID'];
                } elseif ( ( ($request_type == 'NONSSL') && ($connection == 'SSL') && (ENABLE_SSL == 'true') ) || ( ($request_type == 'SSL') && ($connection == 'NONSSL') ) ) {
                    if ($http_domain != $https_domain) {
                        $_sid = session_name() . '=' . session_id();
                    }
                }
            }

            switch(true){
                case (!isset($_SESSION['customer_id']) && defined('ENABLE_PAGE_CACHE') && ENABLE_PAGE_CACHE == 'true' && class_exists('page_cache')):
                    $return = $link . $separator . '<zensid>';
                    break;
                case ($this->not_null($_sid)):
                    $return = $link . $separator . $_sid;
                    break;
                default:
                    $return = $link;
                    break;
            } # end switch
            return $return;
        } # end function

/**
 * Function to parse the parameters into an SEO URL
 * @author Bobby Easland
 * @version 1.2
 * @param string $page
 * @param string $params
 * @param string $separator NOTE: passed by reference
 * @return string
 */
    function parse_parameters($page, $params, &$separator) {
        $p = @explode('&', $params);
        krsort($p);
        $container = array();
        foreach ($p as $index => $valuepair){
            $p2 = @explode('=', $valuepair);
            switch ($p2[0]){

                case 'article_id':
                    switch(true) {
                        case ($page == FILENAME_NEWS_ARTICLE):
                            $url = $this->make_url($page, 'news/' . $this->get_news_article_name($p2[1]), 'news_article_id', $p2[1], '.html', $separator);
                            break;
                        case ($page == FILENAME_NEWS_COMMENTS):
                            $url = $this->make_url($page, 'news/' . $this->get_news_article_name($p2[1]), 'news_comments_article_id', $p2[1], '-comments.html', $separator);
                            break;
                        default:
                            $container[$p2[0]] = $p2[1];
                            break;
                    }
                    break;

                case 'date':
                    switch(true) {
                        case ($page == FILENAME_NEWS_ARCHIVE):
                            $url = $this->make_url($page, FILENAME_NEWS_INDEX, 'news_archive_dates', $p2[1], '.html', $separator);
                            break;
                        case ($page == FILENAME_NEWS_INDEX):
                            $url = $this->make_url($page, FILENAME_NEWS_INDEX, 'news_dates', $p2[1], '.html', $separator);
                            break;
                        default:
                            $container[$p2[0]] = $p2[1];
                            break;
                    }
                    break;

                case 'pages_id':
                    switch(true) {
                        case ($page == FILENAME_INFO_MANAGER):
                            $url = $this->make_url($page, $this->get_info_manager_page_name($p2[1]), 'info_manager_page_id', $p2[1], '.html', $separator);
                            break;
                        default:
                            $container[$p2[0]] = $p2[1];
                            break;
                    }
                    break;

                case 'products_id':
                    switch(true) {
                        case ($page == FILENAME_PRODUCT_REVIEWS):
                            $url = $this->make_url($page, $this->get_product_name($p2[1]), 'products_id_review', $p2[1], '.html', $separator);
                            break;
                        case ($page == FILENAME_PRODUCT_REVIEWS_INFO):
                            $url = $this->make_url($page, $this->get_product_name($p2[1]), 'products_id_review_info', $p2[1], '.html', $separator);
                            break;
//                        case ($page == FILENAME_PRODUCT_INFO && !$this->is_attribute_string($params)):
                        case ($page == FILENAME_PRODUCT_INFO):
                        case ($page == 'product_free_shipping_info'):
                        case ($page == 'product_music_info'):
                        case ($page == 'document_product_info'):
                        case ($page == 'document_general_info'):                                    default:
                            $url = $this->make_url($page, $this->get_product_name($p2[1]), $p2[0], $p2[1], '.html', $separator);
                            break;
                    } # end switch
                    break;
                case 'cPath':
                    switch(true){
                        case ($page == FILENAME_DEFAULT):
                            /* START SEO-ADD-PRODUCT-CAT PATCH
                             *
                             * Patched to use SEO_ADD_PRODUCT_CAT
                             * This allows the use of a directory structure for urls.
                             * @author Andrew Ballanger
                             */
                            if($this->attributes['SEO_ADD_PRODUCT_CAT'] == 'true')
                            {
                                // Retrieve the actual category id
                                $tmp = strrpos($p2[1], '_');
                                if($tmp !== false)
                                {
                                    $p2[1] = substr($p2[1], $tmp+1);
                                }
                                $path = array();
                                $this->get_parent_categories_path($path, $p2[1]);
                                $url = $this->make_url($page, implode('/', $path), $p2[0], $p2[1], '/', $separator);
                            }
                            else
                            {
                            // END SEO-ADD-PRODUCT-CAT PATCH
                                $url = $this->make_url($page, $this->get_category_name($p2[1]), $p2[0], $p2[1], '.html', $separator);
                            // START SEO-ADD-PRODUCT-CAT PATCH
                            }
                            // END SEO-ADD-PRODUCT-CAT PATCH
                            break;
                        case ($this->is_product_string($params)):
                            if ($this->attributes['SEO_ADD_CPATH_TO_PRODUCT_URLS'] == 'true') {
                                $container[$p2[0]] = $p2[1];
                            }
                            break;
                        default:
                            $container[$p2[0]] = $p2[1];
                            break;
                        } # end switch
                    break;
                case 'manufacturers_id':
                    switch(true){
                        case ($page == FILENAME_DEFAULT && !$this->is_cPath_string($params) && !$this->is_product_string($params)):
                            $url = $this->make_url($page, $this->get_manufacturer_name($p2[1]), $p2[0], $p2[1], '.html', $separator);
                            break;
                        case ($page == FILENAME_PRODUCT_INFO):
                            break;
                        default:
                            $container[$p2[0]] = $p2[1];
                            break;
                        } # end switch
                    break;
                case 'pID':
                    switch(true){
                        case ($page == FILENAME_POPUP_IMAGE):
                        $url = $this->make_url($page, $this->get_product_name($p2[1]), $p2[0], $p2[1], '.html', $separator);
                        break;
                    default:
                        $container[$p2[0]] = $p2[1];
                        break;
                    } # end switch
                    break;
                case 'id':    // EZ-Pages
                    switch(true){
                        case ($page == FILENAME_EZPAGES):
                            $url = $this->make_url($page, $this->get_ezpages_name($p2[1]), $p2[0], $p2[1], '.html', $separator);
                            break;
                        default:
                            $container[$p2[0]] = $p2[1];
                            break;
                        } # end switch
                    break;
                default:
                    $container[$p2[0]] = $p2[1];
                    break;
            } # end switch
        } # end foreach $p
        $url = isset($url) ? $url : $page;
        if ( sizeof($container) > 0 ) {
            if ( $imploded_params = $this->implode_assoc($container) ) {
                $url .= $separator . $this->output_string( $imploded_params );
                $separator = '&';
            }
        }
        return $url;
    } # end function

/**
 * Function to return the generated SEO URL
 * @author Bobby Easland
 * @version 1.0
 * @param string $page
 * @param string $string Stripped, formed anchor
 * @param string $anchor_type Parameter type (products_id, cPath, etc.)
 * @param integer $id
 * @param string $extension Default = .html
 * @param string $separator NOTE: passed by reference
 * @return string
 */
    function make_url($page, $string, $anchor_type, $id, $extension = '.html', &$separator){
        // Right now there is but one rewrite method since cName was dropped
        // In the future there will be additional methods here in the switch
        switch ( $this->attributes['SEO_REWRITE_TYPE'] ){
            case 'Rewrite':
                // START SEO-ADD-PRODUCT-CAT PATCH
                if($anchor_type == 'cPath' && $this->attributes['SEO_ADD_PRODUCT_CAT'] == 'true')
                {
                    return $string . $extension;
                }
                // END SEO-ADD-PRODUCT-CAT PATCH
                return $string . $this->reg_anchors[$anchor_type] . $id . $extension;
                break;
            default:
                break;
        } # end switch
    } # end function

    function get_info_manager_page_name($pages_id) {
        switch(true){
            case ($this->attributes['USE_SEO_CACHE_GLOBAL'] == 'true' && defined('INFO_MANAGER_PAGE_NAME_' . $pages_id)):
                $return = constant('INFO_MANAGER_PAGE_NAME_' . $pages_id);
                $this->cache['INFO_MANAGER_PAGES'][$pages_id] = $return;
                break;

            case ($this->attributes['USE_SEO_CACHE_GLOBAL'] == 'true' && isset($this->cache['INFO_MANAGER_PAGES'][$pages_id])):
                $return = $this->cache['INFO_MANAGER_PAGES'][$pages_id];
                break;

            default:
                $sql = "SELECT pages_title
                        FROM " . TABLE_INFO_MANAGER . "
                        WHERE pages_id = " . (int)$pages_id . "
                        LIMIT 1";
                $result = $this->db->Execute($sql, false, true, 43200);
                $pages_title = $this->strip($result->fields['pages_title']);
                $this->cache['INFO_MANAGER_PAGES'][$pages_id] = $pages_title;
                $return = $pages_title;
                break;
        }
        return $return;
    }

    function get_news_article_name($article_id) {
        switch(true){
            case ($this->attributes['USE_SEO_CACHE_GLOBAL'] == 'true' && defined('NEWS_ARTICLE_NAME_' . $article_id)):
                $return = constant('NEWS_ARTICLE_NAME_' . $article_id);
                $this->cache['NEWS_ARTICLES'][$article_id] = $return;
                break;

            case ($this->attributes['USE_SEO_CACHE_GLOBAL'] == 'true' && isset($this->cache['NEWS_ARTICLES'][$article_id])):
                $return = $this->cache['NEWS_ARTICLES'][$article_id];
                break;

            default:
                $sql = "SELECT news_article_name
                        FROM " . TABLE_NEWS_ARTICLES_TEXT . "
                        WHERE article_id = " . (int)$article_id . "
                        AND language_id = " . (int)$this->languages_id . "
                        LIMIT 1";
                $result = $this->db->Execute($sql, false, true, 43200);
                $news_article_name = $this->strip($result->fields['news_article_name']);
                $this->cache['NEWS_ARTICLES'][$article_id] = $news_article_name;
                $return = $news_article_name;
                break;
        }
        return $return;
    }

/**
 * Function to get the product name. Use evaluated cache, per page cache, or database query in that order of precedent
 * @author Bobby Easland
 * @version 1.1
 * @param integer $pID
 * @return string Stripped anchor text
 */
    function get_product_name($pID){
        switch(true){
            case ($this->attributes['USE_SEO_CACHE_GLOBAL'] == 'true' && defined('PRODUCT_NAME_' . $pID)):
                $return = constant('PRODUCT_NAME_' . $pID);
                $this->cache['PRODUCTS'][$pID] = $return;
                break;

            case ($this->attributes['USE_SEO_CACHE_GLOBAL'] == 'true' && isset($this->cache['PRODUCTS'][$pID])):
                $return = $this->cache['PRODUCTS'][$pID];
                break;

            default:
                /* START SEO-ADD-PRODUCT-CAT PATCH
                 *
                 * Patched to use SEO_ADD_PRODUCT_CAT
                 * This allows the use of a directory structure for urls.
                 * @author Andrew Ballanger
                 */
                if($this->attributes['SEO_ADD_PRODUCT_CAT'] == 'true')
                {
                    $sql = 'SELECT pd.products_name AS pName, ptc.categories_id AS c_id ' .
                        'FROM ' . TABLE_PRODUCTS_DESCRIPTION . ' AS pd ' .
                        'LEFT JOIN ' . TABLE_PRODUCTS . ' AS p ' .
                        'ON pd.products_id=p.products_id ' .
                        'LEFT JOIN ' . TABLE_PRODUCTS_TO_CATEGORIES . ' AS ptc ' .
                        'ON pd.products_id=ptc.products_id ' .
                        'WHERE pd.products_id=\'' . (int)$pID . '\' ' .
                        'AND language_id=\'' . (int)$this->languages_id . '\' LIMIT 1';
                }
                else
                {
                // END SEO-ADD-PRODUCT-CAT PATCH
                    $sql = 'SELECT pd.products_name as pName ' .
                        'FROM ' . TABLE_PRODUCTS_DESCRIPTION . ' pd ' .
                        'WHERE products_id=\'' . (int)$pID . '\' ' .
                        'AND language_id=\'' . (int)$this->languages_id . '\' LIMIT 1';
                // START SEO-ADD-PRODUCT-CAT PATCH
                }
                // END SEO-ADD-PRODUCT-CAT PATCH
                $result = $this->db->Execute($sql, false, true, 43200);
                $pName = $this->strip($result->fields['pName']);
                // START SEO-ADD-PRODUCT-CAT PATCH
                if($this->attributes['SEO_ADD_PRODUCT_CAT'] == 'true')
                {
                    $path = array();
                    $this->get_parent_categories_path($path, $result->fields['c_id']);
                    $pName = implode('/', $path) . '/' . $pName;
                }
                // END SEO-ADD-PRODUCT-CAT PATCH
                $this->cache['PRODUCTS'][$pID] = $pName;
                $return = $pName;
                break;
        } # end switch
        return $return;
    } # end function

/**
 * Function to get the category name. Use evaluated cache, per page cache, or database query in that order of precedent
 * @author Bobby Easland
 * @version 1.1
 * @param integer $cID NOTE: passed by reference
 * @return string Stripped anchor text
 */
    function get_category_name(&$cID){
        $full_cPath = $this->get_full_cPath($cID, $single_cID); // full cPath needed for uniformity
        switch(true){
            case ($this->attributes['USE_SEO_CACHE_GLOBAL'] == 'true' && defined('CATEGORY_NAME_' . $full_cPath)):
                $return = constant('CATEGORY_NAME_' . $full_cPath);
                $this->cache['CATEGORIES'][$full_cPath] = $return;
                break;
            case ($this->attributes['USE_SEO_CACHE_GLOBAL'] == 'true' && isset($this->cache['CATEGORIES'][$full_cPath])):
                $return = $this->cache['CATEGORIES'][$full_cPath];
                break;
            default:
                switch(true){
                    case ($this->attributes['SEO_ADD_CAT_PARENT'] == 'true'):
                        $sql = "SELECT c.categories_id, c.parent_id, cd.categories_name as cName, cd2.categories_name as pName
                                FROM ".TABLE_CATEGORIES_DESCRIPTION." cd, ".TABLE_CATEGORIES." c
                                LEFT JOIN ".TABLE_CATEGORIES_DESCRIPTION." cd2
                                ON c.parent_id=cd2.categories_id AND cd2.language_id='".(int)$this->languages_id."'
                                WHERE c.categories_id='".(int)$single_cID."'
                                AND cd.categories_id='".(int)$single_cID."'
                                AND cd.language_id='".(int)$this->languages_id."'
                                LIMIT 1";
                        $result = $this->db->Execute($sql, false, true, 43200);
                        $cName = $this->not_null($result->fields['pName']) ? $result->fields['pName'] . ' ' . $result->fields['cName'] : $result->fields['cName'];
                        break;
                    default:
                        $sql = "SELECT categories_name as cName
                                FROM ".TABLE_CATEGORIES_DESCRIPTION."
                                WHERE categories_id='".(int)$single_cID."'
                                AND language_id='".(int)$this->languages_id."'
                                LIMIT 1";
                        $result = $this->db->Execute($sql, false, true, 43200);
                        $cName = $result->fields['cName'];
                        break;
                }
                $cName = $this->strip($cName);
                $this->cache['CATEGORIES'][$full_cPath] = $cName;
                $return = $cName;
                break;
        } # end switch
        $cID = $full_cPath;
        return $return;
    } # end function

/**
 * Function to get the manufacturer name. Use evaluated cache, per page cache, or database query in that order of precedent.
 * @author Bobby Easland
 * @version 1.1
 * @param integer $mID
 * @return string
 */
    function get_manufacturer_name($mID){
        switch(true){
            case ($this->attributes['USE_SEO_CACHE_GLOBAL'] == 'true' && defined('MANUFACTURER_NAME_' . $mID)):
                $return = constant('MANUFACTURER_NAME_' . $mID);
                $this->cache['MANUFACTURERS'][$mID] = $return;
                break;
            case ($this->attributes['USE_SEO_CACHE_GLOBAL'] == 'true' && isset($this->cache['MANUFACTURERS'][$mID])):
                $return = $this->cache['MANUFACTURERS'][$mID];
                break;
            default:
                $sql = "SELECT manufacturers_name as mName
                        FROM ".TABLE_MANUFACTURERS."
                        WHERE manufacturers_id='".(int)$mID."'
                        LIMIT 1";
                $result = $this->db->Execute($sql, false, true, 43200);
                $mName = $this->strip($result->fields['mName']);
                $this->cache['MANUFACTURERS'][$mID] = $mName;
                $return = $mName;
                break;
        } # end switch
        return $return;
    } # end function

/**
 * Function to get the EZ-Pages name. Use evaluated cache, per page cache, or database query in that order of precedent.
 * @author Bobby Easland, Ronald Crawford
 * @version 1.0
 * @param integer $mID
 * @return string
 */
    function get_ezpages_name($ezpID){
        switch(true){
            case ($this->attributes['USE_SEO_CACHE_GLOBAL'] == 'true' && defined('EZPAGES_NAME_' . $ezpID)):
                $return = constant('EZPAGES_NAME_' . $ezpID);
                $this->cache['EZPAGES'][$ezpID] = $return;
                break;
            case ($this->attributes['USE_SEO_CACHE_GLOBAL'] == 'true' && isset($this->cache['EZPAGES'][$ezpID])):
                $return = $this->cache['EZPAGES'][$ezpID];
                break;
            default:
                // modified by zen-cart.cn
                // $sql = "SELECT pages_title as ezpName
                //    FROM ".TABLE_EZPAGES."
                //        WHERE pages_id='".(int)$ezpID."'
                //    LIMIT 1";
                $sql = "SELECT et.pages_title as ezpName
                from  " . TABLE_EZPAGES . " e, " . TABLE_EZPAGES_TEXT . " et 
                where e.pages_id='" . (int)$ezpID . "' 
            and e.pages_id = et.pages_id 
            and et.languages_id = '" . (int)$_SESSION['languages_id'] . "' 
            and status_sidebox = 1 
            and sidebox_sort_order > 0
            LIMIT 1";

                $result = $this->db->Execute($sql, false, true, 43200);
                $ezpName = $this->strip($result->fields['ezpName']);
                $this->cache['EZPAGES'][$ezpID] = $ezpName;
                $return = $ezpName;
                break;
        } # end switch
        return $return;
    } # end function

/**
 * Function to retrieve full cPath from category ID
 * @author Bobby Easland
 * @version 1.1
 * @param mixed $cID Could contain cPath or single category_id
 * @param integer $original Single category_id passed back by reference
 * @return string Full cPath string
 */
    function get_full_cPath($cID, &$original){
        if ( is_numeric(strpos($cID, '_')) ){
            $temp = @explode('_', $cID);
            $original = $temp[sizeof($temp)-1];
            return $cID;
        } else {
            $c = array();
            $this->get_parent_categories_id($c, $cID);
            $c = array_reverse($c);
            $c[] = $cID;
            $original = $cID;
            $cID = sizeof($c) > 1 ? implode('_', $c) : $cID;
            return $cID;
        }
    } # end function

/**
 * Recursion function to retrieve parent categories from category ID
 * @author Bobby Easland
 * @version 1.0
 * @param mixed $categories Passed by reference
 * @param integer $categories_id
 */
    function get_parent_categories_id(&$categories, $categories_id) {
        $sql = "SELECT parent_id FROM " . TABLE_CATEGORIES . " WHERE categories_id = " . (int)$categories_id;

        $parent_categories = $this->db->Execute($sql);

        while (!$parent_categories->EOF) {
            if ($parent_categories->fields['parent_id'] == 0) return true;

            $categories[sizeof($categories)] = $parent_categories->fields['parent_id'];

            if ($parent_categories->fields['parent_id'] != $categories_id) {
                $this->get_parent_categories_id($categories, $parent_categories->fields['parent_id']);
            }

            $parent_categories->MoveNext();
        }
    }

    // START SEO-ADD-PRODUCT-CAT PATCH
    /**
     * Recursion function to retrieve parent categories from category ID.
     *
     * @author Andrew Ballanger
     * @version 1.0
     * @param mixed $path Passed by reference
     * @param integer $categories_id
     */
    function get_parent_categories_path(&$path, $categories_id, &$cPath = array())
    {
        $sql = 'SELECT c.parent_id AS p_id, cd.categories_name AS name ' .
            'FROM ' . TABLE_CATEGORIES . ' c ' .
            'LEFT JOIN ' . TABLE_CATEGORIES_DESCRIPTION . ' cd ' .
            'ON c.categories_id=cd.categories_id ' .
            'AND cd.language_id=\'' . (int)$this->languages_id . '\'' .
            'WHERE c.categories_id=\'' . (int)$categories_id . '\'';

        $parent = $this->db->Execute($sql, false, true, 43200);

        while (!$parent->EOF) {

            // Recurse if the parent id is not empty or equal the passed categories id
            if ($parent->fields['p_id'] != 0 && $parent->fields['p_id'] != $categories_id) {
                $this->get_parent_categories_path($path, $parent->fields['p_id'], $cPath);
            }

            // Add category id to cPath and name to path
            $cPath[sizeof($cPath)] = $categories_id;
            $path[sizeof($path)] = $this->strip($parent->fields['name']) . '-c-' .
            (sizeof($cPath) > 1 ? implode('_', $cPath) : $categories_id);

            $parent->MoveNext();
        }
    }
    // END SEO-ADD-PRODUCT-CAT PATCH

    function not_null($value) {
        return zen_not_null($value);
    }

    function is_attribute_string($params){
        if (preg_match('/products_id=([0-9]+):([a-zA-z0-9]{32})/', $params)) {
            return true;
        }

        return false;
    }

    function is_product_string($params) {
        if (preg_match('/products_id=/i', $params)) {
            return true;
        }

        return false;
    }

    function is_cPath_string($params) {
        if (preg_match('/cPath=/i', $params)) {
            return true;
        }

        return false;
    }

/**
 * Function to strip the string of punctuation and white space
 * @author Bobby Easland (original), Andrew Ballanger (PHP 5.x update)
 * @version 1.2
 * @param string $string
 * @return string Stripped text. Removes all non-alphanumeric characters.
 */
    function strip($string){
        // START SEO_URLS_FILTER_PCRE PATCH
        $retval = $string;
        // First run PCRE filters over the string
        if (is_array($this->attributes['SEO_URLS_FILTER_PCRE']))
        {
            $retval = preg_replace(
                array_keys($this->attributes['SEO_URLS_FILTER_PCRE']),
                array_values($this->attributes['SEO_URLS_FILTER_PCRE']),
                $retval
            );
        }
        // END SEO_URLS_FILTER_PCRE PATCH

        // Next run Character Conversion Sets over the string
        if (is_array($this->attributes['SEO_CHAR_CONVERT_SET'])) $retval = strtr($retval, $this->attributes['SEO_CHAR_CONVERT_SET']);

        $pattern = '';
        // Remove Special Characters from the strings
        if($this->attributes['SEO_REMOVE_ALL_SPEC_CHARS'])
        {
            // Remove all non alphanumeric characters
            if(!self::$unicodeEnabled) {
                // POSIX named classes are not supported by preg_replace
                $pattern = '/[^a-zA-Z0-9\s]/';
            } else {
                // Each language's alphabet.
                $pattern = '/[^\p{L}\p{N}\s]/u';
            }
        }
        else
        {
            // Remove all punctuation
            if(!self::$unicodeEnabled) {
                // POSIX named classes are not supported by preg_replace
                $pattern = '/[!"#$%&\'()*+,\-.\/:;<=>?@[\\\]^_`{|}~\s]/';
            } else {
                // Each language's punctuation.
                $pattern = '/[\p{P}\p{S}\s]/u';
            }
        }

        // modified by zen-cart.cn
        // $retval = preg_replace($pattern, '', strtolower($retval));
        $pattern = '/[\p{P}\p{S}]/u';
        $retval = preg_replace($pattern, '', GBcase($retval,"lower"));

        // $retval = preg_replace('/\s/', '-', $retval);
        $retval = str_replace(" ","-",$retval);   // 替换英文空格 
        $retval = str_replace(chr(32),"-",$retval);  // 替换中文空格
        $retval = str_replace(chr(227),"-",$retval);   // 替换utf-8空格

        return $this->short_name($retval); // return the short filtered name
    } # end function

/**
 * Function to expand the SEO_CONVERT_SET group
 * @author Bobby Easland
 * @version 1.0
 * @param string $set
 * @return mixed
 */
    // START SEO_URLS_FILTER_PCRE PATCH
    function expand($set, $pcre = false){
    // END SEO_URLS_FILTER_PCRE PATCH
        if ( $this->not_null($set) ){
            if ( $data = @explode(',', $set) ){
                foreach ( $data as $index => $valuepair){
                    $p = @explode('=>', $valuepair);
                    // START SEO_URLS_FILTER_PCRE PATCH
                    if($pcre) $p[0] = '/' . $p[0] . '/';
                    // END SEO_URLS_FILTER_PCRE PATCH
                    $container[trim($p[0])] = trim($p[1]);
                }
                return $container;
            } else {
                return 'false';
            }
        } else {
            return 'false';
        }
    } # end function
/**
 * Function to return the short word filtered string
 * @author Bobby Easland
 * @version 1.0
 * @param string $str
 * @param integer $limit
 * @return string Short word filtered
 */
    function short_name($str, $limit=3){
        if ( $this->attributes['SEO_URLS_FILTER_SHORT_WORDS'] != 'false' ) $limit = (int)$this->attributes['SEO_URLS_FILTER_SHORT_WORDS'];
        $foo = @explode('-', $str);
        foreach($foo as $index => $value){
            switch (true){
                case ( strlen($value) <= $limit ):
                    continue;
                default:
                    $container[] = $value;
                    break;
            }
        } # end foreach
        $container = ( sizeof($container) > 1 ? implode('-', $container) : $str );
        return $container;
    }

/**
 * Function to implode an associative array
 * @author Bobby Easland
 * @version 1.0
 * @param array $array Associative data array
 * @param string $inner_glue
 * @param string $outer_glue
 * @return string
 */
    function implode_assoc($array, $inner_glue='=', $outer_glue='&') {
        $output = array();
        foreach( $array as $key => $item ){
            if ( $this->not_null($key) && $this->not_null($item) ){
                $output[] = $key . $inner_glue . $item;
            }
        } # end foreach
        return @implode($outer_glue, $output);
    }

/**
 * Function to translate a string
 * @author Bobby Easland
 * @version 1.0
 * @param string $data String to be translated
 * @param array $parse Array of tarnslation variables
 * @return string
 */
    function parse_input_field_data($data, $parse) {
        return strtr(trim($data), $parse);
    }

/**
 * Function to output a translated or sanitized string
 * @author Bobby Easland
 * @version 1.0
 * @param string $sting String to be output
 * @param mixed $translate Array of translation characters
 * @param boolean $protected Switch for htemlspecialchars processing
 * @return string
 */
    function output_string($string, $translate = false, $protected = false) {
        if ($protected == true) {
          return htmlspecialchars($string);
        } else {
          if ($translate == false) {
            return $this->parse_input_field_data($string, array('"' => '&quot;'));
          } else {
            return $this->parse_input_field_data($string, $translate);
          }
        }
    }

/**
 * Function to generate EZ-Pages cache entries
 * @author Bobby Easland, Ronald Crawford
 * @version 1.0
 */
    function generate_ezpages_cache(){
        $this->is_cached($this->cache_file . 'ezpages', $is_cached, $is_expired);
        if ( !$is_cached || $is_expired ) {
                // modified by zen-cart.cn
                $sql = "SELECT e.pages_id as id, et.pages_title as name
                FROM  " . TABLE_EZPAGES . " e, " . TABLE_EZPAGES_TEXT . " et 
                WHERE e.pages_id = et.pages_id 
            and et.languages_id = '" . (int)$this->languages_id . "' 
            and status_sidebox = 1 and sidebox_sort_order > 0";

        $ezpages = $this->db->Execute($sql, false, true, 43200);
        $ezpages_cache = '';
        while (!$ezpages->EOF) {
            $define = 'define(\'EZPAGES_NAME_' . $ezpages->fields['id'] . '\', \'' . $this->strip($ezpages->fields['name']) . '\');';
            $ezpages_cache .= $define . "\n";
            eval("$define");
            $product->MoveNext();
        }
        $this->save_cache($this->cache_file . 'ezpages', $ezpages_cache, 'EVAL', 1 , 1);
        unset($ezpages_cache);
        } else {
            $this->get_cache($this->cache_file . 'ezpages');
        }
    } # end function

/**
 * Function to generate products cache entries
 * @author Bobby Easland
 * @version 1.0
 */
    function generate_products_cache(){
        $this->is_cached($this->cache_file . 'products', $is_cached, $is_expired);
        if ( !$is_cached || $is_expired ) {
            /* START SEO-ADD-PRODUCT-CAT PATCH
             *
             * Patched to use SEO_ADD_PRODUCT_CAT
             * This allows the use of a directory structure for urls.
             * @author Andrew Ballanger
             */
            $sql = 'SELECT p.products_id as id, ptc.categories_id as c_id, pd.products_name as name ' .
                   'FROM ' . TABLE_PRODUCTS  . ' AS p ' .
                'LEFT JOIN ' . TABLE_PRODUCTS_DESCRIPTION . ' AS pd ' .
                'ON p.products_id=pd.products_id ' .
                'LEFT JOIN ' . TABLE_PRODUCTS_TO_CATEGORIES . ' AS ptc ' .
                'ON p.products_id=ptc.products_id ' .
                'WHERE p.products_status=\'1\' ' .
                'AND pd.language_id=\'' . (int)$this->languages_id . '\'';
            // END SEO-ADD-PRODUCT-CAT PATCH
            $product = $this->db->Execute($sql, false, true, 43200);
            $prod_cache = '';
            while (!$product->EOF) {
                // START SEO-ADD-PRODUCT-CAT PATCH
                $define = 'define(\'PRODUCT_NAME_' . $product->fields['id'] . '\', \'';
                if($this->attributes['SEO_ADD_PRODUCT_CAT'] == 'true')
                {
                    $path = array();
                    $this->get_parent_categories_path($path, $product->fields['c_id']);
                    $define .= implode('/', $path) . '/';
                }
                $define .= $this->strip($product->fields['name']) . '\');';
                // END SEO-ADD-PRODUCT-CAT PATCH
                $prod_cache .= $define . "\n";
                eval("$define");
                $product->MoveNext();
            }
            $this->save_cache($this->cache_file . 'products', $prod_cache, 'EVAL', 1 , 1);
            unset($prod_cache);
        }
        else
        {
            $this->get_cache($this->cache_file . 'products');
        }
    } # end function

/**
 * Function to generate manufacturers cache entries
 * @author Bobby Easland
 * @version 1.0
 */
    function generate_manufacturers_cache(){
        $this->is_cached($this->cache_file . 'manufacturers', $is_cached, $is_expired);
        if ( !$is_cached || $is_expired ) { // it's not cached so create it
        $sql = "SELECT m.manufacturers_id as id, m.manufacturers_name as name
                FROM ".TABLE_MANUFACTURERS." m
                LEFT JOIN ".TABLE_MANUFACTURERS_INFO." md
                ON m.manufacturers_id=md.manufacturers_id
                AND md.languages_id='".(int)$this->languages_id."'";
        $manufacturers = $this->db->Execute($sql, false, true, 43200);
        $man_cache = '';
        while (!$manufacturers->EOF) {
            $define = 'define(\'MANUFACTURER_NAME_' . $manufacturer->fields['id'] . '\', \'' . $this->strip($manufacturer->fields['name']) . '\');';
            $man_cache .= $define . "\n";
            eval("$define");
            $manufacturers->MoveNext();
        }
        $this->save_cache($this->cache_file . 'manufacturers', $man_cache, 'EVAL', 1 , 1);
        unset($man_cache);
        } else {
            $this->get_cache($this->cache_file . 'manufacturers');
        }
    } # end function

/**
 * Function to generate categories cache entries
 * @author Bobby Easland
 * @version 1.1
 */
    function generate_categories_cache(){
        $this->is_cached($this->cache_file . 'categories', $is_cached, $is_expired);
        if ( !$is_cached || $is_expired ) { // it's not cached so create it
            switch(true){
                /* START SEO-ADD-PRODUCT-CAT PATCH
                 *
                 * Patched to use SEO_ADD_PRODUCT_CAT
                 * This allows the use of a directory structure for urls.
                 * @author Andrew Ballanger
                 */
                // Disable SEO_ADD_CAT_PARENT if using the SEO-ADD-PRODUCT-CAT patch
                case ($this->attributes['SEO-ADD-PRODUCT-CAT'] != 'true' && $this->attributes['SEO_ADD_CAT_PARENT'] == 'true'):
                // END SEO-ADD-PRODUCT-CAT PATCH
                    $sql = "SELECT c.categories_id as id, c.parent_id, cd.categories_name as cName, cd2.categories_name as pName
                            FROM ".TABLE_CATEGORIES." c
                            LEFT JOIN ".TABLE_CATEGORIES_DESCRIPTION." cd2
                            ON c.parent_id=cd2.categories_id AND cd2.language_id='".(int)$this->languages_id."',
                            ".TABLE_CATEGORIES_DESCRIPTION." cd
                            WHERE c.categories_id=cd.categories_id
                            AND cd.language_id='".(int)$this->languages_id."'";
                    //IMAGINADW.COM;
                    break;
                default:
                    $sql = "SELECT categories_id as id, categories_name as cName
                            FROM ".TABLE_CATEGORIES_DESCRIPTION."
                            WHERE language_id='".(int)$this->languages_id."'";
                    break;
            } # end switch
            $category = $this->db->Execute($sql, false, true, 43200);
            $cat_cache = '';
            while (!$category->EOF) {
                // START SEO-ADD-PRODUCT-CAT PATCH
                if($this->attributes['SEO_ADD_PRODUCT_CAT'] == 'true')
                {
                    $path = array();
                    $this->get_parent_categories_path($path, $category->fields['id']);
                    $name = implode('/', $path) . '/' . $cName;
                }
                else
                {
                // END SEO-ADD-PRODUCT-CAT PATCH
                    $id = $this->get_full_cPath($category->fields['id'], $single_cID);
                    $name = $this->not_null($category->fields['pName']) ? $category->fields['pName'] . ' ' . $category->fields['cName'] : $category->fields['cName'];
                // START SEO-ADD-PRODUCT-CAT PATCH
                }
                // END SEO-ADD-PRODUCT-CAT PATCH
                $define = 'define(\'CATEGORY_NAME_' . $id . '\', \'' . $this->strip($name) . '\');';
                $cat_cache .= $define . "\n";
                eval("$define");
                $category->MoveNext();
            }
            $this->save_cache($this->cache_file . 'categories', $cat_cache, 'EVAL', 1 , 1);
            unset($cat_cache);
        } else {
            $this->get_cache($this->cache_file . 'categories');
        }
    } # end function

/**
 * Function to generate articles cache entries
 * @author Bobby Easland
 * @version 1.0
 */
    function generate_news_articles_cache(){
        $this->is_cached($this->cache_file . 'news_articles', $is_cached, $is_expired);
        if ( !$is_cached || $is_expired ) { // it's not cached so create it
            $sql = "SELECT article_id as id, news_article_name as name
                    FROM ".TABLE_NEWS_ARTICLES_TEXT."
                    WHERE language_id = '".(int)$this->languages_id."'";
            $article = $this->db->Execute($sql, false, true, 43200);
            $article_cache = '';
            while (!$article->EOF) {
                $define = 'define(\'NEWS_ARTICLE_NAME_' . $article->fields['id'] . '\', \'' . $this->strip($article->fields['name']) . '\');';
                $article_cache .= $define . "\n";
                eval("$define");
                $article->MoveNext();
            }
            $this->save_cache($this->cache_file . 'news_articles', $article_cache, 'EVAL', 1 , 1);
            unset($article_cache);
        } else {
            $this->get_cache($this->cache_file . 'news_articles');
        }
    } # end function

/**
 * Function to generate information cache entries
 * @author Bobby Easland
 * @version 1.0
 */
    function generate_info_manager_cache(){
        $this->is_cached($this->cache_file . 'info_manager', $is_cached, $is_expired);
        if ( !$is_cached || $is_expired ) { // it's not cached so create it
            $sql = "SELECT pages_id as id, pages_title as name
                    FROM ".TABLE_INFO_MANAGER;
            $information = $this->db->Execute($sql, false, true, 43200);
            $information_cache = '';
            while (!$information->EOF) {
                $define = 'define(\'INFO_MANAGER_PAGE_NAME_' . $information->fields['id'] . '\', \'' . $this->strip($information->fields['name']) . '\');';
                $information_cache .= $define . "\n";
                eval("$define");
                $information->MoveNext();
            }
            $this->save_cache($this->cache_file . 'info_manager', $information_cache, 'EVAL', 1 , 1);
            unset($information_cache);
        } else {
            $this->get_cache($this->cache_file . 'info_manager');
        }
    } # end function

/**
 * Function to save the cache to database
 * @author Bobby Easland
 * @version 1.0
 * @param string $name Cache name
 * @param mixed $value Can be array, string, PHP code, or just about anything
 * @param string $method RETURN, ARRAY, EVAL
 * @param integer $gzip Enables compression
 * @param integer $global Sets whether cache record is global is scope
 * @param string $expires Sets the expiration
 */
    function save_cache($name, $value, $method='RETURN', $gzip=1, $global=0, $expires = '30 days'){
        global $queryCache;

        $expires = date('Y-m-d H:i:s', strtotime('+' . $expires));
        if ($method == 'ARRAY') $value = serialize($value);
        $value = ( $gzip === 1 ? base64_encode(gzdeflate($value, 1)) : addslashes($value) );
        $sql_data_array = array(
            'cache_id' => md5($name),
            'cache_language_id' => (int)$this->languages_id,
            'cache_name' => $name,
            'cache_data' => $value,
            'cache_global' => (int)$global,
            'cache_gzip' => (int)$gzip,
            'cache_method' => $method,
            'cache_date' => date("Y-m-d H:i:s"),
            'cache_expires' => $expires
        );
        $this->is_cached($name, $is_cached, $is_expired);
        $cache_check = ( $is_cached ? 'true' : 'false' );
        switch ( $cache_check ) {
            case 'true':
                zen_db_perform(TABLE_SEO_CACHE, $sql_data_array, 'update', "cache_id='".md5($name)."'");
                break;
            case 'false':
                zen_db_perform(TABLE_SEO_CACHE, $sql_data_array, 'insert');
                break;
            default:
                break;
        } # end switch ($cache check)

        // Fix for query_cache
        $query = "SELECT cache_expires FROM " . TABLE_SEO_CACHE . " WHERE cache_id='".md5($name)."' AND cache_language_id='".(int)$this->languages_id."' LIMIT 1";
        if(isset($queryCache) && $queryCache->inCache($query))
            $queryCache->removeFromCache($query);

        # unset the variables...clean as we go
        unset($value, $expires, $sql_data_array, $query);
    }# end function save_cache()

/**
 * Function to get cache entry
 * @author Bobby Easland
 * @version 1.0
 * @param string $name
 * @param boolean $local_memory
 * @return mixed
 */
    function get_cache($name = 'GLOBAL', $local_memory = false){
        $select_list = 'cache_id, cache_language_id, cache_name, cache_data, cache_global, cache_gzip, cache_method, cache_date, cache_expires';
        $global = ( $name == 'GLOBAL' ? true : false ); // was GLOBAL passed or is using the default?
        switch($name){
            case 'GLOBAL':
                $cache = $this->db->Execute("SELECT ".$select_list." FROM " . TABLE_SEO_CACHE . " WHERE cache_language_id='".(int)$this->languages_id."' AND cache_global='1'");
                break;
            default:
                $cache = $this->db->Execute("SELECT ".$select_list." FROM " . TABLE_SEO_CACHE . " WHERE cache_id='".md5($name)."' AND cache_language_id='".(int)$this->languages_id."'");
                break;
        }
        $num_rows = $cache->RecordCount();
        if ($num_rows){
            $container = array();
            while(!$cache->EOF){
                $cache_name = $cache->fields['cache_name'];
                if ( $cache->fields['cache_expires'] > date("Y-m-d H:i:s") ) {
                    $cache_data = ( $cache->fields['cache_gzip'] == 1 ? gzinflate(base64_decode($cache->fields['cache_data'])) : stripslashes($cache->fields['cache_data']) );
                    switch($cache->fields['cache_method']){
                        case 'EVAL': // must be PHP code
                            eval("$cache_data");
                            break;
                        case 'ARRAY':
                            $cache_data = unserialize($cache_data);
                        case 'RETURN':
                        default:
                            break;
                    } # end switch ($cache['cache_method'])
                    if ($global) $container['GLOBAL'][$cache_name] = $cache_data;
                    else $container[$cache_name] = $cache_data; // not global
                } else { // cache is expired
                    if ($global) $container['GLOBAL'][$cache_name] = false;
                    else $container[$cache_name] = false;
                }# end if ( $cache['cache_expires'] > date("Y-m-d H:i:s") )
                if ( $this->keep_in_memory || $local_memory ) {
                    if ($global) $this->data['GLOBAL'][$cache_name] = $container['GLOBAL'][$cache_name];
                    else $this->data[$cache_name] = $container[$cache_name];
                }
                $cache->MoveNext();
            } # end while ($cache = $this->DB->FetchArray($this->cache_query))
            unset($cache_data);
            switch (true) {
                case ($num_rows == 1):
                    if ($global){
                        if ($container['GLOBAL'][$cache_name] == false || !isset($container['GLOBAL'][$cache_name])) return false;
                        else return $container['GLOBAL'][$cache_name];
                    } else { // not global
                        if ($container[$cache_name] == false || !isset($container[$cache_name])) return false;
                        else return $container[$cache_name];
                    } # end if ($global)
                case ($num_rows > 1):
                default:
                    return $container;
                    break;
            }# end switch (true)
        } else {
            return false;
        }# end if ( $num_rows )
    } # end function get_cache()

/**
 * Function to get cache from memory
 * @author Bobby Easland
 * @version 1.0
 * @param string $name
 * @param string $method
 * @return mixed
 */
    function get_cache_memory($name, $method = 'RETURN'){
        $data = ( isset($this->data['GLOBAL'][$name]) ? $this->data['GLOBAL'][$name] : $this->data[$name] );
        if ( isset($data) && !empty($data) && $data != false ){
            switch($method){
                case 'EVAL': // data must be PHP
                    eval("$data");
                    return true;
                    break;
                case 'ARRAY':
                case 'RETURN':
                default:
                    return $data;
                    break;
            } # end switch ($method)
        } else {
            return false;
        } # end if (isset($data) && !empty($data) && $data != false)
    } # end function get_cache_memory()

/**
 * Function to perform basic garbage collection for database cache system
 * @author Bobby Easland
 * @version 1.0
 */
    function cache_gc(){
        $this->db->Execute("DELETE FROM " . TABLE_SEO_CACHE . " WHERE cache_expires <= '" . date("Y-m-d H:i:s") . "'");
    }

/**
 * Function to check if the cache is in the database and expired
 * @author Bobby Easland
 * @version 1.0
 * @param string $name
 * @param boolean $is_cached NOTE: passed by reference
 * @param boolean $is_expired NOTE: passed by reference
 */
    function is_cached($name, &$is_cached, &$is_expired){ // NOTE: $is_cached and $is_expired is passed by reference !!
        $this->cache_query = $this->db->Execute("SELECT cache_expires FROM " . TABLE_SEO_CACHE . " WHERE cache_id='".md5($name)."' AND cache_language_id='".(int)$this->languages_id."' LIMIT 1");
        $is_cached = ( $this->cache_query->RecordCount() > 0 ? true : false );

        if ($is_cached){
            $is_expired = ( $this->cache_query->fields['cache_expires'] <= date("Y-m-d H:i:s") ? true : false );
            unset($check);
        }
    }# end function is_cached()

/**
 * Function to initialize the redirect logic
 * @author Bobby Easland
 * @version 1.1
 */
    function check_redirect(){
        $this->need_redirect = false;
        $this->uri = ltrim( basename($_SERVER['REQUEST_URI']), '/' );
        $this->real_uri = ltrim( basename($_SERVER['SCRIPT_NAME']) . '?' . $_SERVER['QUERY_STRING'], '/' );

        // damn zen cart attributes use illegal url characters
        if ($this->is_attribute_string($this->uri)) {
            $parsed_url = parse_url($this->uri);
            $this->uri_parsed = parse_url($parsed_url['scheme']);
            $this->uri_parsed['query'] = preg_replace('/products_id=([0-9]+)/', 'products_id=$1:' . $parsed_url['path'], $this->uri_parsed['query']);
        } else {
            $this->uri_parsed = parse_url($this->uri);
        }

        $this->attributes['SEO_REDIRECT']['URI'] = $this->uri;
        $this->attributes['SEO_REDIRECT']['REAL_URI'] = $this->real_uri;
        $this->attributes['SEO_REDIRECT']['URI_PARSED'] = $this->uri_parsed;
        $this->need_redirect();
        $this->check_seo_page();

        if ($this->need_redirect && $this->is_seopage && $this->attributes['USE_SEO_REDIRECT'] == 'true') {
            $this->do_redirect();
        }
    } # end function

/**
 * Function to check if the URL needs to be redirected
 * @author Bobby Easland
 * @version 1.2
 */
    function need_redirect() {
        $this->need_redirect = ((preg_match('/main_page=/i', $this->uri)) ? true : false);
        // QUICK AND DIRTY WAY TO DISABLE REDIRECTS ON PAGES WHEN SEO_URLS_ONLY_IN is enabled IMAGINADW.COM
        $sefu = explode(",", preg_replace('/ +/', '', SEO_URLS_ONLY_IN ));
        if ((SEO_URLS_ONLY_IN!="") && !in_array($_GET['main_page'],$sefu) ) $this->need_redirect = false;
        // IMAGINADW.COM

        $this->attributes['SEO_REDIRECT']['NEED_REDIRECT'] = $this->need_redirect ? 'true' : 'false';
    }

/**
 * Function to check if it's a valid redirect page
 * @author Bobby Easland
 * @version 1.1
 */
    function check_seo_page() {
        if (!isset($_GET['main_page']) || (!$this->not_null($_GET['main_page']))) {
            $_GET['main_page'] = 'index';
        }

        $this->is_seopage = (($this->attributes['SEO_ENABLED'] == 'true') ? true : false);

        $this->attributes['SEO_REDIRECT']['IS_SEOPAGE'] = $this->is_seopage ? 'true' : 'false';
    }

/**
 * Function to perform redirect
 * @author Bobby Easland
 * @version 1.0
 */
    function do_redirect() {
        $p = @explode('&', $this->uri_parsed['query']);
        foreach( $p as $index => $value ) {
            $tmp = @explode('=', $value);

            if ($tmp[0] == 'main_page') continue;

            switch($tmp[0]){
                case 'products_id':
                    if ($this->is_attribute_string('products_id=' . $tmp[1])) {
                        $pieces = explode(':', $tmp[1]);
                        $params[] = $tmp[0] . '=' . $pieces[0];
                    } else {
                        $params[] = $tmp[0] . '=' . $tmp[1];
                    }
                    break;
                default:
                    $params[] = $tmp[0].'='.$tmp[1];
                    break;
            }
        } # end foreach( $params as $var => $value )
        $params = ( sizeof($params) > 1 ? implode('&', $params) : $params[0] );

        $url = $this->href_link($_GET['main_page'], $params, 'NONSSL', false);
        // cleanup url for redirection
        $url = str_replace('&amp;', '&', $url);

        switch($this->attributes['USE_SEO_REDIRECT']){
            case 'true':
                header("HTTP/1.1 301 Moved Permanently");
                header("Location: $url");
                break;
            default:
                $this->attributes['SEO_REDIRECT']['REDIRECT_URL'] = $url;
                break;
        } # end switch
    } # end function do_redirect

} # end class
?>