diff --git a/blog/wp-content/plugins/shopp/Shopp.php b/blog/wp-content/plugins/shopp/Shopp.php new file mode 100644 index 0000000..111b671 --- /dev/null +++ b/blog/wp-content/plugins/shopp/Shopp.php @@ -0,0 +1,1617 @@ +. + +*/ + +define('SHOPP_VERSION','1.0.17'); +define('SHOPP_REVISION','$Rev: 661 $'); +define('SHOPP_GATEWAY_USERAGENT','WordPress Shopp Plugin/'.SHOPP_VERSION); +define('SHOPP_HOME','http://shopplugin.net/'); +define('SHOPP_DOCS','http://docs.shopplugin.net/'); + +require("core/functions.php"); +require_once("core/DB.php"); +require("core/model/Settings.php"); + +if (isset($_GET['shopp_image']) || + preg_match('/images\/\d+/',$_SERVER['REQUEST_URI'])) + shopp_image(); +if (isset($_GET['shopp_lookup']) && $_GET['shopp_lookup'] == 'catalog.css') shopp_catalog_css(); +if (isset($_GET['shopp_lookup']) && $_GET['shopp_lookup'] == 'settings.js') + shopp_settings_js(basename(dirname(__FILE__))); + +require("core/Flow.php"); +require("core/model/Cart.php"); +require("core/model/ShipCalcs.php"); +require("core/model/Catalog.php"); +require("core/model/Purchase.php"); + +$Shopp = new Shopp(); + +class Shopp { + var $Cart; + var $Flow; + var $Settings; + var $ShipCalcs; + var $Product; + var $Category; + var $Gateway; + var $Catalog; + var $_debug; + + function Shopp () { + if (WP_DEBUG) { + $this->_debug = new StdClass(); + if (function_exists('memory_get_peak_usage')) + $this->_debug->memory = "Initial: ".number_format(memory_get_peak_usage(true)/1024/1024, 2, '.', ',') . " MB
"; + if (function_exists('memory_get_usage')) + $this->_debug->memory = "Initial: ".number_format(memory_get_usage(true)/1024/1024, 2, '.', ',') . " MB
"; + } + + $this->path = dirname(__FILE__); + $this->file = basename(__FILE__); + $this->directory = basename($this->path); + + $this->uri = WP_PLUGIN_URL."/".$this->directory; + $this->siteurl = get_bloginfo('url'); + $this->wpadminurl = admin_url(); + + $this->secure = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on"); + if ($this->secure) { + $this->uri = str_replace('http://','https://',$this->uri); + $this->siteurl = str_replace('http://','https://',$this->siteurl); + $this->wpadminurl = str_replace('http://','https://',$this->wpadminurl); + } + + $this->Settings = new Settings(); + $this->Flow = new Flow($this); + + register_deactivation_hook($this->directory."/Shopp.php", array(&$this, 'deactivate')); + register_activation_hook($this->directory."/Shopp.php", array(&$this, 'install')); + + // Keep any DB operations from occuring while in maintenance mode + if (!empty($_GET['updated']) && + ($this->Settings->get('maintenance') == "on" || $this->Settings->unavailable)) { + $this->Flow->upgrade(); + $this->Settings->save("maintenance","off"); + } elseif ($this->Settings->get('maintenance') == "on") { + add_action('init', array(&$this, 'ajax')); + add_action('wp', array(&$this, 'shortcodes')); + return true; + } + + // Initialize defaults if they have not been entered + if (!$this->Settings->get('shopp_setup')) { + if ($this->Settings->unavailable) return true; + $this->Flow->setup(); + } + + add_action('init', array(&$this,'init')); + add_action('init', array(&$this, 'xorder')); + add_action('init', array(&$this, 'ajax')); + add_action('parse_request', array(&$this, 'lookups') ); + add_action('parse_request', array(&$this, 'cart')); + add_action('parse_request', array(&$this, 'checkout')); + add_action('parse_request', array(&$this, 'catalog') ); + add_action('wp', array(&$this, 'shortcodes')); + add_action('wp', array(&$this, 'behaviors')); + + // Admin calls + add_action('admin_menu', array(&$this, 'lookups')); + add_action('admin_init', array(&$this, 'tinymce')); + add_action('admin_init', array(&$this->Flow, 'admin')); + add_action('admin_menu', array(&$this, 'add_menus')); + add_filter('favorite_actions', array(&$this, 'favorites')); + add_action('admin_footer', array(&$this, 'footer')); + add_action('wp_dashboard_setup', array(&$this, 'dashboard_init')); + add_action('wp_dashboard_widgets', array(&$this, 'dashboard')); + add_action('admin_print_styles-index.php', array(&$this, 'dashboard_css')); + add_action('save_post', array(&$this, 'pages_index'),10,2); + + // Theme widgets + add_action('widgets_init', array(&$this, 'widgets')); + add_filter('wp_list_pages',array(&$this->Flow,'secure_page_links')); + + add_action('admin_head-options-reading.php',array(&$this,'pages_index')); + add_action('generate_rewrite_rules',array(&$this,'pages_index')); + add_filter('rewrite_rules_array',array(&$this,'rewrites')); + add_filter('query_vars', array(&$this,'queryvars')); + + // Extras & Integrations + add_filter('aioseop_canonical_url', array(&$this,'canonurls')); + + // Start up the cart + $this->Cart = new Cart(); + + } + + function init() { + $pages = $this->Settings->get('pages'); + if (SHOPP_PERMALINKS) { + $this->shopuri = trailingslashit($this->link('catalog')); + if ($this->shopuri == trailingslashit(get_bloginfo('url'))) $this->shopuri .= "{$pages['catalog']['name']}/"; + $this->imguri = trailingslashit($this->shopuri)."images/"; + } else { + $this->shopuri = add_query_arg('page_id',$pages['catalog']['id'],get_bloginfo('url')); + $this->imguri = add_query_arg('shopp_image','=',get_bloginfo('url')); + } + if ($this->secure) { + $this->shopuri = str_replace('http://','https://',$this->shopuri); + $this->imguri = str_replace('http://','https://',$this->imguri); + } + + if (SHOPP_LOOKUP) return true; + + // Initialize the session if not already done + // by another plugin + if(session_id() == "") @session_start(); + + // Setup Error handling + $Errors = &ShoppErrors(); + + $this->ErrorLog = new ShoppErrorLogging($this->Settings->get('error_logging')); + $this->ErrorNotify = new ShoppErrorNotification($this->Settings->get('merchant_email'), + $this->Settings->get('error_notifications')); + + if (!$this->Cart->handlers) new ShoppError(__('The Cart session handlers could not be initialized because the session was started by the active theme or an active plugin before Shopp could establish its session handlers. The cart will not function.','Shopp'),'shopp_cart_handlers',SHOPP_ADMIN_ERR); + if (SHOPP_DEBUG && $this->Cart->handlers) new ShoppError('Session handlers initialized successfully.','shopp_cart_handlers',SHOPP_DEBUG_ERR); + if (SHOPP_DEBUG) new ShoppError('Session started.','shopp_session_debug',SHOPP_DEBUG_ERR); + + // Initialize the catalog and shipping calculators + $this->Catalog = new Catalog(); + $this->ShipCalcs = new ShipCalcs($this->path); + + // Handle WordPress-processed logins + $this->Cart->logins(); + } + + /** + * install() + * Installs the tables and initializes settings */ + function install () { + global $wpdb,$wp_rewrite; + + // If no settings are available, + // no tables exist, so this is a + // new install + if ($this->Settings->unavailable) + include("core/install.php"); + + $ver = $this->Settings->get('version'); + if (!empty($ver) && $ver != SHOPP_VERSION) + $this->Flow->upgrade(); + + if ($this->Settings->get('shopp_setup')) { + $this->Settings->save('maintenance','off'); + $this->Settings->save('shipcalc_lastscan',''); + + // Publish/re-enable Shopp pages + $filter = ""; + $pages = $this->Settings->get('pages'); + foreach ($pages as $page) $filter .= ($filter == "")?"ID={$page['id']}":" OR ID={$page['id']}"; + if ($filter != "") $wpdb->query("UPDATE $wpdb->posts SET post_status='publish' WHERE $filter"); + $this->pages_index(true); + + // Update rewrite rules + $wp_rewrite->flush_rules(); + $wp_rewrite->wp_rewrite_rules(); + + } + + if ($this->Settings->get('show_welcome') == "on") + $this->Settings->save('display_welcome','on'); + } + + /** + * deactivate() + * Resets the data_model to prepare for potential upgrades/changes to the table schema */ + function deactivate() { + global $wpdb,$wp_rewrite; + + // Unpublish/disable Shopp pages + $filter = ""; + $pages = $this->Settings->get('pages'); + if (!is_array($pages)) return true; + foreach ($pages as $page) $filter .= ($filter == "")?"ID={$page['id']}":" OR ID={$page['id']}"; + if ($filter != "") $wpdb->query("UPDATE $wpdb->posts SET post_status='draft' WHERE $filter"); + + // Update rewrite rules + $wp_rewrite->flush_rules(); + $wp_rewrite->wp_rewrite_rules(); + + $this->Settings->save('data_model',''); + + return true; + } + + /** + * add_menus() + * Adds the WordPress admin menus */ + function add_menus () { + $menus = array(); + if (function_exists('add_object_page')) $menus['main'] = add_object_page('Shopp', 'Shopp', SHOPP_USERLEVEL, $this->Flow->Admin->default, array(&$this,'orders'),$this->uri."/core/ui/icons/shopp.png"); + else $menus['main'] = add_menu_page('Shopp', 'Shopp', SHOPP_USERLEVEL, $this->Flow->Admin->default, array(&$this,'orders'),$this->uri."/core/ui/icons/shopp.png"); + + $menus['orders'] = add_submenu_page($this->Flow->Admin->default,__('Orders','Shopp'), __('Orders','Shopp'), SHOPP_USERLEVEL, $this->Flow->Admin->orders, array(&$this,'orders')); + + $menus['customers'] = add_submenu_page($this->Flow->Admin->default,__('Customers','Shopp'), __('Customers','Shopp'), SHOPP_USERLEVEL, $this->Flow->Admin->customers, array(&$this,'customers')); + if (SHOPP_WP27) $customers_parent = $menus['customers']; + else $customers_parent = $this->Flow->Admin->default; + $menus['editcustomer'] = add_submenu_page($customers_parent,__('Edit Customer','Shopp'), false, SHOPP_USERLEVEL, $this->Flow->Admin->editcustomer, array(&$this,'customers')); + + $menus['promotions'] = add_submenu_page($this->Flow->Admin->default,__('Promotions','Shopp'), __('Promotions','Shopp'), SHOPP_USERLEVEL, $this->Flow->Admin->promotions, array(&$this,'promotions')); + if (SHOPP_WP27) $promos_parent = $menus['promotions']; + else $promos_parent = $this->Flow->Admin->default; + $menus['editpromos'] = add_submenu_page($promos_parent,__('Edit Promotion','Shopp'), false, SHOPP_USERLEVEL, $this->Flow->Admin->editpromo, array(&$this,'promotions')); + + $menus['products'] = add_submenu_page($this->Flow->Admin->default,__('Products','Shopp'), __('Products','Shopp'), SHOPP_USERLEVEL, $this->Flow->Admin->products, array(&$this,'products')); + if (SHOPP_WP27) $products_parent = $menus['products']; + else $products_parent = $this->Flow->Admin->default; + $menus['editproducts'] = add_submenu_page($products_parent,__('Product Editor','Shopp'), false, SHOPP_USERLEVEL, $this->Flow->Admin->editproduct, array(&$this,'products')); + + $menus['categories'] = add_submenu_page($this->Flow->Admin->default,__('Categories','Shopp'), __('Categories','Shopp'), SHOPP_USERLEVEL, $this->Flow->Admin->categories, array(&$this,'categories')); + if (SHOPP_WP27) $category_parent = $menus['categories']; + else $category_parent = $this->Flow->Admin->default; + $menus['editcategory'] = add_submenu_page($category_parent,__('Edit Category','Shopp'), false, SHOPP_USERLEVEL, $this->Flow->Admin->editcategory, array(&$this,'categories')); + + $menus['settings'] = add_submenu_page($this->Flow->Admin->default,__('Settings','Shopp'), __('Settings','Shopp'), 8, $this->Flow->Admin->settings['settings'][0], array(&$this,'settings')); + + $settings_screens = array(); + foreach ($this->Flow->Admin->settings as $key => $screen) { + if (SHOPP_WP27) $settings_parent = $menus['settings']; + else $settings_parent = $this->Flow->Admin->default; + $settings_screens[$key] = add_submenu_page($settings_parent,$screen[1],false, 8, $screen[0], array(&$this,'settings')); + // echo $settings_screens[$key].BR; + } + + if (function_exists('add_contextual_help')) { + foreach ($menus as $menu => $page) $this->Flow->helpdoc($menu,$page); + foreach ($settings_screens as $menu => $page) $this->Flow->helpdoc($menu,$page); + } else $menus['help'] = add_submenu_page($this->Flow->Admin->default,__('Help','Shopp'), __('Help','Shopp'), SHOPP_USERLEVEL, $this->Flow->Admin->help, array(&$this,'help')); + + // $welcome = add_submenu_page($this->Flow->Admin->default,__('Welcome','Shopp'), __('Welcome','Shopp'), SHOPP_USERLEVEL, $this->Flow->Admin->welcome, array(&$this,'welcome')); + + // add_action("admin_head-$editproduct", array(&$this, 'admin_behaviors')); + + foreach($menus as $name => $menu) + add_action("admin_print_scripts-$menu", array(&$this, 'admin_behaviors')); + + foreach ($settings_screens as $settings_screen) + add_action("admin_print_scripts-$settings_screen", array(&$this, 'admin_behaviors')); + + add_action("admin_print_scripts-{$menus['orders']}", array(&$this->Flow, 'orders_list_columns')); + add_action("admin_print_scripts-{$menus['customers']}", array(&$this->Flow, 'customers_list_columns')); + add_action("admin_print_scripts-{$menus['promotions']}", array(&$this->Flow, 'promotions_list_columns')); + add_action("admin_print_scripts-{$menus['products']}", array(&$this->Flow, 'products_list_columns')); + add_action("admin_print_scripts-{$menus['categories']}", array(&$this->Flow, 'categories_list_columns')); + + if (SHOPP_WP27) { + add_action("admin_head-{$menus['editproducts']}", array(&$this->Flow, 'product_editor_ui')); + add_action("admin_head-{$menus['editcustomer']}", array(&$this->Flow, 'customer_editor_ui')); + add_action("admin_head-{$menus['editcategory']}", array(&$this->Flow, 'category_editor_ui')); + add_action("admin_head-{$menus['editpromos']}", array(&$this->Flow, 'promotion_editor_ui')); + } + + } + + function favorites ($actions) { + $key = add_query_arg(array('page'=>$this->Flow->Admin->editproduct,'id'=>'new'),$this->wpadminurl); + $actions[$key] = array(__('New Shopp Product','Shopp'),8); + return $actions; + } + + /** + * admin_behaviors() + * Dynamically includes necessary JavaScript and stylesheets for the admin */ + function admin_behaviors () { + global $wp_version; + wp_enqueue_script('jquery'); + wp_enqueue_script('shopp',"{$this->uri}/core/ui/behaviors/shopp.js",array('jquery'),SHOPP_VERSION,true); + wp_enqueue_script('shopp-settings',add_query_arg('shopp_lookup','settings.js',get_bloginfo('url')),array(),SHOPP_VERSION); + + // Load only for the product editor to keep other admin screens snappy + if (($_GET['page'] == $this->Flow->Admin->editproduct || + $_GET['page'] == $this->Flow->Admin->editcustomer || + $_GET['page'] == $this->Flow->Admin->editcategory || + $_GET['page'] == $this->Flow->Admin->editpromo)) { + if (SHOPP_WP27) { + add_action( 'admin_head', 'wp_tiny_mce' ); + wp_enqueue_script('postbox'); + if ( user_can_richedit() ) + wp_enqueue_script('editor'); + } + + wp_enqueue_script("shopp-thickbox","{$this->uri}/core/ui/behaviors/thickbox.js",array('jquery'),SHOPP_VERSION); + wp_enqueue_script('shopp.editor.lib',"{$this->uri}/core/ui/behaviors/editors.js",array('jquery'),SHOPP_VERSION,true); + + if ($_GET['page'] == $this->Flow->Admin->editproduct) + wp_enqueue_script('shopp.product.editor',"{$this->uri}/core/ui/products/editor.js",array('jquery'),SHOPP_VERSION,true); + + if (SHOPP_WP27) wp_enqueue_script('shopp.editor.priceline',"{$this->uri}/core/ui/behaviors/priceline.js",array('jquery'),SHOPP_VERSION,true); + else wp_enqueue_script('shopp.editor.priceline',"{$this->uri}/core/ui/behaviors/priceline-wp26.js",array('jquery'),SHOPP_VERSION,true); + + wp_enqueue_script('shopp.ocupload',"{$this->uri}/core/ui/behaviors/ocupload.js",array('jquery'),SHOPP_VERSION,true); + wp_enqueue_script('jquery-ui-sortable', '/wp-includes/js/jquery/ui.sortable.js', array('jquery','jquery-ui-core'),SHOPP_VERSION,true); + + wp_enqueue_script('shopp.swfupload',"{$this->uri}/core/ui/behaviors/swfupload/swfupload.js",array(),SHOPP_VERSION); + wp_enqueue_script('shopp.swfupload.swfobject',"{$this->uri}/core/ui/behaviors/swfupload/plugins/swfupload.swfobject.js",array('shopp.swfupload'),SHOPP_VERSION); + } + + ?> + + + +Flow,'dashboard_stats'), + array('all_link' => '','feed_link' => '','width' => 'half','height' => 'single') + ); + + wp_register_sidebar_widget('dashboard_shopp_orders', __('Shopp Orders','Shopp'), array(&$this->Flow,'dashboard_orders'), + array('all_link' => 'admin.php?page='.$this->Flow->Admin->orders,'feed_link' => '','width' => 'half','height' => 'single') + ); + + wp_register_sidebar_widget('dashboard_shopp_products', __('Shopp Products','Shopp'), array(&$this->Flow,'dashboard_products'), + array('all_link' => 'admin.php?page='.$this->Flow->Admin->products,'feed_link' => '','width' => 'half','height' => 'single') + ); + + } + + /** + * dashboard () + * Adds the Shopp dashboard widgets to the WordPress Dashboard */ + function dashboard ($widgets) { + $dashboard = $this->Settings->get('dashboard'); + if (current_user_can(SHOPP_USERLEVEL) && $dashboard == "on") + array_unshift($widgets,'dashboard_shopp_stats','dashboard_shopp_orders','dashboard_shopp_products'); + return $widgets; + } + + /** + * behaviors() + * Dynamically includes necessary JavaScript and stylesheets as needed in + * public shopping pages handled by Shopp */ + function behaviors () { + global $wp_query; + $object = $wp_query->get_queried_object(); + + if(isset($_SERVER['HTTPS']) && $_SERVER["HTTPS"] == "on") { + add_filter('option_siteurl', 'force_ssl'); + add_filter('option_home', 'force_ssl'); + add_filter('option_url', 'force_ssl'); + add_filter('option_wpurl', 'force_ssl'); + add_filter('option_stylesheet_url', 'force_ssl'); + add_filter('option_template_url', 'force_ssl'); + add_filter('script_loader_src', 'force_ssl'); + } + + // Determine which tag is getting used in the current post/page + $tag = false; + $tagregexp = join( '|', array_keys($this->shortcodes) ); + foreach ($wp_query->posts as $post) { + if (preg_match('/\[('.$tagregexp.')\b(.*?)(?:(\/))?\](?:(.+?)\[\/\1\])?/',$post->post_content,$matches)) + $tag = $matches[1]; + } + + // Include stylesheets and javascript based on whether shopp shortcodes are used + add_action('wp_head', array(&$this, 'header')); + add_action('wp_footer', array(&$this, 'footer')); + + $loading = $this->Settings->get('script_loading'); + if (!$loading || $loading == "global" || $tag !== false) { + wp_enqueue_script('jquery'); + wp_enqueue_script('shopp-settings',add_query_arg('shopp_lookup','settings.js',get_bloginfo('url'))); + wp_enqueue_script("shopp-thickbox","{$this->uri}/core/ui/behaviors/thickbox.js",array('jquery'),SHOPP_VERSION,true); + wp_enqueue_script("shopp","{$this->uri}/core/ui/behaviors/shopp.js",array('jquery'),SHOPP_VERSION,true); + } + + if ($tag == "checkout") + wp_enqueue_script('shopp_checkout',"{$this->uri}/core/ui/behaviors/checkout.js",array('jquery'),SHOPP_VERSION,true); + + + } + + /** + * widgets() + * Initializes theme widgets */ + function widgets () { + global $wp_version; + + include('core/ui/widgets/cart.php'); + include('core/ui/widgets/categories.php'); + include('core/ui/widgets/section.php'); + include('core/ui/widgets/tagcloud.php'); + include('core/ui/widgets/facetedmenu.php'); + include('core/ui/widgets/product.php'); + + if (version_compare($wp_version,'2.8-dev','<')) { + $ShoppCategories = new LegacyShoppCategoriesWidget(); + $ShoppSection = new LegacyShoppCategorySectionWidget(); + $ShoppTagCloud = new LegacyShoppTagCloudWidget(); + $ShoppFacetedMenu = new LegacyShoppFacetedMenuWidget(); + $ShoppCart = new LegacyShoppCartWidget(); + $ShoppProduct = new LegacyShoppProductWidget(); + } + + } + + /** + * shortcodes() + * Handles shortcodes used on Shopp-installed pages and used by + * site owner for including categories/products in posts and pages */ + function shortcodes () { + + $this->shortcodes = array(); + $this->shortcodes['catalog'] = array(&$this->Flow,'catalog'); + $this->shortcodes['cart'] = array(&$this->Flow,'cart'); + $this->shortcodes['checkout'] = array(&$this->Flow,'checkout'); + $this->shortcodes['account'] = array(&$this->Flow,'account'); + $this->shortcodes['product'] = array(&$this->Flow,'product_shortcode'); + $this->shortcodes['category'] = array(&$this->Flow,'category_shortcode'); + + foreach ($this->shortcodes as $name => &$callback) + if ($this->Settings->get("maintenance") == "on") + add_shortcode($name,array(&$this->Flow,'maintenance_shortcode')); + else add_shortcode($name,$callback); + } + + function tinymce () { + if (!current_user_can('edit_posts') && !current_user_can('edit_pages')) return; + + // Add TinyMCE buttons when using rich editor + if (get_user_option('rich_editing') == 'true') { + add_filter('tiny_mce_version', array(&$this,'mceupdate')); // Move to plugin activation + add_filter('mce_external_plugins', array(&$this,'mceplugin'),5); + add_filter('mce_buttons', array(&$this,'mcebutton'),5); + } + } + + function mceplugin ($plugins) { + $plugins['Shopp'] = $this->uri.'/core/ui/behaviors/tinymce/editor_plugin.js'; + return $plugins; + } + + function mcebutton ($buttons) { + array_push($buttons, "separator", "Shopp"); + return $buttons; + } + + function my_change_mce_settings( $init_array ) { + $init_array['disk_cache'] = false; // disable caching + $init_array['compress'] = false; // disable gzip compression + $init_array['old_cache_max'] = 3; // keep 3 different TinyMCE configurations cached (when switching between several configurations regularly) + } + + function mceupdate($ver) { + return ++$ver; + } + + + /** + * pages_index() + * Handles changes to Shopp-installed pages that may affect 'pretty' urls */ + function pages_index ($update=false,$updates=false) { + global $wpdb; + $pages = $this->Settings->get('pages'); + + // No pages setting, use defaults + $pages = $this->Flow->Pages; + + // Find pages with Shopp-related main shortcodes + $codes = array(); + $search = ""; + foreach ($pages as $page) $codes[] = $page['content']; + foreach ($codes as $code) $search .= ((!empty($search))?" OR ":"")."post_content LIKE '%$code%'"; + $query = "SELECT ID,post_title,post_name,post_content FROM $wpdb->posts WHERE post_status='publish' AND ($search)"; + $results = $wpdb->get_results($query); + + // Match updates from the found results to our pages index + foreach ($pages as $key => &$page) { + foreach ($results as $index => $post) { + if (strpos($post->post_content,$page['content']) !== false) { + $page['id'] = $post->ID; + $page['title'] = $post->post_title; + $page['name'] = $post->post_name; + $page['permalink'] = str_replace(trailingslashit(get_bloginfo('url')),'',get_permalink($page['id'])); + if ($page['permalink'] == get_bloginfo('url')) $page['permalink'] = ""; + break; + } + } + } + + $this->Settings->save('pages',$pages); + + if ($update) return $update; + } + + /** + * rewrites() + * Adds Shopp-specific pretty-url rewrite rules to the WordPress rewrite rules */ + function rewrites ($wp_rewrite_rules) { + $this->pages_index(true); + $pages = $this->Settings->get('pages'); + if (!$pages) $pages = $this->Flow->Pages; + $shop = $pages['catalog']['permalink']; + if (!empty($shop)) $shop = trailingslashit($shop); + $catalog = $pages['catalog']['name']; + $cart = $pages['cart']['permalink']; + $checkout = $pages['checkout']['permalink']; + $account = $pages['account']['permalink']; + + $rules = array( + $cart.'?$' => 'index.php?pagename='.shopp_pagename($cart), + $account.'?$' => 'index.php?pagename='.shopp_pagename($account), + $checkout.'?$' => 'index.php?pagename='.shopp_pagename($checkout).'&shopp_proc=checkout', + (empty($shop)?"$catalog/":$shop).'feed/?$' => 'index.php?shopp_lookup=newproducts-rss', + (empty($shop)?"$catalog/":$shop).'receipt/?$' => 'index.php?pagename='.shopp_pagename($checkout).'&shopp_proc=receipt', + (empty($shop)?"$catalog/":$shop).'confirm-order/?$' => 'index.php?pagename='.shopp_pagename($checkout).'&shopp_proc=confirm-order', + (empty($shop)?"$catalog/":$shop).'download/([a-z0-9]{40})/?$' => 'index.php?pagename='.shopp_pagename($account).'&shopp_download=$matches[1]', + (empty($shop)?"$catalog/":$shop).'images/(\d+)/?.*?$' => 'index.php?shopp_image=$matches[1]' + ); + + // catalog/category/category-slug + if (empty($shop)) { + $rules[$catalog.'/category/(.+?)/feed/?$'] = 'index.php?shopp_lookup=category-rss&shopp_category=$matches[1]'; + $rules[$catalog.'/category/(.+?)/page/?([A-Z0-9]{1,})/?$'] = 'index.php?pagename='.shopp_pagename($catalog).'&shopp_category=$matches[1]&paged=$matches[2]'; + $rules[$catalog.'/category/(.+)/?$'] = 'index.php?pagename='.shopp_pagename($catalog).'&shopp_category=$matches[1]'; + } else { + $rules[$shop.'category/(.+?)/feed/?$'] = 'index.php?shopp_lookup=category-rss&shopp_category=$matches[1]'; + $rules[$shop.'category/(.+?)/page/?([A-Z0-9]{1,})/?$'] = 'index.php?pagename='.shopp_pagename($shop).'&shopp_category=$matches[1]&paged=$matches[2]'; + $rules[$shop.'category/(.+)/?$'] = 'index.php?pagename='.shopp_pagename($shop).'&shopp_category=$matches[1]'; + } + + // tags + if (empty($shop)) { + $rules[$catalog.'/tag/(.+?)/feed/?$'] = 'index.php?shopp_lookup=category-rss&shopp_tag=$matches[1]'; + $rules[$catalog.'/tag/(.+?)/page/?([0-9]{1,})/?$'] = 'index.php?pagename='.shopp_pagename($catalog).'&shopp_tag=$matches[1]&paged=$matches[2]'; + $rules[$catalog.'/tag/(.+)/?$'] = 'index.php?pagename='.shopp_pagename($catalog).'&shopp_tag=$matches[1]'; + } else { + $rules[$shop.'tag/(.+?)/feed/?$'] = 'index.php?shopp_lookup=category-rss&shopp_tag=$matches[1]'; + $rules[$shop.'tag/(.+?)/page/?([0-9]{1,})/?$'] = 'index.php?pagename='.shopp_pagename($shop).'&shopp_tag=$matches[1]&paged=$matches[2]'; + $rules[$shop.'tag/(.+)/?$'] = 'index.php?pagename='.shopp_pagename($shop).'&shopp_tag=$matches[1]'; + } + + // catalog/productid + if (empty($shop)) $rules[$catalog.'/(\d+(,\d+)?)/?$'] = 'index.php?pagename='.shopp_pagename($catalog).'&shopp_pid=$matches[1]'; + else $rules[$shop.'(\d+(,\d+)?)/?$'] = 'index.php?pagename='.shopp_pagename($shop).'&shopp_pid=$matches[1]'; + + // catalog/product-slug + if (empty($shop)) $rules[$catalog.'/(.+)/?$'] = 'index.php?pagename='.shopp_pagename($catalog).'&shopp_product=$matches[1]'; // category/product-slug + else $rules[$shop.'(.+)/?$'] = 'index.php?pagename='.shopp_pagename($shop).'&shopp_product=$matches[1]'; // category/product-slug + + // catalog/categories/path/product-slug + if (empty($shop)) $rules[$catalog.'/([\w%_\\+-\/]+?)/([\w_\-]+?)/?$'] = 'index.php?pagename='.shopp_pagename($catalog).'&shopp_category=$matches[1]&shopp_product=$matches[2]'; // category/product-slug + else $rules[$shop.'([\w%_\+\-\/]+?)/([\w_\-]+?)/?$'] = 'index.php?pagename='.shopp_pagename($shop).'&shopp_category=$matches[1]&shopp_product=$matches[2]'; // category/product-slug + + return $rules + $wp_rewrite_rules; + } + + /** + * queryvars() + * Registers the query variables used by Shopp */ + function queryvars ($vars) { + $vars[] = 'shopp_proc'; + $vars[] = 'shopp_category'; + $vars[] = 'shopp_tag'; + $vars[] = 'shopp_pid'; + $vars[] = 'shopp_product'; + $vars[] = 'shopp_lookup'; + $vars[] = 'shopp_image'; + $vars[] = 'shopp_download'; + $vars[] = 'shopp_xco'; + $vars[] = 'st'; + + return $vars; + } + + /** + * orders() + * Handles order administration screens */ + function orders () { + if ($this->Settings->get('display_welcome') == "on") { + $this->welcome(); return; + } + if (!empty($_GET['id'])) $this->Flow->order_manager(); + else $this->Flow->orders_list(); + } + + /** + * customers() + * Handles order administration screens */ + function customers () { + if ($this->Settings->get('display_welcome') == "on") { + $this->welcome(); return; + } + + if ($_GET['page'] == $this->Flow->Admin->editcustomer) + $this->Flow->customer_editor(); + else $this->Flow->customers_list(); + } + + /** + * categories() + * Handles category administration screens */ + function categories () { + if ($this->Settings->get('display_welcome') == "on") { + $this->welcome(); return; + } + if ($_GET['page'] == $this->Flow->Admin->editcategory) + $this->Flow->category_editor(); + else $this->Flow->categories_list(); + } + + /** + * products() + * Handles product administration screens */ + function products () { + if ($this->Settings->get('display_welcome') == "on") { + $this->welcome(); return; + } + + if ($_GET['page'] == $this->Flow->Admin->editproduct) + $this->Flow->product_editor(); + else $this->Flow->products_list(); + + } + + /** + * promotions() + * Handles product administration screens */ + function promotions () { + if ($this->Settings->get('display_welcome') == "on") { + $this->welcome(); return; + } + if ($_GET['page'] == $this->Flow->Admin->editpromo) + $this->Flow->promotion_editor(); + else $this->Flow->promotions_list(); + } + + /** + * settings() + * Handles settings administration screens */ + function settings () { + if ($this->Settings->get('display_welcome') == "on" && empty($_POST['setup'])) { + $this->welcome(); return; + } + + $pages = explode("-",$_GET['page']); + $screen = end($pages); + switch($screen) { + case "catalog": $this->Flow->settings_catalog(); break; + case "cart": $this->Flow->settings_cart(); break; + case "checkout": $this->Flow->settings_checkout(); break; + case "payments": $this->Flow->settings_payments(); break; + case "shipping": $this->Flow->settings_shipping(); break; + case "taxes": $this->Flow->settings_taxes(); break; + case "presentation": $this->Flow->settings_presentation(); break; + case "system": $this->Flow->settings_system(); break; + case "update": $this->Flow->settings_update(); break; + default: $this->Flow->settings_general(); + } + + } + + /** + * titles () + * Changes the Shopp catalog page titles to include the product + * name and category (when available) */ + function titles ($title,$sep=" — ",$placement="left") { + if (empty($this->Product->name) && empty($this->Category->name)) return $title; + if ($placement == "right") { + if (!empty($this->Product->name)) $title = $this->Product->name." $sep ".$title; + if (!empty($this->Category->name)) $title = $this->Category->name." $sep ".$title; + } else { + if (!empty($this->Category->name)) $title .= " $sep ".$this->Category->name; + if (!empty($this->Product->name)) $title .= " $sep ".$this->Product->name; + } + return $title; + } + + function feeds () { + if (empty($this->Category)):?> + + +Category->uri; + if ($this->Category->slug == "tag") $uri = $this->Category->slug.'/'.$this->Category->tag; + + if (SHOPP_PERMALINKS) $link = $this->shopuri.urldecode($uri).'/feed/'; + else $link = add_query_arg(array('shopp_category'=>urldecode($this->Category->uri),'shopp_lookup'=>'category-rss'),$this->shopuri); + ?> + + +query_vars['s'] = $this->Cart->data->Search; + } + + function metadata () { + $keywords = false; + $description = false; + if (!empty($this->Product)) { + if (empty($this->Product->tags)) $this->Product->load_data(array('tags')); + foreach($this->Product->tags as $tag) + $keywords .= (!empty($keywords))?", {$tag->name}":$tag->name; + $description = $this->Product->summary; + } elseif (!empty($this->Category)) { + $description = $this->Category->description; + } + $keywords = attribute_escape(apply_filters('shopp_meta_keywords',$keywords)); + $description = attribute_escape(apply_filters('shopp_meta_description',$description)); + ?> + + Product->slug)) return $Shopp->Product->tag('url','echo=0'); + if (!empty($Shopp->Category->slug)) return $Shopp->Category->tag('url','echo=0'); + return $url; + } + + /** + * header() + * Adds stylesheets necessary for Shopp public shopping pages */ + function header () { +?> +'catalog.css','ver'=>urlencode(SHOPP_VERSION)),get_bloginfo('url'))); ?>' type='text/css' /> + + +canonurls(false); + if (is_shopp_page('catalog') && !empty($canonurl)): ?>_debug->memory .= "End: ".number_format(memory_get_peak_usage(true)/1024/1024, 2, '.', ',') . " MB
"; + elseif (function_exists('memory_get_usage')) + $this->_debug->memory .= "End: ".number_format(memory_get_usage(true)/1024/1024, 2, '.', ',') . " MB"; + + echo ''."\n"; + + } + + function catalog ($wp) { + $pages = $this->Settings->get('pages'); + $options = array(); + + $type = "catalog"; + if (isset($wp->query_vars['shopp_category']) && + $category = $wp->query_vars['shopp_category']) $type = "category"; + if (isset($wp->query_vars['shopp_pid']) && + $productid = $wp->query_vars['shopp_pid']) $type = "product"; + if (isset($wp->query_vars['shopp_product']) && + $productname = $wp->query_vars['shopp_product']) $type = "product"; + + if (isset($wp->query_vars['shopp_tag']) && + $tag = $wp->query_vars['shopp_tag']) { + $type = "category"; + $category = "tag"; + } + + $referer = wp_get_referer(); + $target = "blog"; + if (isset($wp->query_vars['st'])) $target = $wp->query_vars['st']; + if (!empty($wp->query_vars['s']) && // Search query is present and... + // The search target is set to shopp + ($target == "shopp" + // The referering page includes a Shopp catalog page path + || strpos($referer,$this->link('catalog')) !== false || + strpos($referer,'page_id='.$pages['catalog']['id']) !== false || + // Or the referer was a search that matches the last recorded Shopp search + substr($referer,-1*(strlen($this->Cart->data->Search))) == $this->Cart->data->Search || + // Or the blog URL matches the Shopp catalog URL (Takes over search for store-only search) + trailingslashit(get_bloginfo('url')) == $this->link('catalog') || + // Or the referer is one of the Shopp cart, checkout or account pages + $referer == $this->link('cart') || $referer == $this->link('checkout') || + $referer == $this->link('account'))) { + $this->Cart->data->Search = $wp->query_vars['s']; + $wp->query_vars['s'] = ""; + $wp->query_vars['pagename'] = $pages['catalog']['name']; + add_action('wp_head', array(&$this, 'updatesearch')); + if ($type != "product") $type = "category"; + $category = "search-results"; + } + + // Load a category/tag + if (!empty($category) || !empty($tag)) { + if (isset($this->Cart->data->Search)) $options = array('search'=>$this->Cart->data->Search); + if (isset($tag)) $options = array('tag'=>$tag); + + // Split for encoding multi-byte slugs + $slugs = explode("/",$category); + $category = join("/",array_map('urlencode',$slugs)); + + // Load the category + $this->Category = Catalog::load_category($category,$options); + $this->Cart->data->breadcrumb = (isset($tag)?"tag/":"").$this->Category->uri; + } + + if (empty($category) && empty($tag) && + empty($productid) && empty($productname)) + $this->Cart->data->breadcrumb = ""; + + // Category Filters + if (!empty($this->Category->slug)) { + if (empty($this->Cart->data->Category[$this->Category->slug])) + $this->Cart->data->Category[$this->Category->slug] = array(); + $CategoryFilters =& $this->Cart->data->Category[$this->Category->slug]; + + // Add new filters + if (isset($_GET['shopp_catfilters'])) { + if (is_array($_GET['shopp_catfilters'])) { + $CategoryFilters = array_filter(array_merge($CategoryFilters,$_GET['shopp_catfilters'])); + $CategoryFilters = stripslashes_deep($CategoryFilters); + if (isset($wp->query_vars['paged'])) $wp->query_vars['paged'] = 1; // Force back to page 1 + } else unset($this->Cart->data->Category[$this->Category->slug]); + } + + } + + // Catalog sort order setting + if (isset($_GET['shopp_orderby'])) + $this->Cart->data->Category['orderby'] = $_GET['shopp_orderby']; + + if (empty($this->Category)) $this->Category = Catalog::load_category($this->Cart->data->breadcrumb,$options); + + // Find product by given ID + if (!empty($productid) && empty($this->Product->id)) + $this->Product = new Product($productid); + + // Find product by product slug + if (!empty($productname) && empty($this->Product->id)) + $this->Product = new Product(urlencode($productname),"slug"); + + // Product must be published + if (!empty($this->Product->id) && $this->Product->published == "off" || empty($this->Product->id)) + $this->Product = false; + + // No product found, try to load a page instead + if ($type == "product" && !$this->Product) + $wp->query_vars['pagename'] = $wp->request; + + $this->Catalog = new Catalog($type); + add_filter('wp_title', array(&$this, 'titles'),10,3); + add_action('wp_head', array(&$this, 'metadata')); + add_action('wp_head', array(&$this, 'feeds')); + + } + + /** + * cart() + * Handles shopping cart requests */ + function cart () { + if (isset($_REQUEST['shopping']) && $_REQUEST['shopping'] == "reset") { + $this->Cart->reset(); + shopp_redirect($this->link()); + } + + if (empty($_REQUEST['cart'])) return true; + + $this->Cart->request(); + if ($this->Cart->updated) $this->Cart->totals(); + if (isset($_REQUEST['ajax'])) $this->Cart->ajax(); + $redirect = false; + if (isset($_REQUEST['redirect'])) $redirect = $_REQUEST['redirect']; + switch ($redirect) { + case "checkout": shopp_redirect($this->link($redirect,true)); break; + default: + if (!empty($_REQUEST['redirect'])) + shopp_redirect(esc_url($this->link($_REQUEST['redirect']))); + else shopp_redirect($this->link('cart')); + } + } + + /** + * checkout() + * Handles checkout process */ + function checkout ($wp) { + + $pages = $this->Settings->get('pages'); + // If checkout page requested + // Note: we have to use custom detection here as + // the wp->post vars are not available at this point + // to make use of is_shopp_page() + if (((SHOPP_PERMALINKS && isset($wp->query_vars['pagename']) + && $wp->query_vars['pagename'] == $pages['checkout']['permalink']) + || (isset($wp->query_vars['page_id']) && $wp->query_vars['page_id'] == $pages['checkout']['id'])) + && $wp->query_vars['shopp_proc'] == "checkout") { + + $this->Cart->updated(); + $this->Cart->totals(); + + if ($this->Cart->data->ShippingPostcodeError) { + header('Location: '.$this->link('cart')); + exit(); + } + + // Force secure checkout page if its not already + $secure = true; + $gateway = $this->Settings->get('payment_gateway'); + if (strpos($gateway,"TestMode") !== false + || isset($wp->query_vars['shopp_xco']) + || $this->Cart->orderisfree()) + $secure = false; + + if ($secure && !$this->secure && !SHOPP_NOSSL) { + header('Location: '.$this->link('checkout',$secure)); + exit(); + } + } + + // Cancel this process if there is no order data + if (!isset($this->Cart->data->Order)) return; + $Order = $this->Cart->data->Order; + + // Intercept external checkout processing + if (!empty($wp->query_vars['shopp_xco'])) { + if ($this->gateway($wp->query_vars['shopp_xco'])) { + if ($wp->query_vars['shopp_proc'] != "confirm-order" && + !isset($_POST['checkout'])) { + $this->Gateway->checkout(); + $this->Gateway->error(); + } + } + } + + // Cancel if no checkout process detected + if (empty($_POST['checkout'])) return true; + // Handoff to order processing + if ($_POST['checkout'] == "confirmed") return $this->Flow->order(); + // Cancel if checkout process is not ready for processing + if ($_POST['checkout'] != "process") return true; + // Cancel if processing a login from the checkout form + if (isset($_POST['process-login']) + && $_POST['process-login'] == "true") return true; + + // Start processing the checkout form + $_POST = attribute_escape_deep($_POST); + + $_POST['billing']['cardexpires'] = sprintf("%02d%02d",$_POST['billing']['cardexpires-mm'],$_POST['billing']['cardexpires-yy']); + + // If the card number is provided over a secure connection + // Change the cart to operate in secure mode + if (isset($_POST['billing']['card']) && is_shopp_secure()) + $this->Cart->secured(true); + + // Sanitize the card number to ensure it only contains numbers + $_POST['billing']['card'] = preg_replace('/[^\d]/','',$_POST['billing']['card']); + + if (isset($_POST['data'])) $Order->data = stripslashes_deep($_POST['data']); + if (empty($Order->Customer)) + $Order->Customer = new Customer(); + $Order->Customer->updates($_POST); + + if (isset($_POST['confirm-password'])) + $Order->Customer->confirm_password = $_POST['confirm-password']; + + if (empty($Order->Billing)) + $Order->Billing = new Billing(); + $Order->Billing->updates($_POST['billing']); + + if (!empty($_POST['billing']['cardexpires-mm']) && !empty($_POST['billing']['cardexpires-yy'])) { + $Order->Billing->cardexpires = mktime(0,0,0, + $_POST['billing']['cardexpires-mm'],1, + ($_POST['billing']['cardexpires-yy'])+2000 + ); + } else $Order->Billing->cardexpires = 0; + + $Order->Billing->cvv = preg_replace('/[^\d]/','',$_POST['billing']['cvv']); + + if (empty($Order->Shipping)) + $Order->Shipping = new Shipping(); + + if (isset($_POST['shipping'])) $Order->Shipping->updates($_POST['shipping']); + if (!empty($_POST['shipmethod'])) $Order->Shipping->method = $_POST['shipmethod']; + else $Order->Shipping->method = key($this->Cart->data->ShipCosts); + + // Override posted shipping updates with billing address + if ($_POST['sameshipaddress'] == "on") + $Order->Shipping->updates($Order->Billing, + array("_datatypes","_table","_key","_lists","id","created","modified")); + + $estimatedTotal = $this->Cart->data->Totals->total; + $this->Cart->updated(); + $this->Cart->totals(); + if ($this->Cart->validate() !== true) return; + else $Order->Customer->updates($_POST); // Catch changes from validation + + // If the cart's total changes at all, confirm the order + if ($estimatedTotal != $this->Cart->data->Totals->total || + $this->Settings->get('order_confirmation') == "always") { + $gateway = $this->Settings->get('payment_gateway'); + $secure = true; + if (strpos($gateway,"TestMode") !== false + || isset($wp->query_vars['shopp_xco']) + || $this->Cart->orderisfree()) + $secure = false; + shopp_redirect($this->link('confirm-order',$secure)); + } else $this->Flow->order(); + + } + + /** + * xorder () + * Handle external checkout system order notifications */ + function xorder () { + $path = false; + if (!empty($_GET['shopp_xorder'])) { + $gateway = $this->Settings->get($_GET['shopp_xorder']); + if (isset($gateway['path'])) $path = $gateway['path']; + // Use the old path support for transition if the new path setting isn't available + if (empty($path)) $path = "{$_GET['shopp_xorder']}/{$_GET['shopp_xorder']}.php"; + if ($this->gateway($path)) $this->Gateway->process(); + exit(); + } + } + + /** + * gateway () + * Loads a requested gateway */ + function gateway ($gateway,$load=false) { + if (substr($gateway,-4) != ".php") $gateway .= ".php"; + $filepath = join(DIRECTORY_SEPARATOR,array($this->path,'gateways',$gateway)); + if (!file_exists($filepath)) { + new ShoppError(__('There was a problem loading the requested payment processor.','Shopp').' ('.$gateway.')','shopp_load_gateway'); + return false; + } + $meta = $this->Flow->scan_gateway_meta($filepath); + $ProcessorClass = $meta->tags['class']; + include_once($filepath); + + if (isset($this->Cart->data->Order) && !$load) $this->Gateway = new $ProcessorClass($this->Cart->data->Order); + else $this->Gateway = new $ProcessorClass(); + + return true; + } + + /** + * link () + * Builds a full URL for a specific Shopp-related resource */ + function link ($target,$secure=false) { + $internals = array("receipt","confirm-order"); + $pages = $this->Settings->get('pages'); + + if (!is_array($pages)) $pages = $this->Flow->Pages; + + $uri = get_bloginfo('url'); + if ($secure && !SHOPP_NOSSL) $uri = str_replace('http://','https://',$uri); + + if (array_key_exists($target,$pages)) $page = $pages[$target]; + else { + if (in_array($target,$internals)) { + $page = $pages['checkout']; + if (SHOPP_PERMALINKS) { + $catalog = $pages['catalog']['permalink']; + if (empty($catalog)) $catalog = $pages['catalog']['name']; + $page['permalink'] = trailingslashit($catalog).$target; + } else $page['id'] .= "&shopp_proc=$target"; + } else $page = $pages['catalog']; + } + + if (SHOPP_PERMALINKS) return $uri."/".$page['permalink']; + else return add_query_arg('page_id',$page['id'],trailingslashit($uri)); + } + + /** + * help() + * This function provides graceful degradation when the + * contextual javascript behavior isn't working, this + * provides the default behavior of showing a help gateway + * page with instructions on where to find help on Shopp. */ + function help () { + include(SHOPP_ADMINPATH."/help/help.php"); + } + + function welcome () { + include(SHOPP_ADMINPATH."/help/welcome.php"); + } + + /** + * AJAX Responses */ + + /** + * lookups () + * Provides fast db lookups with as little overhead as possible */ + function lookups($wp) { + $db =& DB::get(); + + // Grab query requests from permalink rewriting query vars + $admin = false; + $download = (isset($wp->query_vars['shopp_download']))?$wp->query_vars['shopp_download']:''; + $lookup = (isset($wp->query_vars['shopp_lookup']))?$wp->query_vars['shopp_lookup']:''; + + // Admin Lookups + if (isset($_GET['page']) && $_GET['page'] == "shopp-lookup") { + $admin = true; + $image = $_GET['id']; + $download = $_GET['download']; + } + + if (!empty($download)) $lookup = "download"; + if (empty($lookup)) $lookup = (isset($_GET['lookup']))?$_GET['lookup']:''; + + switch($lookup) { + case "purchaselog": + if (!defined('WP_ADMIN') || !is_user_logged_in() || !current_user_can('manage_options')) die('-1'); + $db =& DB::get(); + + if (!isset($_POST['settings']['purchaselog_columns'])) { + $_POST['settings']['purchaselog_columns'] = + array_keys(array_merge($Purchase,$Purchased)); + $_POST['settings']['purchaselog_headers'] = "on"; + } + + $this->Flow->settings_save(); + + $format = $this->Settings->get('purchaselog_format'); + if (empty($format)) $format = 'tab'; + + switch ($format) { + case "csv": new PurchasesCSVExport(); break; + case "xls": new PurchasesXLSExport(); break; + case "iif": new PurchasesIIFExport(); break; + default: new PurchasesTabExport(); + } + exit(); + break; + case "customerexport": + if (!defined('WP_ADMIN') || !is_user_logged_in() || !current_user_can('manage_options')) die('-1'); + $db =& DB::get(); + + if (!isset($_POST['settings']['customerexport_columns'])) { + $Customer = Customer::exportcolumns(); + $Billing = Billing::exportcolumns(); + $Shipping = Shipping::exportcolumns(); + $_POST['settings']['customerexport_columns'] = + array_keys(array_merge($Customer,$Billing,$Shipping)); + $_POST['settings']['customerexport_headers'] = "on"; + } + + $this->Flow->settings_save(); + + $format = $this->Settings->get('customerexport_format'); + if (empty($format)) $format = 'tab'; + + switch ($format) { + case "csv": new CustomersCSVExport(); break; + case "xls": new CustomersXLSExport(); break; + default: new CustomersTabExport(); + } + exit(); + break; + case "receipt": + if (!defined('WP_ADMIN') || !is_user_logged_in() || !current_user_can('manage_options')) die('-1'); + if (preg_match("/\d+/",$_GET['id'])) { + $this->Cart->data->Purchase = new Purchase($_GET['id']); + $this->Cart->data->Purchase->load_purchased(); + } else die('-1'); + echo ""; + echo ''; + echo ""; + echo ""; + echo $this->Flow->order_receipt(); + if (isset($_GET['print']) && $_GET['print'] == 'auto') + echo ''; + echo ""; + exit(); + break; + case "zones": + $zones = $this->Settings->get('zones'); + if (isset($_GET['country'])) + echo json_encode($zones[$_GET['country']]); + exit(); + break; + case "shipcost": + @session_start(); + $this->ShipCalcs = new ShipCalcs($this->path); + if (isset($_GET['method'])) { + $this->Cart->data->Order->Shipping->method = $_GET['method']; + $this->Cart->retotal = true; + $this->Cart->updated(); + $this->Cart->totals(); + echo json_encode($this->Cart->data->Totals); + } + exit(); + break; + case "category-menu": + echo $this->Flow->category_menu(); + exit(); + break; + case "category-products-menu": + echo $this->Flow->category_products(); + exit(); + break; + case "spectemplate": + $db = DB::get(); + $table = DatabaseObject::tablename(Category::$table); + $result = $db->query("SELECT specs FROM $table WHERE id='{$_GET['cat']}' AND spectemplate='on'"); + echo json_encode(unserialize($result->specs)); + exit(); + break; + case "optionstemplate": + $db = DB::get(); + $table = DatabaseObject::tablename(Category::$table); + $result = $db->query("SELECT options,prices FROM $table WHERE id='{$_GET['cat']}' AND variations='on'"); + if (empty($result)) exit(); + $result->options = unserialize($result->options); + $result->prices = unserialize($result->prices); + foreach ($result->options as &$menu) { + foreach ($menu['options'] as &$option) $option['id'] += $_GET['cat']; + } + foreach ($result->prices as &$price) { + $optionids = explode(",",$price['options']); + foreach ($optionids as &$id) $id += $_GET['cat']; + $price['options'] = join(",",$optionids); + $price['optionkey'] = ""; + } + + echo json_encode($result); + exit(); + break; + case "newproducts-rss": + $NewProducts = new NewProducts(array('show' => 5000)); + header("Content-type: application/rss+xml; charset=utf-8"); + echo shopp_rss($NewProducts->rss()); + exit(); + break; + case "category-rss": + $this->catalog($wp); + header("Content-type: application/rss+xml; charset=utf-8"); + echo shopp_rss($this->Category->rss()); + exit(); + break; + case "download": + if (empty($download)) break; + + if ($admin) { + $Asset = new Asset($download); + } else { + $db = DB::get(); + $pricetable = DatabaseObject::tablename(Purchase::$table); + $pricetable = DatabaseObject::tablename(Price::$table); + $assettable = DatabaseObject::tablename(Asset::$table); + + require_once("core/model/Purchased.php"); + $Purchased = new Purchased($download,"dkey"); + $Purchase = new Purchase($Purchased->purchase); + $target = $db->query("SELECT target.* FROM $assettable AS target LEFT JOIN $pricetable AS pricing ON pricing.id=target.parent AND target.context='price' WHERE pricing.id=$Purchased->price AND target.datatype='download'"); + $Asset = new Asset(); + $Asset->populate($target); + + $forbidden = false; + + // Purchase Completion check + if ($Purchase->transtatus != "CHARGED" + && !SHOPP_PREPAYMENT_DOWNLOADS) { + new ShoppError(__('This file cannot be downloaded because payment has not been received yet.','Shopp'),'shopp_download_limit'); + $forbidden = true; + } + + // Account restriction checks + if ($this->Settings->get('account_system') != "none" + && (!$this->Cart->data->login + || $this->Cart->data->Order->Customer->id != $Purchase->customer)) { + new ShoppError(__('You must login to access this download.','Shopp'),'shopp_download_limit',SHOPP_ERR); + header('Location: '.$this->link('account')); + exit(); + } + + // Download limit checking + if ($this->Settings->get('download_limit') // Has download credits available + && $Purchased->downloads+1 > $this->Settings->get('download_limit')) { + new ShoppError(__('This file can no longer be downloaded because the download limit has been reached.','Shopp'),'shopp_download_limit'); + $forbidden = true; + } + + // Download expiration checking + if ($this->Settings->get('download_timelimit') // Within the timelimit + && $Purchased->created+$this->Settings->get('download_timelimit') < mktime() ) { + new ShoppError(__('This file can no longer be downloaded because it has expired.','Shopp'),'shopp_download_limit'); + $forbidden = true; + } + + // IP restriction checks + if ($this->Settings->get('download_restriction') == "ip" + && !empty($Purchase->ip) + && $Purchase->ip != $_SERVER['REMOTE_ADDR']) { + new ShoppError(__('The file cannot be downloaded because this computer could not be verified as the system the file was purchased from.','Shopp'),'shopp_download_limit'); + $forbidden = true; + } + + do_action_ref_array('shopp_download_request',array(&$Purchased)); + } + + if ($forbidden) { + header("Status: 403 Forbidden"); + return; + } + + if ($Asset->download($download)) { + $Purchased->downloads++; + $Purchased->save(); + do_action_ref_array('shopp_download_success',array(&$Purchased)); + exit(); + } + break; + } + } + + /** + * ajax () + * Handles AJAX request processing */ + function ajax() { + if (!isset($_REQUEST['action']) || !defined('DOING_AJAX')) return; + + if (isset($_POST['action'])) { + switch($_POST['action']) { + // Upload an image in the product editor + case "shopp_add_image": + $this->Flow->add_images(); + exit(); + break; + + // Upload a product download file in the product editor + case "shopp_add_download": + $this->Flow->product_downloads(); + exit(); + break; + } + } + + if ((!is_user_logged_in() || !current_user_can('manage_options')) + && strpos($_GET['action'],'wp_ajax_shopp_') !== false) die('-1'); + + if (empty($_GET['action'])) return; + switch($_GET['action']) { + + // Add a category in the product editor + case "wp_ajax_shopp_add_category": + check_admin_referer('shopp-ajax_add_category'); + + if (!empty($_GET['name'])) { + $Catalog = new Catalog(); + $Catalog->load_categories(); + + $Category = new Category(); + $Category->name = $_GET['name']; + $Category->slug = sanitize_title_with_dashes($Category->name); + $Category->parent = $_GET['parent']; + + // Work out pathing + $paths = array(); + if (!empty($Category->slug)) $paths = array($Category->slug); // Include self + + $parentkey = -1; + // If we're saving a new category, lookup the parent + if ($Category->parent > 0) { + array_unshift($paths,$Catalog->categories[$Category->parent]->slug); + $parentkey = $Catalog->categories[$Category->parent]->parent; + } + + while ($category_tree = $Catalog->categories[$parentkey]) { + array_unshift($paths,$category_tree->slug); + $parentkey = $category_tree->parent; + } + + if (count($paths) > 1) $Category->uri = join("/",$paths); + else $Category->uri = $paths[0]; + + $Category->save(); + echo json_encode($Category); + } + exit(); + break; + + case "wp_ajax_shopp_edit_slug": + check_admin_referer('shopp-ajax_edit_slug'); + if ( !current_user_can('manage_options') ) die("-1"); + + switch ($_REQUEST['type']) { + case "category": + $Category = new Category($_REQUEST['id']); + if (empty($_REQUEST['slug'])) $_REQUEST['slug'] = $Category->name; + $Category->slug = sanitize_title_with_dashes($_REQUEST['slug']); + if ($Category->save()) echo apply_filters('editable_slug',$Category->slug); + else echo '-1'; + break; + case "product": + $Product = new Product($_REQUEST['id']); + if (empty($_REQUEST['slug'])) $_REQUEST['slug'] = $Product->name; + $Product->slug = sanitize_title_with_dashes($_REQUEST['slug']); + if ($Product->save()) echo apply_filters('editable_slug',$Product->slug); + else echo '-1'; + break; + } + exit(); + break; + + // Upload a product download file in the product editor + case "wp_ajax_shopp_verify_file": + check_admin_referer('shopp-ajax_verify_file'); + if ( !current_user_can('manage_options') ) exit(); + $target = trailingslashit($this->Settings->get('products_path')).$_POST['filepath']; + if (!file_exists($target)) die("NULL"); + if (is_dir($target)) die("ISDIR"); + if (!is_readable($target)) die("READ"); + die("OK"); + break; + + // Perform a version check for any updates + case "wp_ajax_shopp_version_check": + check_admin_referer('shopp-wp_ajax_shopp_update'); + $request = array( + "ShoppServerRequest" => "version-check", + "ver" => '1.0' + ); + $data = array( + 'core' => SHOPP_VERSION, + 'addons' => join("-",$this->Flow->validate_addons()) + ); + echo $this->Flow->callhome($request,$data); + exit(); + case "wp_ajax_shopp_verify": + if ($this->Settings->get('maintenance') == "on") echo "1"; + exit(); + + // Perform an update process + case "wp_ajax_shopp_update": + check_admin_referer('shopp-wp_ajax_shopp_update'); + $this->Flow->update(); + exit(); + case "wp_ajax_shopp_setftp": + check_admin_referer('shopp-wp_ajax_shopp_update'); + $this->Flow->settings_save(); + $updates = $this->Settings->get('ftp_credentials'); + exit(); + } + + } + +} // end Shopp + +/** + * shopp() + * Provides the Shopp 'tag' support to allow for complete + * customization of customer interfaces + * + * @param $object The object to get the tag property from + * @param $property The property of the object to get/output + * @param $options Custom options for the property result in query form + * (option1=value&option2=value&...) or alternatively as an associative array + */ +function shopp () { + global $Shopp; + $args = func_get_args(); + + $object = strtolower($args[0]); + $property = strtolower($args[1]); + $options = array(); + + if (isset($args[2])) { + if (is_array($args[2]) && !empty($args[2])) { + // handle associative array for options + foreach(array_keys($args[2]) as $key) + $options[strtolower($key)] = $args[2][$key]; + } else { + // regular url-compatible arguments + $paramsets = explode("&",$args[2]); + foreach ((array)$paramsets as $paramset) { + if (empty($paramset)) continue; + $key = $paramset; + $value = ""; + if (strpos($paramset,"=") !== false) + list($key,$value) = explode("=",$paramset); + $options[strtolower($key)] = $value; + } + } + } + + $Object = false; $result = false; + switch (strtolower($object)) { + case "cart": if (isset($Shopp->Cart)) $Object =& $Shopp->Cart; break; + case "cartitem": if (isset($Shopp->Cart)) $Object =& $Shopp->Cart; break; + case "shipping": if (isset($Shopp->Cart)) $Object =& $Shopp->Cart; break; + case "checkout": if (isset($Shopp->Cart)) $Object =& $Shopp->Cart; break; + case "category": if (isset($Shopp->Category)) $Object =& $Shopp->Category; break; + case "subcategory": if (isset($Shopp->Category->child)) $Object =& $Shopp->Category->child; break; + case "catalog": if (isset($Shopp->Catalog)) $Object =& $Shopp->Catalog; break; + case "product": if (isset($Shopp->Product)) $Object =& $Shopp->Product; break; + case "purchase": if (isset($Shopp->Cart->data->Purchase)) $Object =& $Shopp->Cart->data->Purchase; break; + case "customer": if (isset($Shopp->Cart->data->Order->Customer)) $Object =& $Shopp->Cart->data->Order->Customer; break; + case "error": if (isset($Shopp->Cart->data->Errors)) $Object =& $Shopp->Cart->data->Errors; break; + default: $Object = false; + } + + if (!$Object) new ShoppError("The shopp('$object') tag cannot be used in this context because the object responsible for handling it doesn't exist.",'shopp_tag_error',SHOPP_ADMIN_ERR); + else { + switch (strtolower($object)) { + case "cartitem": $result = $Object->itemtag($property,$options); break; + case "shipping": $result = $Object->shippingtag($property,$options); break; + case "checkout": $result = $Object->checkouttag($property,$options); break; + default: $result = $Object->tag($property,$options); break; + } + } + + // Force boolean result + if (isset($options['is'])) { + if (value_is_true($options['is'])) { + if ($result) return true; + } else { + if ($result == false) return true; + } + return false; + } + + // Always return a boolean if the result is boolean + if (is_bool($result)) return $result; + + // Return the result instead of outputting it + if ((isset($options['return']) && value_is_true($options['return'])) || + isset($options['echo']) && !value_is_true($options['echo'])) + return $result; + + // Output the result + echo $result; + return true; +} + +?> diff --git a/blog/wp-content/plugins/shopp/core/DB.php b/blog/wp-content/plugins/shopp/core/DB.php new file mode 100644 index 0000000..69ccbd1 --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/DB.php @@ -0,0 +1,423 @@ + array("int", "bit", "bool", "boolean"), + "float" => array("float", "double", "decimal", "real"), + "string" => array("char", "binary", "varbinary", "text", "blob"), + "list" => array("enum","set"), + "date" => array("date", "time", "year") + ); + var $results = array(); + var $queries = array(); + var $dbh = false; + + + protected function DB () { + global $wpdb; + $this->dbh = $wpdb->dbh; + $this->version = mysql_get_server_info(); + } + + function __clone() { trigger_error('Clone is not allowed.', E_USER_ERROR); } + static function &get() { + if (!self::$instance instanceof self) + self::$instance = new self; + return self::$instance; + } + + + /* + * Connects to the database server */ + function connect($user, $password, $database, $host) { + $this->dbh = @mysql_connect($host, $user, $password); + if (!$this->dbh) trigger_error("Could not connect to the database server '$host'."); + else $this->select($database); + } + + /** + * Select the database to use for our connection */ + function select($database) { + if(!@mysql_select_db($database,$this->dbh)) + trigger_error("Could not select the '$database' database."); + } + + /** + * Escape contents of data for safe insertion into the db */ + function escape($data) { + // Prevent double escaping by stripping any existing escapes out + return addslashes(stripslashes($data)); + } + + function clean($data) { + if (is_array($data)) array_map(array(&$this,'clean'), $data); + if (is_string($data)) rtrim($data); + return $data; + } + + /** + * Send a query to the database */ + function query($query, $output=true, $errors=true) { + if (SHOPP_QUERY_DEBUG) $this->queries[] = $query; + $result = @mysql_query($query, $this->dbh); + if (SHOPP_QUERY_DEBUG && class_exists('ShoppError')) new ShoppError($query,'shopp_query_debug',SHOPP_DEBUG_ERR); + + // Error handling + if ($this->dbh && $error = mysql_error($this->dbh)) + new ShoppError(sprintf(__('Query failed: %s - DB Query: %s','Shopp'),$error, str_replace("\n","",$query)),'shopp_query_error',SHOPP_DB_ERR); + + // Results handling + if ( preg_match("/^\\s*(create|drop|insert|delete|update|replace) /i",$query) ) { + $this->affected = mysql_affected_rows(); + if ( preg_match("/^\\s*(insert|replace) /i",$query) ) { + $insert = @mysql_fetch_object(@mysql_query("SELECT LAST_INSERT_ID() AS id", $this->dbh)); + return $insert->id; + } + + if ($this->affected > 0) return $this->affected; + else return true; + } else { + if ($result === true) return true; + $this->results = array(); + while ($row = @mysql_fetch_object($result)) { + $this->results[] = $row; + } + @mysql_free_result($result); + if ($output && sizeof($this->results) == 1) $this->results = $this->results[0]; + return $this->results; + } + + } + + function datatype($type) { + foreach((array)$this->_datatypes as $datatype => $patterns) { + foreach((array)$patterns as $pattern) { + if (strpos($type,$pattern) !== false) return $datatype; + } + } + return false; + } + + /** + * Prepare the data properties for entry into + * the database */ + function prepare($object) { + $data = array(); + + // Go through each data property of the object + foreach(get_object_vars($object) as $property => $value) { + if (!isset($object->_datatypes[$property])) continue; + + // If the property is has a _datatype + // it belongs in the database and needs + // to be prepared + + // Process the data + switch ($object->_datatypes[$property]) { + case "string": + // Escape characters in strings as needed + if (is_array($value)) $value = serialize($value); + $data[$property] = "'".$this->escape($value)."'"; + break; + case "list": + // If value is empty, skip setting the field + // so it inherits the default value in the db + if (!empty($value)) + $data[$property] = "'$value'"; + break; + case "date": + // If it's an empty date, set it to now()'s timestamp + if (is_null($value)) { + $data[$property] = "now()"; + // If the date is an integer, convert it to an + // sql YYYY-MM-DD HH:MM:SS format + } elseif (!empty($value) && is_int(intval($value))) { + $data[$property] = "'".mkdatetime(intval($value))."'"; + // Otherwise it's already ready, so pass it through + } else { + $data[$property] = "'$value'"; + } + break; + case "int": + case "float": + $value = floatnum($value); + + $data[$property] = "'$value'"; + + if (empty($value)) $data[$property] = "'0'"; + + // Special exception for id fields + if ($property == "id" && empty($value)) $data[$property] = "NULL"; + + break; + default: + // Anything not needing processing + // passes through into the object + $data[$property] = "'$value'"; + } + + } + + return $data; + } + + /** + * Get the list of possible values for an enum() or set() column */ + function column_options($table = null, $column = null) { + if ( ! ($table && $column)) return array(); + $r = $this->query("SHOW COLUMNS FROM $table LIKE '$column'"); + if ( strpos($r[0]->Type,"enum('") ) + $list = substr($r[0]->Type, 6, strlen($r[0]->Type) - 8); + + if ( strpos($r[0]->Type,"set('") ) + $list = substr($r[0]->Type, 5, strlen($r[0]->Type) - 7); + + return explode("','",$list); + } + + /** + * Send a large set of queries to the database. */ + function loaddata ($queries) { + $queries = explode(";\n", $queries); + array_pop($queries); + foreach ($queries as $query) if (!empty($query)) $this->query($query); + return true; + } + + +} // END class DB + + +/* class DatabaseObject + * Generic database glueware between the database and the active data model */ + +class DatabaseObject { + + function DatabaseObject () { + // Constructor + } + + /** + * Initializes the db object by grabbing table schema + * so we know how to work with this table */ + function init ($table,$key="id") { + global $Shopp; + $db = DB::get(); + + $this->_table = $this->tablename($table); // So we know what the table name is + $this->_key = $key; // So we know what the primary key is + $this->_datatypes = array(); // So we know the format of the table + $this->_lists = array(); // So we know the options for each list + $this->_defaults = array(); // So we know the default values for each field + + if (isset($Shopp->Settings)) { + $Tables = $Shopp->Settings->get('data_model'); + if (isset($Tables[$this->_table])) { + $this->_datatypes = $Tables[$this->_table]->_datatypes; + $this->_lists = $Tables[$this->_table]->_lists; + foreach($this->_datatypes as $property => $type) { + $this->{$property} = (isset($this->_defaults[$property]))? + $this->_defaults[$property]:''; + if (empty($this->{$property}) && $type == "date") + $this->{$property} = null; + } + return true; + } + } + + if (!$r = $db->query("SHOW COLUMNS FROM $this->_table",true,false)) return false; + + // Map out the table definition into our data structure + foreach($r as $object) { + $property = $object->Field; + $this->{$property} = $object->Default; + $this->_datatypes[$property] = $db->datatype($object->Type); + $this->_defaults[$property] = $object->Default; + + // Grab out options from list fields + if ($db->datatype($object->Type) == "list") { + $values = str_replace("','", ",", substr($object->Type,strpos($object->Type,"'")+1,-2)); + $this->_lists[$property] = explode(",",$values); + } + } + + if (isset($Shopp->Settings)) { + $Tables[$this->_table] = new StdClass(); + $Tables[$this->_table]->_datatypes = $this->_datatypes; + $Tables[$this->_table]->_lists = $this->_lists; + $Tables[$this->_table]->_defaults = $this->_defaults; + $Shopp->Settings->save('data_model',$Tables); + } + } + + /** + * Load a single record by the primary key or a custom query + * @param $where - An array of key/values to be built into an SQL where clause + * or + * @param $id - A string containing the id for db object's predefined primary key + * or + * @param $id - A string containing the object's id value + * @param $key - A string of the name of the db object's primary key + **/ + function load () { + $db = DB::get(); + + $args = func_get_args(); + if (empty($args[0])) return false; + + $where = ""; + if (is_array($args[0])) + foreach ($args[0] as $key => $id) + $where .= ($where == ""?"":" AND ")."$key='".$db->escape($id)."'"; + else { + $id = $args[0]; + $key = $this->_key; + if (!empty($args[1])) $key = $args[1]; + $where = $key."='".$db->escape($id)."'"; + } + + $r = $db->query("SELECT * FROM $this->_table WHERE $where LIMIT 1"); + $this->populate($r); + + if (!empty($this->id)) return true; + return false; + } + + /** + * Processes a bulk string of semi-colon separated SQL queries */ + function loaddata ($queries) { + $queries = explode(";\n", $queries); + array_pop($queries); + foreach ($queries as $query) if (!empty($query)) $this->query($query); + return true; + } + + function tablename ($table) { + global $table_prefix; + return $table_prefix.SHOPP_DBPREFIX.$table; + } + + /** + * Save a record, updating when we have a value for the primary key, + * inserting a new record when we don't */ + function save () { + $db = DB::get(); + + $data = $db->prepare($this); + $id = $this->{$this->_key}; + // Update record + if (!empty($id)) { + if (isset($data['modified'])) $data['modified'] = "now()"; + $dataset = $this->dataset($data); + $db->query("UPDATE $this->_table SET $dataset WHERE $this->_key=$id"); + return true; + // Insert new record + } else { + if (isset($data['created'])) $data['created'] = "now()"; + if (isset($data['modified'])) $data['modified'] = "now()"; + $dataset = $this->dataset($data); + //print "INSERT $this->_table SET $dataset"; + $this->id = $db->query("INSERT $this->_table SET $dataset"); + return $this->id; + } + + } + + /** + * Deletes the record associated with this object */ + function delete () { + $db = DB::get(); + // Delete record + $id = $this->{$this->_key}; + if (!empty($id)) $db->query("DELETE FROM $this->_table WHERE $this->_key='$id'"); + else return false; + } + + /** + * Populate the object properties from a set of + * loaded results */ + function populate($data) { + if(empty($data)) return false; + foreach(get_object_vars($data) as $property => $value) { + if (empty($this->_datatypes[$property])) continue; + // Process the data + switch ($this->_datatypes[$property]) { + case "date": + $this->{$property} = mktimestamp($value); + break; + case "int": + case "float": + case "string": + // If string has been serialized, unserialize it + if (preg_match("/^[sibNaO](?:\:.+?\{.*\}$|\:.+;$|;$)/s",$value)) $value = unserialize($value); + default: + // Anything not needing processing + // passes through into the object + $this->{$property} = $value; + } + } + } + + /** + * Build an SQL-ready string of the prepared data + * for entry into the database */ + function dataset($data) { + $query = ""; + foreach($data as $property => $value) { + if (!empty($query)) $query .= ", "; + $query .= "$property=$value"; + } + return $query; + } + + /** + * Populate the object properties from a set of + * form post inputs */ + function updates($data,$ignores = array()) { + $db = DB::get(); + + foreach ($data as $key => $value) { + if (!is_null($value) && + !in_array($key,$ignores) && + property_exists($this, $key) ) { + $this->{$key} = $db->clean($value); + } + } + } + + /** + * Copy property values from a given (like) object to this object + * where the property names match */ + function copydata ($Object,$prefix="") { + $db = DB::get(); + + $ignores = array("_datatypes","_table","_key","_lists","id","created","modified"); + foreach(get_object_vars($Object) as $property => $value) { + $property = $prefix.$property; + if (property_exists($this,$property) && + !in_array($property,$ignores)) + $this->{$property} = $db->clean($value); + } + } + +} // END class DatabaseObject + +?> \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/Flow.php b/blog/wp-content/plugins/shopp/core/Flow.php new file mode 100644 index 0000000..986dda9 --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/Flow.php @@ -0,0 +1,3010 @@ +Settings = $Core->Settings; + $this->Cart = $Core->Cart; + + $langpath = array(PLUGINDIR,$Core->directory,'lang'); + load_plugin_textdomain('Shopp',join(DIRECTORY_SEPARATOR,$langpath)); + + $this->basepath = dirname(dirname(__FILE__)); + $this->uri = ((!empty($_SERVER['HTTPS']))?"https://":"http://"). + $_SERVER['SERVER_NAME'].str_replace("?".$_SERVER['QUERY_STRING'],"",$_SERVER['REQUEST_URI']); + $this->secureuri = 'https://'.$_SERVER['SERVER_NAME'].$this->uri; + + $this->Admin = new stdClass(); + $this->Admin->orders = $Core->directory."-orders"; + $this->Admin->customers = $Core->directory."-customers"; + $this->Admin->editcustomer = $Core->directory."-customers-edit"; + $this->Admin->categories = $Core->directory."-categories"; + $this->Admin->editcategory = $Core->directory."-categories-edit"; + $this->Admin->products = $Core->directory."-products"; + $this->Admin->editproduct = $Core->directory."-products-edit"; + $this->Admin->promotions = $Core->directory."-promotions"; + $this->Admin->editpromo = $Core->directory."-promotions-edit"; + $this->Admin->settings = array( + 'settings' => array($Core->directory."-settings",__('General','Shopp')), + 'checkout' => array($Core->directory."-settings-checkout",__('Checkout','Shopp')), + 'payments' => array($Core->directory."-settings-payments",__('Payments','Shopp')), + 'shipping' => array($Core->directory."-settings-shipping",__('Shipping','Shopp')), + 'taxes' => array($Core->directory."-settings-taxes",__('Taxes','Shopp')), + 'presentation' => array($Core->directory."-settings-presentation",__('Presentation','Shopp')), + 'system' => array($Core->directory."-settings-system",__('System','Shopp')), + 'update' => array($Core->directory."-settings-update",__('Update','Shopp')) + ); + $this->Admin->help = $Core->directory."-help"; + $this->Admin->welcome = $Core->directory."-welcome"; + $this->Admin->default = $this->Admin->orders; + + $this->Pages = $Core->Settings->get('pages'); + if (empty($this->Pages)) { + $this->Pages = array(); + $this->Pages['catalog'] = array('name'=>'shop','title'=>'Shop','content'=>'[catalog]'); + $this->Pages['cart'] = array('name'=>'cart','title'=>'Cart','content'=>'[cart]'); + $this->Pages['checkout'] = array('name'=>'checkout','title'=>'Checkout','content'=>'[checkout]'); + $this->Pages['account'] = array('name'=>'account','title'=>'Your Orders','content'=>'[account]'); + } + $this->Docs = array( + 'orders' => 'Managing Orders', + 'customers' => 'Managing Customers', + 'promotions' => 'Running Sales & Promotions', + 'editpromos' => 'Running Sales & Promotions', + 'products' => 'Editing a Product', + 'editproducts' => 'Editing a Product', + 'categories' => 'Editing a Category', + 'editcategory' => 'Editing a Category', + 'settings' => 'General Settings', + 'checkout' => 'Checkout Settings', + 'payments' => 'Payments Settings', + 'shipping' => 'Shipping Settings', + 'taxes' => 'Taxes Settings', + 'presentation' => 'Presentation Settings', + 'system' => 'System Settings', + 'update' => 'Update Settings' + ); + + $this->coremods = array("GoogleCheckout.php", "PayPalExpress.php", + "TestMode.php", "FlatRates.php", "ItemQuantity.php", + "OrderAmount.php", "OrderWeight.php"); + + if (!defined('BR')) define('BR','
'); + + // Overrideable macros + if (!defined('SHOPP_USERLEVEL')) define('SHOPP_USERLEVEL',8); + if (!defined('SHOPP_NOSSL')) define('SHOPP_NOSSL',false); + if (!defined('SHOPP_PREPAYMENT_DOWNLOADS')) define('SHOPP_PREPAYMENT_DOWNLOADS',false); + if (!defined('SHOPP_SESSION_TIMEOUT')) define('SHOPP_SESSION_TIMEOUT',7200); + if (!defined('SHOPP_QUERY_DEBUG')) define('SHOPP_QUERY_DEBUG',false); + + define("SHOPP_WP27",(!version_compare($wp_version,"2.7","<"))); + define("SHOPP_DEBUG",($Core->Settings->get('error_logging') == 2048)); + define("SHOPP_PATH",$this->basepath); + define("SHOPP_ADMINPATH",SHOPP_PATH."/core/ui"); + define("SHOPP_PLUGINURI",$Core->uri); + define("SHOPP_DBSCHEMA",SHOPP_PATH."/core/model/schema.sql"); + + define("SHOPP_TEMPLATES",($Core->Settings->get('theme_templates') != "off" && + is_dir($Core->Settings->get('theme_templates')))? + $Core->Settings->get('theme_templates'): + SHOPP_PATH.DIRECTORY_SEPARATOR."templates"); + define("SHOPP_TEMPLATES_URI",($Core->Settings->get('theme_templates') != "off" && + is_dir($Core->Settings->get('theme_templates')))? + get_bloginfo('stylesheet_directory')."/shopp": + $Core->uri."/templates"); + + define("SHOPP_GATEWAYS",SHOPP_PATH.DIRECTORY_SEPARATOR."gateways".DIRECTORY_SEPARATOR); + + define("SHOPP_PERMALINKS",(get_option('permalink_structure') == "")?false:true); + + define("SHOPP_LOOKUP",(strpos($_SERVER['REQUEST_URI'],"images/") !== false || + strpos($_SERVER['REQUEST_URI'],"lookup=") !== false)?true:false); + + $this->uploadErrors = array( + UPLOAD_ERR_INI_SIZE => __('The uploaded file exceeds the upload_max_filesize directive in PHP\'s configuration file','Shopp'), + UPLOAD_ERR_FORM_SIZE => __('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.','Shopp'), + UPLOAD_ERR_PARTIAL => __('The uploaded file was only partially uploaded.','Shopp'), + UPLOAD_ERR_NO_FILE => __('No file was uploaded.','Shopp'), + UPLOAD_ERR_NO_TMP_DIR => __('The server\'s temporary folder is missing.','Shopp'), + UPLOAD_ERR_CANT_WRITE => __('Failed to write the file to disk.','Shopp'), + UPLOAD_ERR_EXTENSION => __('File upload stopped by extension.','Shopp'), + ); + + } + + function admin () { + global $Shopp; + $db =& DB::get(); + if (!defined('WP_ADMIN') || !isset($_GET['page'])) return; + $Admin = $Shopp->Flow->Admin; + $adminurl = $Shopp->wpadminurl."admin.php"; + + $defaults = array( + 'page' => false, + 'deleting' => false, + 'delete' => false, + 'id' => false, + 'save' => false, + 'duplicate' => false, + 'next' => false + ); + $args = array_merge($defaults,$_REQUEST); + extract($args,EXTR_SKIP); + + if (strstr($page,$Admin->categories)) { + + if ($page == "shopp-categories" + && !empty($deleting) + && !empty($delete) + && is_array($delete)) { + foreach($delete as $deletion) { + $Category = new Category($deletion); + $db->query("UPDATE $Category->_table SET parent=0 WHERE parent=$Category->id"); + $Category->delete(); + } + $redirect = esc_url(add_query_arg(array_merge($_GET,array('delete[]'=>null,'deleting'=>null)),$adminurl)); + shopp_redirect($redirect); + } + + if ($id && $id != "new") + $Shopp->Category = new Category($id); + else $Shopp->Category = new Category(); + + if ($save) { + $this->save_category($Shopp->Category); + $this->Notice = ''.stripslashes($Shopp->Category->name).' '.__('has been saved.','Shopp'); + + if ($next) { + if ($next != "new") + $Shopp->Category = new Category($next); + else $Shopp->Category = new Category(); + } else { + if (empty($id)) $id = $Shopp->Category->id; + $Shopp->Category = new Category($id); + } + + } + + } // end $Admin->categories + + if (strstr($page,$Admin->products)) { + if ($page == "shopp-products" + && !empty($deleting) + && !empty($delete) + && is_array($delete)) { + foreach($delete as $deletion) { + $Product = new Product($deletion); + $Product->delete(); + } + $redirect = esc_url(add_query_arg(array_merge($_GET,array('delete'=>null,'deleting'=>null)),$adminurl)); + shopp_redirect($redirect); + exit(); + } + + if ($duplicate) { + $Product = new Product(); + $Product->load($duplicate); + $Product->duplicate(); + shopp_redirect(add_query_arg('page',$Admin->products,$adminurl)); + } + + if ($id && $id != "new") { + $Shopp->Product = new Product($id); + $Shopp->Product->load_data(array('prices','specs','categories','tags')); + } else { + $Shopp->Product = new Product(); + $Shopp->Product->published = "on"; + } + + if ($save) { + $this->save_product($Shopp->Product); + $this->Notice = ''.stripslashes($Shopp->Product->name).' '.__('has been saved.','Shopp'); + + if ($next) { + if ($next == "new") { + $Shopp->Product = new Product(); + $Shopp->Product->published = "on"; + } else { + $Shopp->Product = new Product($next); + $Shopp->Product->load_data(array('prices','specs','categories','tags')); + } + } else { + if (empty($id)) $id = $Shopp->Product->id; + $Shopp->Product = new Product($id); + $Shopp->Product->load_data(array('prices','specs','categories','tags')); + } + } + } // end $Admin->products + + } + + function helpdoc ($menu,$page) { + if (!isset($this->Docs[$menu])) return; + $url = SHOPP_DOCS.str_replace("+","_",urlencode($this->Docs[$menu])); + $link = htmlspecialchars($this->Docs[$menu]); + $content = ''.$link.''; + + if ($menu == "orders" || $menu == "customers") { + ob_start(); + include("{$this->basepath}/core/ui/help/$menu.php"); + $help = ob_get_contents(); + ob_end_clean(); + $content .= $help; + } + + add_contextual_help($page,$content); + } + + /** + * Catalog flow handlers + **/ + function catalog () { + global $Shopp; + + if (SHOPP_DEBUG) new ShoppError('Displaying catalog page request: '.$_SERVER['REQUEST_URI'],'shopp_catalog',SHOPP_DEBUG_ERR); + + ob_start(); + switch ($Shopp->Catalog->type) { + case "product": + if (file_exists(SHOPP_TEMPLATES."/product-{$Shopp->Product->id}.php")) + include(SHOPP_TEMPLATES."/product-{$Shopp->Product->id}.php"); + else include(SHOPP_TEMPLATES."/product.php"); break; + + case "category": + if (isset($Shopp->Category->smart) && + file_exists(SHOPP_TEMPLATES."/category-{$Shopp->Category->slug}.php")) + include(SHOPP_TEMPLATES."/category-{$Shopp->Category->slug}.php"); + elseif (isset($Shopp->Category->id) && + file_exists(SHOPP_TEMPLATES."/category-{$Shopp->Category->id}.php")) + include(SHOPP_TEMPLATES."/category-{$Shopp->Category->id}.php"); + else include(SHOPP_TEMPLATES."/category.php"); break; + + default: include(SHOPP_TEMPLATES."/catalog.php"); break; + } + $content = ob_get_contents(); + ob_end_clean(); + + $classes = $Shopp->Catalog->type; + if (!isset($_COOKIE['shopp_catalog_view'])) { + // No cookie preference exists, use shopp default setting + $view = $Shopp->Settings->get('default_catalog_view'); + if ($view == "list") $classes .= " list"; + if ($view == "grid") $classes .= " grid"; + } else { + if ($_COOKIE['shopp_catalog_view'] == "list") $classes .= " list"; + if ($_COOKIE['shopp_catalog_view'] == "grid") $classes .= " grid"; + } + + return apply_filters('shopp_catalog','
'.$content.'
'); + } + + /** + * Shopping Cart flow handlers + **/ + function cart ($attrs=array()) { + $Cart = $this->Cart; + + ob_start(); + include(SHOPP_TEMPLATES."/cart.php"); + $content = ob_get_contents(); + ob_end_clean(); + + return apply_filters('shopp_cart_template','
'.$content.'
'); + } + + function shipping_estimate ($attrs) { + $Cart = $this->Cart; + + ob_start(); + include(SHOPP_TEMPLATES."/shipping.php"); + $content = ob_get_contents(); + ob_end_clean(); + + return $content; + } + + /** + * Checkout flow handlers + **/ + function checkout () { + global $Shopp; + $Cart = $Shopp->Cart; + + $process = get_query_var('shopp_proc'); + $xco = get_query_var('shopp_xco'); + if (!empty($xco)) { + $Shopp->gateway($xco); + $Shopp->Gateway->actions(); + } + + switch ($process) { + case "confirm-order": $content = $this->order_confirmation(); break; + case "receipt": $content = $this->order_receipt(); break; + default: + ob_start(); + if ($Cart->data->Errors->exist(SHOPP_COMM_ERR)) { + include(SHOPP_TEMPLATES."/errors.php"); + $Cart->data->Errors->reset(); + } + if (!empty($xco)) { + + if (!empty($Shopp->Gateway)) { + if ($Shopp->Gateway->checkout) include(SHOPP_TEMPLATES."/checkout.php"); + else { + if ($Cart->data->Errors->exist(SHOPP_COMM_ERR)) + include(SHOPP_TEMPLATES."/errors.php"); + include(SHOPP_TEMPLATES."/summary.php"); + echo $Shopp->Gateway->tag('button'); + } + } else include(SHOPP_TEMPLATES."/summary.php"); + + } else include(SHOPP_TEMPLATES."/checkout.php"); + $content = ob_get_contents(); + ob_end_clean(); + + unset($Cart->data->OrderError); + } + + // Wrap with #shopp if not already wrapped + if (strpos($content,'
') === false) + $content = '
'.$content.'
'; + + return apply_filters('shopp_checkout',$content); + } + + function checkout_order_summary () { + global $Shopp; + $Cart = $Shopp->Cart; + + ob_start(); + include(SHOPP_TEMPLATES."/summary.php"); + $content = ob_get_contents(); + ob_end_clean(); + + return apply_filters('shopp_order_summary',$content); + } + + function secure_page_links ($linklist) { + global $Shopp; + $gateway = $Shopp->Settings->get('payment_gateway'); + if (strpos($gateway,"TestMode.php") !== false) return $linklist; + $hrefs = array( + 'checkout' => $Shopp->link('checkout'), + 'account' => $Shopp->link('account') + ); + if (empty($gateway)) return str_replace($hrefs['checkout'],$Shopp->link('cart'),$linklist); + + foreach ($hrefs as $href) { + $secure_href = str_replace("http://","https://",$href); + $linklist = str_replace($href,$secure_href,$linklist); + } + return $linklist; + } + + /** + * order() + * Processes orders by passing transaction information to the active + * payment gateway */ + function order ($gateway = false) { + global $Shopp; + $Cart = $Shopp->Cart; + $db = DB::get(); + + do_action('shopp_order_preprocessing'); + + $Order = $Shopp->Cart->data->Order; + $Order->Totals = $Shopp->Cart->data->Totals; + $Order->Items = $Shopp->Cart->contents; + $Order->Cart = $Shopp->Cart->session; + + if ($Shopp->Gateway && !$Cart->orderisfree()) { + // Use an external checkout payment gateway + if (SHOPP_DEBUG) new ShoppError('Processing order through a remote-payment gateway service.',false,SHOPP_DEBUG_ERR); + $Purchase = $Shopp->Gateway->process(); + if (!$Purchase) { + if (SHOPP_DEBUG) new ShoppError('The remote-payment gateway encountered an error.',false,SHOPP_DEBUG_ERR); + $Shopp->Gateway->error(); + return false; + } + if (SHOPP_DEBUG) new ShoppError('Transaction successfully processed by remote-payment gateway service.',false,SHOPP_DEBUG_ERR); + } else { + // Use local payment gateway set in payment settings + + $gateway = $Shopp->Settings->get('payment_gateway'); + + // Process a transaction if the order has a cost (is not free) + if (!$Cart->orderisfree()) { + + if (!$Shopp->gateway($gateway)) return false; + + // Process the transaction through the payment gateway + if (SHOPP_DEBUG) new ShoppError('Processing order through local-payment gateway service.',false,SHOPP_DEBUG_ERR); + $processed = $Shopp->Gateway->process(); + + // exit(); + // There was a problem processing the transaction, + // grab the error response from the gateway so we can report it + if (!$processed) { + if (SHOPP_DEBUG) new ShoppError('The local-payment gateway encountered an error.',false,SHOPP_DEBUG_ERR); + $Shopp->Gateway->error(); + return false; + } + $gatewaymeta = $this->scan_gateway_meta(SHOPP_GATEWAYS.$gateway); + $gatewayname = $gatewaymeta->name; + $transactionid = $Shopp->Gateway->transactionid(); + + if (SHOPP_DEBUG) new ShoppError('Transaction '.$transactionid.' successfully processed by local-payment gateway service '.$gatewayname.'.',false,SHOPP_DEBUG_ERR); + + } else { + if(!$Cart->validorder()){ + new ShoppError(__('There is not enough customer information to process the order.','Shopp'),'invalid_order',SHOPP_TRXN_ERR); + return false; + } + $gatewayname = __('N/A','Shopp'); + $transactionid = __('(Free Order)','Shopp'); + } + + $authentication = $Shopp->Settings->get('account_system'); + + // Transaction successful, save the order + + if ($authentication == "wordpress") { + // Check if they've logged in + // If the shopper is already logged-in, save their updated customer info + if ($Shopp->Cart->data->login) { + if (SHOPP_DEBUG) new ShoppError('Customer logged in, linking Shopp customer account to existing WordPress account.',false,SHOPP_DEBUG_ERR); + get_currentuserinfo(); + global $user_ID; + $Order->Customer->wpuser = $user_ID; + } + + // Create WordPress account (if necessary) + if (!$Order->Customer->wpuser) { + if (SHOPP_DEBUG) new ShoppError('Creating a new WordPress account for this customer.',false,SHOPP_DEBUG_ERR); + if(!$Order->Customer->new_wpuser()) new ShoppError(__('Account creation failed on order for customer id:' . $Order->Customer->id, "Shopp"), false,SHOPP_TRXN_ERR); + } + } + + // Create a WP-compatible password hash to go in the db + if (empty($Order->Customer->id)) + $Order->Customer->password = wp_hash_password($Order->Customer->password); + $Order->Customer->save(); + + $Order->Billing->customer = $Order->Customer->id; + $Order->Billing->card = substr($Order->Billing->card,-4); + $Order->Billing->save(); + + // Card data is truncated, switch the cart to normal mode + if ($Shopp->Cart->secured() && is_shopp_secure()) + $Shopp->Cart->secured(false); + + if (!empty($Order->Shipping->address)) { + $Order->Shipping->customer = $Order->Customer->id; + $Order->Shipping->save(); + } + + $Promos = array(); + foreach ($Shopp->Cart->data->PromosApplied as $promo) + $Promos[$promo->id] = $promo->name; + + if ($Shopp->Cart->orderisfree()) $orderisfree = true; + else $orderisfree = false; + + $Purchase = new Purchase(); + $Purchase->customer = $Order->Customer->id; + $Purchase->billing = $Order->Billing->id; + $Purchase->shipping = $Order->Shipping->id; + $Purchase->copydata($Order->Customer); + $Purchase->copydata($Order->Billing); + $Purchase->copydata($Order->Shipping,'ship'); + $Purchase->copydata($Shopp->Cart->data->Totals); + $Purchase->data = $Order->data; + $Purchase->promos = $Promos; + $Purchase->freight = $Shopp->Cart->data->Totals->shipping; + $Purchase->gateway = $gatewayname; + $Purchase->transactionid = $transactionid; + $Purchase->transtatus = "CHARGED"; + $Purchase->ip = $Shopp->Cart->ip; + $Purchase->save(); + // echo "
"; print_r($Purchase); echo "
"; + + foreach($Shopp->Cart->contents as $Item) { + $Purchased = new Purchased(); + $Purchased->copydata($Item); + $Purchased->purchase = $Purchase->id; + if (!empty($Purchased->download)) $Purchased->keygen(); + $Purchased->save(); + if ($Item->inventory) $Item->unstock(); + } + + if (SHOPP_DEBUG) new ShoppError('Purchase '.$Purchase->id.' was successfully saved to the database.',false,SHOPP_DEBUG_ERR); + } + + // Skip post order if no Purchase ID exists + if (empty($Purchase->id)) return true; + + // Empty cart on successful order + $Shopp->Cart->unload(); + session_destroy(); + + // Start new cart session + $Shopp->Cart = new Cart(); + session_start(); + + // Keep the user logged in or log them in if they are a new customer + if ($Shopp->Cart->data->login || $authentication != "none") + $Shopp->Cart->loggedin($Order->Customer); + + // Save the purchase ID for later lookup + $Shopp->Cart->data->Purchase = new Purchase($Purchase->id); + $Shopp->Cart->data->Purchase->load_purchased(); + // // $Shopp->Cart->save(); + + // Allow other WordPress plugins access to Purchase data to extend + // what Shopp does after a successful transaction + do_action_ref_array('shopp_order_success',array(&$Shopp->Cart->data->Purchase)); + + // Send email notifications + // notification(addressee name, email, subject, email template, receipt template) + $Purchase->notification( + "$Purchase->firstname $Purchase->lastname", + $Purchase->email, + __('Order Receipt','Shopp') + ); + + if ($Shopp->Settings->get('receipt_copy') == 1) { + $Purchase->notification( + '', + $Shopp->Settings->get('merchant_email'), + __('New Order','Shopp') + ); + } + + $ssl = true; + // Test Mode will not require encrypted checkout + if (strpos($gateway,"TestMode.php") !== false + || isset($_GET['shopp_xco']) + || $orderisfree + || SHOPP_NOSSL) + $ssl = false; + shopp_redirect($Shopp->link('receipt',$ssl)); + } + + // Display the confirm order screen + function order_confirmation () { + global $Shopp; + $Cart = $Shopp->Cart; + + ob_start(); + include(SHOPP_TEMPLATES."/confirm.php"); + $content = ob_get_contents(); + ob_end_clean(); + return apply_filters('shopp_order_confirmation','
'.$content.'
'); + } + + // Display a sales receipt + function order_receipt ($template="receipt.php") { + global $Shopp; + $Cart = $Shopp->Cart; + + ob_start(); + include(trailingslashit(SHOPP_TEMPLATES).$template); + $content = ob_get_contents(); + ob_end_clean(); + return apply_filters('shopp_order_receipt','
'.$content.'
'); + } + + // Display an error page + function error_page ($template="errors.php") { + global $Shopp; + $Cart = $Shopp->Cart; + + ob_start(); + include(trailingslashit(SHOPP_TEMPLATES).$template); + $content = ob_get_contents(); + ob_end_clean(); + return apply_filters('shopp_errors_page','
'.$content.'
'); + } + + + /** + * Orders admin flow handlers + */ + function orders_list() { + global $Shopp,$Orders; + $db = DB::get(); + + $defaults = array( + 'page' => false, + 'deleting' => false, + 'selected' => false, + 'update' => false, + 'newstatus' => false, + 'pagenum' => 1, + 'per_page' => false, + 'start' => '', + 'end' => '', + 'status' => false, + 's' => '', + 'range' => '', + 'startdate' => '', + 'enddate' => '', + ); + + $args = array_merge($defaults,$_GET); + extract($args, EXTR_SKIP); + + if ( !current_user_can(SHOPP_USERLEVEL) ) + wp_die(__('You do not have sufficient permissions to access this page.','Shopp')); + + if ($page == "shopp-orders" + && !empty($deleting) + && !empty($selected) + && is_array($selected)) { + foreach($selected as $selection) { + $Purchase = new Purchase($selection); + $Purchase->load_purchased(); + foreach ($Purchase->purchased as $purchased) { + $Purchased = new Purchased($purchased->id); + $Purchased->delete(); + } + $Purchase->delete(); + } + } + + $statusLabels = $this->Settings->get('order_status'); + if (empty($statusLabels)) $statusLabels = array(''); + $txnStatusLabels = array( + 'PENDING' => __('Pending','Shopp'), + 'CHARGED' => __('Charged','Shopp'), + 'REFUNDED' => __('Refunded','Shopp'), + 'VOID' => __('Void','Shopp') + ); + + if ($update == "order" + && !empty($selected) + && is_array($selected)) { + foreach($selected as $selection) { + $Purchase = new Purchase($selection); + $Purchase->status = $newstatus; + $Purchase->save(); + } + } + + $Purchase = new Purchase(); + + if (!empty($start)) { + $startdate = $start; + list($month,$day,$year) = explode("/",$startdate); + $starts = mktime(0,0,0,$month,$day,$year); + } + if (!empty($end)) { + $enddate = $end; + list($month,$day,$year) = explode("/",$enddate); + $ends = mktime(23,59,59,$month,$day,$year); + } + + $pagenum = absint( $pagenum ); + if ( empty($pagenum) ) + $pagenum = 1; + if( !$per_page || $per_page < 0 ) + $per_page = 20; + $start = ($per_page * ($pagenum-1)); + + $where = ''; + if (!empty($status) || $status === '0') $where = "WHERE status='$status'"; + + if (!empty($s)) { + $s = stripslashes($s); + if (preg_match_all('/(\w+?)\:(?="(.+?)"|(.+?)\b)/',$s,$props,PREG_SET_ORDER) > 0) { + foreach ($props as $search) { + $keyword = !empty($search[2])?$search[2]:$search[3]; + switch(strtolower($search[1])) { + case "txn": $where .= (empty($where)?"WHERE ":" AND ")."transactionid='$keyword'"; break; + case "gateway": $where .= (empty($where)?"WHERE ":" AND ")."gateway LIKE '%$keyword%'"; break; + case "cardtype": $where .= ((empty($where))?"WHERE ":" AND ")."cardtype LIKE '%$keyword%'"; break; + case "address": $where .= ((empty($where))?"WHERE ":" AND ")."(address LIKE '%$keyword%' OR xaddress='%$keyword%')"; break; + case "city": $where .= ((empty($where))?"WHERE ":" AND ")."city LIKE '%$keyword%'"; break; + case "province": + case "state": $where .= ((empty($where))?"WHERE ":" AND ")."state='$keyword'"; break; + case "zip": + case "zipcode": + case "postcode": $where .= ((empty($where))?"WHERE ":" AND ")."postcode='$keyword'"; break; + case "country": $where .= ((empty($where))?"WHERE ":" AND ")."country='$keyword'"; break; + } + } + if (empty($where)) $where .= ((empty($where))?"WHERE ":" AND ")." (id='$s' OR CONCAT(firstname,' ',lastname) LIKE '%$s%')"; + } elseif (strpos($s,'@') !== false) { + $where .= ((empty($where))?"WHERE ":" AND ")." email='$s'"; + } else $where .= ((empty($where))?"WHERE ":" AND ")." (id='$s' OR CONCAT(firstname,' ',lastname) LIKE '%$s%')"; + } + if (!empty($starts) && !empty($ends)) $where .= ((empty($where))?"WHERE ":" AND ").' (UNIX_TIMESTAMP(created) >= '.$starts.' AND UNIX_TIMESTAMP(created) <= '.$ends.')'; + $ordercount = $db->query("SELECT count(*) as total,SUM(total) AS sales,AVG(total) AS avgsale FROM $Purchase->_table $where ORDER BY created DESC"); + $query = "SELECT * FROM $Purchase->_table $where ORDER BY created DESC LIMIT $start,$per_page"; + $Orders = $db->query($query,AS_ARRAY); + + $num_pages = ceil($ordercount->total / $per_page); + $page_links = paginate_links( array( + 'base' => add_query_arg( 'pagenum', '%#%' ), + 'format' => '', + 'total' => $num_pages, + 'current' => $pagenum + )); + + $ranges = array( + 'all' => __('Show All Orders','Shopp'), + 'today' => __('Today','Shopp'), + 'week' => __('This Week','Shopp'), + 'month' => __('This Month','Shopp'), + 'quarter' => __('This Quarter','Shopp'), + 'year' => __('This Year','Shopp'), + 'yesterday' => __('Yesterday','Shopp'), + 'lastweek' => __('Last Week','Shopp'), + 'last30' => __('Last 30 Days','Shopp'), + 'last90' => __('Last 3 Months','Shopp'), + 'lastmonth' => __('Last Month','Shopp'), + 'lastquarter' => __('Last Quarter','Shopp'), + 'lastyear' => __('Last Year','Shopp'), + 'lastexport' => __('Last Export','Shopp'), + 'custom' => __('Custom Dates','Shopp') + ); + + $exports = array( + 'tab' => __('Tab-separated.txt','Shopp'), + 'csv' => __('Comma-separated.csv','Shopp'), + 'xls' => __('Microsoft® Excel.xls','Shopp'), + 'iif' => __('Intuit® QuickBooks.iif','Shopp') + ); + + $formatPref = $Shopp->Settings->get('purchaselog_format'); + if (!$formatPref) $formatPref = 'tab'; + + $columns = array_merge(Purchase::exportcolumns(),Purchased::exportcolumns()); + $selected = $Shopp->Settings->get('purchaselog_columns'); + if (empty($selected)) $selected = array_keys($columns); + + include("{$this->basepath}/core/ui/orders/orders.php"); + } + + function orders_list_columns () { + shopp_register_column_headers('toplevel_page_shopp-orders', array( + 'cb'=>'', + 'order'=>__('Order','Shopp'), + 'name'=>__('Name','Shopp'), + 'destination'=>__('Destination','Shopp'), + 'total'=>__('Total','Shopp'), + 'txn'=>__('Transaction','Shopp'), + 'date'=>__('Date','Shopp')) + ); + } + + function order_manager () { + global $Shopp; + global $is_IIS; + + if ( !current_user_can(SHOPP_USERLEVEL) ) + wp_die(__('You do not have sufficient permissions to access this page.','Shopp')); + + if (preg_match("/\d+/",$_GET['id'])) { + $Shopp->Cart->data->Purchase = new Purchase($_GET['id']); + $Shopp->Cart->data->Purchase->load_purchased(); + } else $Shopp->Cart->data->Purchase = new Purchase(); + + $Purchase = $Shopp->Cart->data->Purchase; + $Customer = new Customer($Purchase->customer); + + if (!empty($_POST['update'])) { + check_admin_referer('shopp-save-order'); + + if ($_POST['transtatus'] != $Purchase->transtatus) + do_action_ref_array('shopp_order_txnstatus_update',array(&$_POST['transtatus'],&$Purchase)); + + $Purchase->updates($_POST); + if ($_POST['notify'] == "yes") { + $labels = $this->Settings->get('order_status'); + + // Send the e-mail notification + $notification = array(); + $notification['from'] = $Shopp->Settings->get('merchant_email'); + if($is_IIS) $notification['to'] = $Purchase->email; + else $notification['to'] = "\"{$Purchase->firstname} {$Purchase->lastname}\" <{$Purchase->email}>"; + $notification['subject'] = __('Order Updated','Shopp'); + $notification['url'] = get_bloginfo('siteurl'); + $notification['sitename'] = get_bloginfo('name'); + + if ($_POST['receipt'] == "yes") + $notification['receipt'] = $this->order_receipt(); + + $notification['status'] = strtoupper($labels[$Purchase->status]); + $notification['message'] = wpautop($_POST['message']); + + shopp_email(SHOPP_TEMPLATES."/notification.html",$notification); + + } + + $Purchase->save(); + $updated = __('Order status updated.','Shopp'); + } + + $targets = $this->Settings->get('target_markets'); + $txnStatusLabels = array( + 'PENDING' => __('Pending','Shopp'), + 'CHARGED' => __('Charged','Shopp'), + 'REFUNDED' => __('Refunded','Shopp'), + 'VOID' => __('Void','Shopp') + ); + + $statusLabels = $this->Settings->get('order_status'); + if (empty($statusLabels)) $statusLabels = array(''); + + + $taxrate = 0; + $base = $Shopp->Settings->get('base_operations'); + if ($base['vat']) $taxrate = $Shopp->Cart->taxrate(); + + + include("{$this->basepath}/core/ui/orders/order.php"); + } + + function order_status_counts () { + $db = DB::get(); + + $purchase_table = DatabaseObject::tablename(Purchase::$table); + $labels = $this->Settings->get('order_status'); + + if (empty($labels)) return false; + + $r = $db->query("SELECT status,COUNT(status) AS total FROM $purchase_table GROUP BY status ORDER BY status ASC",AS_ARRAY); + + $status = array(); + foreach ($r as $count) $status[$count->status] = $count->total; + foreach ($labels as $id => $label) if (empty($status[$id])) $status[$id] = 0; + return $status; + } + + function account () { + global $Shopp,$wp; + + if ($Shopp->Cart->data->login + && isset($Shopp->Cart->data->Order->Customer)) + $Shopp->Cart->data->Order->Customer->management(); + + if (isset($_GET['acct']) && $_GET['acct'] == "rp") $Shopp->Cart->data->Order->Customer->reset_password($_GET['key']); + if (isset($_POST['recover-login'])) $Shopp->Cart->data->Order->Customer->recovery(); + + ob_start(); + if (isset($wp->query_vars['shopp_download'])) include(SHOPP_TEMPLATES."/errors.php"); + elseif ($Shopp->Cart->data->login) include(SHOPP_TEMPLATES."/account.php"); + else include(SHOPP_TEMPLATES."/login.php"); + $content = ob_get_contents(); + ob_end_clean(); + + return apply_filters('shopp_account_template','
'.$content.'
'); + + } + + function customers_list () { + global $Shopp,$Customers,$wpdb; + $db = DB::get(); + + $defaults = array( + 'page' => false, + 'deleting' => false, + 'selected' => false, + 'update' => false, + 'newstatus' => false, + 'pagenum' => 1, + 'per_page' => false, + 'start' => '', + 'end' => '', + 'status' => false, + 's' => '', + 'range' => '', + 'startdate' => '', + 'enddate' => '', + ); + + $args = array_merge($defaults,$_GET); + extract($args, EXTR_SKIP); + + if ($page == "shopp-customers" + && !empty($deleting) + && !empty($selected) + && is_array($selected)) { + foreach($selected as $deletion) { + $Customer = new Customer($deletion); + $Billing = new Billing($Customer->id,'customer'); + $Billing->delete(); + $Shipping = new Shipping($Customer->id,'customer'); + $Shipping->delete(); + $Customer->delete(); + } + } + + if (!empty($_POST['save'])) { + check_admin_referer('shopp-save-customer'); + + if ($_POST['id'] != "new") { + $Customer = new Customer($_POST['id']); + $Billing = new Billing($Customer->id,'customer'); + $Shipping = new Shipping($Customer->id,'customer'); + } else $Customer = new Customer(); + + $Customer->updates($_POST); + + if (!empty($_POST['new-password']) && !empty($_POST['confirm-password']) + && $_POST['new-password'] == $_POST['confirm-password']) { + $Customer->password = wp_hash_password($_POST['new-password']); + if (!empty($Customer->wpuser)) wp_set_password($_POST['new-password'], $Customer->wpuser); + } + + $Customer->save(); + + $Billing->updates($_POST['billing']); + $Billing->save(); + + $Shipping->updates($_POST['shipping']); + $Shipping->save(); + + } + + $pagenum = absint( $pagenum ); + if ( empty($pagenum) ) + $pagenum = 1; + if( !$per_page || $per_page < 0 ) + $per_page = 20; + $index = ($per_page * ($pagenum-1)); + + if (!empty($start)) { + $startdate = $start; + list($month,$day,$year) = explode("/",$startdate); + $starts = mktime(0,0,0,$month,$day,$year); + } + if (!empty($end)) { + $enddate = $end; + list($month,$day,$year) = explode("/",$enddate); + $ends = mktime(23,59,59,$month,$day,$year); + } + + $customer_table = DatabaseObject::tablename(Customer::$table); + $billing_table = DatabaseObject::tablename(Billing::$table); + $purchase_table = DatabaseObject::tablename(Purchase::$table); + $users_table = $wpdb->users; + + $where = ''; + if (!empty($s)) { + $s = stripslashes($s); + if (preg_match_all('/(\w+?)\:(?="(.+?)"|(.+?)\b)/',$s,$props,PREG_SET_ORDER)) { + foreach ($props as $search) { + $keyword = !empty($search[2])?$search[2]:$search[3]; + switch(strtolower($search[1])) { + case "company": $where .= ((empty($where))?"WHERE ":" AND ")."c.company LIKE '%$keyword%'"; break; + case "login": $where .= ((empty($where))?"WHERE ":" AND ")."u.user_login LIKE '%$keyword%'"; break; + case "address": $where .= ((empty($where))?"WHERE ":" AND ")."(b.address LIKE '%$keyword%' OR b.xaddress='%$keyword%')"; break; + case "city": $where .= ((empty($where))?"WHERE ":" AND ")."b.city LIKE '%$keyword%'"; break; + case "province": + case "state": $where .= ((empty($where))?"WHERE ":" AND ")."b.state='$keyword'"; break; + case "zip": + case "zipcode": + case "postcode": $where .= ((empty($where))?"WHERE ":" AND ")."b.postcode='$keyword'"; break; + case "country": $where .= ((empty($where))?"WHERE ":" AND ")."b.country='$keyword'"; break; + } + } + } elseif (strpos($s,'@') !== false) { + $where .= ((empty($where))?"WHERE ":" AND ")."c.email='$s'"; + } else $where .= ((empty($where))?"WHERE ":" AND ")." (c.id='$s' OR CONCAT(c.firstname,' ',c.lastname) LIKE '%$s%' OR c.company LIKE '%$s%')"; + + } + if (!empty($starts) && !empty($ends)) $where .= ((empty($where))?"WHERE ":" AND ").' (UNIX_TIMESTAMP(c.created) >= '.$starts.' AND UNIX_TIMESTAMP(c.created) <= '.$ends.')'; + + $customercount = $db->query("SELECT count(*) as total FROM $customer_table AS c $where"); + $query = "SELECT c.*,b.city,b.state,b.country, u.user_login, SUM(p.total) AS total,count(distinct p.id) AS orders FROM $customer_table AS c LEFT JOIN $purchase_table AS p ON p.customer=c.id LEFT JOIN $billing_table AS b ON b.customer=c.id LEFT JOIN $users_table AS u ON u.ID=c.wpuser AND (c.wpuser IS NULL OR c.wpuser !=0) $where GROUP BY c.id ORDER BY c.created DESC LIMIT $index,$per_page"; + $Customers = $db->query($query,AS_ARRAY); + + $num_pages = ceil($customercount->total / $per_page); + $page_links = paginate_links( array( + 'base' => add_query_arg( 'pagenum', '%#%' ), + 'format' => '', + 'total' => $num_pages, + 'current' => $pagenum + )); + + $ranges = array( + 'all' => __('Show New Customers','Shopp'), + 'today' => __('Today','Shopp'), + 'week' => __('This Week','Shopp'), + 'month' => __('This Month','Shopp'), + 'quarter' => __('This Quarter','Shopp'), + 'year' => __('This Year','Shopp'), + 'yesterday' => __('Yesterday','Shopp'), + 'lastweek' => __('Last Week','Shopp'), + 'last30' => __('Last 30 Days','Shopp'), + 'last90' => __('Last 3 Months','Shopp'), + 'lastmonth' => __('Last Month','Shopp'), + 'lastquarter' => __('Last Quarter','Shopp'), + 'lastyear' => __('Last Year','Shopp'), + 'lastexport' => __('Last Export','Shopp'), + 'custom' => __('Custom Dates','Shopp') + ); + + $exports = array( + 'tab' => __('Tab-separated.txt','Shopp'), + 'csv' => __('Comma-separated.csv','Shopp'), + 'xls' => __('Microsoft® Excel.xls','Shopp') + ); + + + $formatPref = $Shopp->Settings->get('customerexport_format'); + if (!$formatPref) $formatPref = 'tab'; + + $columns = array_merge(Customer::exportcolumns(),Billing::exportcolumns(),Shipping::exportcolumns()); + $selected = $Shopp->Settings->get('customerexport_columns'); + if (empty($selected)) $selected = array_keys($columns); + + $authentication = $Shopp->Settings->get('account_system'); + + include("{$this->basepath}/core/ui/customers/customers.php"); + + } + + function customers_list_columns () { + shopp_register_column_headers('shopp_page_shopp-customers', array( + 'cb'=>'', + 'name'=>__('Name','Shopp'), + 'login'=>__('Login','Shopp'), + 'email'=>__('Email','Shopp'), + 'location'=>__('Location','Shopp'), + 'orders'=>__('Orders','Shopp'), + 'joined'=>__('Joined','Shopp')) + ); + + } + + function customer_editor_ui () { + global $Shopp; + include("{$this->basepath}/core/ui/customers/ui.php"); + } + + function customer_editor () { + global $Shopp,$Customer; + + if ( !current_user_can(SHOPP_USERLEVEL) ) + wp_die(__('You do not have sufficient permissions to access this page.')); + + + if ($_GET['id'] != "new") { + $Customer = new Customer($_GET['id']); + $Customer->Billing = new Billing($Customer->id,'customer'); + $Customer->Shipping = new Shipping($Customer->id,'customer'); + if (empty($Customer->id)) + wp_die(__('The requested customer record does not exist.','Shopp')); + } else $Customer = new Customer(); + + $countries = array(''=>''); + $countrydata = $Shopp->Settings->get('countries'); + foreach ($countrydata as $iso => $c) { + if (isset($_POST['settings']) && $_POST['settings']['base_operations']['country'] == $iso) + $base_region = $c['region']; + $countries[$iso] = $c['name']; + } + $Customer->countries = $countries; + + $regions = $Shopp->Settings->get('zones'); + $Customer->billing_states = array_merge(array(''),(array)$regions[$Customer->Billing->country]); + $Customer->shipping_states = array_merge(array(''),(array)$regions[$Customer->Shipping->country]); + + include("{$this->basepath}/core/ui/customers/editor.php"); + } + + /** + * Products admin flow handlers + **/ + function products_list($workflow=false) { + global $Products,$Shopp; + $db = DB::get(); + + if ( !current_user_can(SHOPP_USERLEVEL) ) + wp_die(__('You do not have sufficient permissions to access this page.')); + + $defaults = array( + 'cat' => false, + 'pagenum' => 1, + 'per_page' => 20, + 's' => '', + 'sl' => '', + 'matchcol' => '' + ); + + $args = array_merge($defaults,$_GET); + extract($args,EXTR_SKIP); + + if (!$workflow) { + if (empty($categories)) $categories = array(''); + + $category_table = DatabaseObject::tablename(Category::$table); + $query = "SELECT id,name,parent FROM $category_table ORDER BY parent,name"; + $categories = $db->query($query,AS_ARRAY); + $categories = sort_tree($categories); + if (empty($categories)) $categories = array(); + + $categories_menu = ''; + $categories_menu .= ''; + foreach ($categories as $category) { + $padding = str_repeat(" ",$category->depth*3); + if ($cat == $category->id) $categories_menu .= ''; + else $categories_menu .= ''; + } + $inventory_filters = array( + 'all' => __('View all products','Shopp'), + 'is' => __('In stock','Shopp'), + 'ls' => __('Low stock','Shopp'), + 'oos' => __('Out-of-stock','Shopp'), + 'ns' => __('Not stocked','Shopp') + ); + $inventory_menu = menuoptions($inventory_filters,$sl,true); + } + + $pagenum = absint( $pagenum ); + if ( empty($pagenum) ) + $pagenum = 1; + if( !$per_page || $per_page < 0 ) + $per_page = 20; + $start = ($per_page * ($pagenum-1)); + + $pd = DatabaseObject::tablename(Product::$table); + $pt = DatabaseObject::tablename(Price::$table); + $catt = DatabaseObject::tablename(Category::$table); + $clog = DatabaseObject::tablename(Catalog::$table); + + $orderby = "pd.created DESC"; + + $where = "true"; + $having = ""; + if (!empty($s)) { + if (strpos($s,"sku:") !== false) { // SKU search + $where .= ' AND pt.sku="'.substr($s,4).'"'; + $orderby = "pd.name"; + } else { // keyword search + $interference = array("'s","'",".","\""); + $search = preg_replace('/(\s?)(\w+)(\s?)/','\1*\2*\3',str_replace($interference,"", stripslashes($s))); + $match = "MATCH(pd.name,pd.summary,pd.description) AGAINST ('$search' IN BOOLEAN MODE)"; + $where .= " AND $match"; + $matchcol = ", $match AS score"; + $orderby = "score DESC"; + } + } + // if (!empty($cat)) $where .= " AND cat.id='$cat' AND (clog.category != 0 OR clog.id IS NULL)"; + if (!empty($cat)) { + if ($cat == "-") { + $having = "HAVING COUNT(cat.id) = 0"; + } else { + $matchcol .= ", GROUP_CONCAT(DISTINCT cat.id ORDER BY cat.id SEPARATOR ',') AS catids"; + $where .= " AND (clog.category != 0 OR clog.id IS NULL)"; + $having = "HAVING FIND_IN_SET('$cat',catids) > 0"; + } + } + if (!empty($sl)) { + switch($sl) { + case "ns": $where .= " AND pt.inventory='off'"; break; + case "oos": + $where .= " AND (pt.inventory='on')"; + $having .= (empty($having)?"HAVING ":" AND ")."SUM(pt.stock) = 0"; + break; + case "ls": + $ls = $Shopp->Settings->get('lowstock_level'); + if (empty($ls)) $ls = '0'; + $where .= " AND (pt.inventory='on' AND pt.stock <= $ls AND pt.stock > 0)"; + break; + case "is": $where .= " AND (pt.inventory='on' AND pt.stock > 0)"; + } + } + + $base = $Shopp->Settings->get('base_operations'); + if ($base['vat']) $taxrate = $Shopp->Cart->taxrate(); + if (empty($taxrate)) $taxrate = 0; + + $columns = "SQL_CALC_FOUND_ROWS pd.id,pd.name,pd.slug,pd.featured,pd.variations,GROUP_CONCAT(DISTINCT cat.name ORDER BY cat.name SEPARATOR ', ') AS categories,if(pt.options=0,IF(pt.tax='off',pt.price,pt.price+(pt.price*$taxrate)),-1) AS mainprice,IF(MAX(pt.tax)='off',MAX(pt.price),MAX(pt.price+(pt.price*$taxrate))) AS maxprice,IF(MAX(pt.tax)='off',MIN(pt.price),MIN(pt.price+(pt.price*$taxrate))) AS minprice,IF(pt.inventory='on','on','off') AS inventory,ROUND(SUM(pt.stock)/count(DISTINCT clog.id),0) AS stock"; + if ($workflow) $columns = "pd.id"; + // Load the products + $query = "SELECT $columns $matchcol FROM $pd AS pd LEFT JOIN $pt AS pt ON pd.id=pt.product AND pt.type != 'N/A' LEFT JOIN $clog AS clog ON pd.id=clog.product LEFT JOIN $catt AS cat ON cat.id=clog.category WHERE $where GROUP BY pd.id $having ORDER BY $orderby LIMIT $start,$per_page"; + $Products = $db->query($query,AS_ARRAY); + $productcount = $db->query("SELECT FOUND_ROWS() as total"); + + $num_pages = ceil($productcount->total / $per_page); + $page_links = paginate_links( array( + 'base' => add_query_arg(array("edit"=>null,'pagenum' => '%#%')), + 'format' => '', + 'total' => $num_pages, + 'current' => $pagenum, + )); + + if ($workflow) return $Products; + + include("{$this->basepath}/core/ui/products/products.php"); + } + + function products_list_columns () { + shopp_register_column_headers('shopp_page_shopp-products', array( + 'cb'=>'', + 'name'=>__('Name','Shopp'), + 'category'=>__('Category','Shopp'), + 'price'=>__('Price','Shopp'), + 'inventory'=>__('Inventory','Shopp'), + 'featured'=>__('Featured','Shopp')) + ); + } + + function product_shortcode ($atts) { + global $Shopp; + + if (isset($atts['name'])) { + $Shopp->Product = new Product($atts['name'],'name'); + } elseif (isset($atts['slug'])) { + $Shopp->Product = new Product($atts['slug'],'slug'); + } elseif (isset($atts['id'])) { + $Shopp->Product = new Product($atts['id']); + } else return ""; + + if (isset($atts['nowrap']) && value_is_true($atts['nowrap'])) + return $Shopp->Catalog->tag('product',$atts); + else return '
'.$Shopp->Catalog->tag('product',$atts).'
'; + } + + function category_shortcode ($atts) { + global $Shopp; + + $tag = 'category'; + if (isset($atts['name'])) { + $Shopp->Category = new Category($atts['name'],'name'); + unset($atts['name']); + } elseif (isset($atts['slug'])) { + switch ($atts['slug']) { + case SearchResults::$_slug: $tag = 'search-products'; unset($atts['slug']); + break; + case TagProducts::$_slug: $tag = 'tag-products'; unset($atts['slug']); + break; + case BestsellerProducts::$_slug: $tag = 'bestseller-products'; unset($atts['slug']); + break; + case CatalogProducts::$_slug: $tag = 'catalog-products'; unset($atts['slug']); + break; + case NewProducts::$_slug: $tag = 'new-products'; unset($atts['slug']); + break; + case FeaturedProducts::$_slug: $tag = 'featured-products'; unset($atts['slug']); + break; + case OnSaleProducts::$_slug: $tag = 'onsale-products'; unset($atts['slug']); + break; + case RandomProducts::$_slug: $tag = 'random-products'; unset($atts['slug']); + break; + } + } elseif (isset($atts['id'])) { + $Shopp->Category = new Category($atts['id']); + unset($atts['id']); + } else return ""; + + if (isset($atts['nowrap']) && value_is_true($atts['nowrap'])) + return $Shopp->Catalog->tag($tag,$atts); + else return '
'.$Shopp->Catalog->tag($tag,$atts).'
'; + + } + + function maintenance_shortcode ($atts) { + return '

The store is currently down for maintenance. We\'ll be back soon!

'; + } + + function product_editor_ui () { + global $Shopp; + include("{$this->basepath}/core/ui/products/ui.php"); + } + + function product_editor() { + global $Shopp; + $db = DB::get(); + + if ( !current_user_can(SHOPP_USERLEVEL) ) + wp_die(__('You do not have sufficient permissions to access this page.')); + + if (empty($Shopp->Product)) { + $Product = new Product(); + $Product->published = "on"; + } else $Product = $Shopp->Product; + + // $Product->load_data(array('images')); + // echo "
"; print_r($Product->imagesets); echo "
"; + + $Product->slug = apply_filters('editable_slug',$Product->slug); + $permalink = $Shopp->shopuri; + + require_once("{$this->basepath}/core/model/Asset.php"); + require_once("{$this->basepath}/core/model/Category.php"); + + $Price = new Price(); + $priceTypes = array( + array('value'=>'Shipped','label'=>__('Shipped','Shopp')), + array('value'=>'Virtual','label'=>__('Virtual','Shopp')), + array('value'=>'Download','label'=>__('Download','Shopp')), + array('value'=>'Donation','label'=>__('Donation','Shopp')), + array('value'=>'N/A','label'=>__('Disabled','Shopp')), + ); + + $workflows = array( + "continue" => __('Continue Editing','Shopp'), + "close" => __('Products Manager','Shopp'), + "new" => __('New Product','Shopp'), + "next" => __('Edit Next','Shopp'), + "previous" => __('Edit Previous','Shopp') + ); + + $taglist = array(); + foreach ($Product->tags as $tag) $taglist[] = $tag->name; + + if ($Product->id) { + $Assets = new Asset(); + $Images = $db->query("SELECT id,src,properties FROM $Assets->_table WHERE context='product' AND parent=$Product->id AND datatype='thumbnail' ORDER BY sortorder",AS_ARRAY); + unset($Assets); + } + + $shiprates = $this->Settings->get('shipping_rates'); + if (!empty($shiprates)) ksort($shiprates); + + $uploader = $Shopp->Settings->get('uploader_pref'); + if (!$uploader) $uploader = 'flash'; + + $process = (!empty($Product->id)?$Product->id:'new'); + $_POST['action'] = add_query_arg(array_merge($_GET,array('page'=>$this->Admin->products)),$Shopp->wpadminurl."admin.php"); + + include("{$this->basepath}/core/ui/products/editor.php"); + + } + + function save_product($Product) { + global $Shopp; + $db = DB::get(); + check_admin_referer('shopp-save-product'); + + if ( !current_user_can(SHOPP_USERLEVEL) ) + wp_die(__('You do not have sufficient permissions to access this page.')); + + $this->settings_save(); // Save workflow setting + + $base = $Shopp->Settings->get('base_operations'); + $taxrate = 0; + if ($base['vat']) $taxrate = $Shopp->Cart->taxrate(); + + if (!$_POST['options']) $Product->options = array(); + else $_POST['options'] = stripslashes_deep($_POST['options']); + + if (empty($Product->slug)) $Product->slug = sanitize_title_with_dashes($_POST['name']); + + // Check for an existing product slug + $exclude_product = !empty($Product->id)?"AND id != $Product->id":""; + $existing = $db->query("SELECT slug FROM $Product->_table WHERE slug='$Product->slug' $exclude_product LIMIT 1"); + if ($existing) { + $suffix = 2; + while($existing) { + $altslug = substr($Product->slug, 0, 200-(strlen($suffix)+1)). "-$suffix"; + $existing = $db->query("SELECT slug FROM $Product->_table WHERE slug='$altslug' $exclude_product LIMIT 1"); + $suffix++; + } + $Product->slug = $altslug; + } + + if (isset($_POST['content'])) $_POST['description'] = $_POST['content']; + $Product->updates($_POST,array('categories')); + $Product->save(); + + $Product->save_categories($_POST['categories']); + $Product->save_tags(explode(",",$_POST['taglist'])); + + if (!empty($_POST['price']) && is_array($_POST['price'])) { + + // Delete prices that were marked for removal + if (!empty($_POST['deletePrices'])) { + $deletes = array(); + if (strpos($_POST['deletePrices'],",")) $deletes = explode(',',$_POST['deletePrices']); + else $deletes = array($_POST['deletePrices']); + + foreach($deletes as $option) { + $Price = new Price($option); + $Price->delete(); + } + } + + // Save prices that there are updates for + foreach($_POST['price'] as $i => $option) { + if (empty($option['id'])) { + $Price = new Price(); + $option['product'] = $Product->id; + } else $Price = new Price($option['id']); + $option['sortorder'] = array_search($i,$_POST['sortorder'])+1; + + // Remove VAT amount to save in DB + if ($base['vat'] && $option['tax'] == "on") { + $option['price'] = number_format(floatnum($option['price'])/(1+$taxrate),2); + $option['saleprice'] = number_format(floatnum($option['saleprice'])/(1+$taxrate),2); + } + + $Price->updates($option); + $Price->save(); + if (!empty($option['download'])) $Price->attach_download($option['download']); + if (!empty($option['downloadpath'])) { + $basepath = trailingslashit($Shopp->Settings->get('products_path')); + $download = $basepath.ltrim($option['downloadpath'],"/"); + if (file_exists($download)) { + $File = new Asset(); + $File->parent = 0; + $File->context = "price"; + $File->datatype = "download"; + $File->name = basename($download); + $File->value = substr(dirname($download),strlen($basepath)); + $File->size = filesize($download); + $File->properties = array("mimetype" => file_mimetype($download,$File->name)); + $File->save(); + $Price->attach_download($File->id); + } + } + } + unset($Price); + } + + // No variation options at all, delete all variation-pricelines + if (empty($Product->options) && !empty($Product->prices) && is_array($Product->prices)) { + foreach ($Product->prices as $priceline) { + // Skip if not tied to variation options + if ($priceline->optionkey == 0) continue; + $Price = new Price($priceline->id); + $Price->delete(); + } + } + + if (!empty($_POST['details']) || !empty($_POST['deletedSpecs'])) { + $deletes = array(); + if (!empty($_POST['deletedSpecs'])) { + if (strpos($_POST['deletedSpecs'],",")) $deletes = explode(',',$_POST['deletedSpecs']); + else $deletes = array($_POST['deletedSpecs']); + foreach($deletes as $option) { + $Spec = new Spec($option); + $Spec->delete(); + } + unset($Spec); + } + + if (is_array($_POST['details'])) { + foreach ($_POST['details'] as $i => $spec) { + if (in_array($spec['id'],$deletes)) continue; + if (isset($spec['new'])) { + $Spec = new Spec(); + $spec['id'] = ''; + $spec['product'] = $Product->id; + } else $Spec = new Spec($spec['id']); + $spec['sortorder'] = array_search($i,$_POST['details-sortorder'])+1; + + $Spec->updates($spec); + if (preg_match('/^.*?(\d+[\.\,\d]*).*$/',$spec['content'])) + $Spec->numeral = preg_replace('/^.*?(\d+[\.\,\d]*).*$/','$1',$spec['content']); + + $Spec->save(); + } + } + } + + if (!empty($_POST['deleteImages'])) { + $deletes = array(); + if (strpos($_POST['deleteImages'],",")) $deletes = explode(',',$_POST['deleteImages']); + else $deletes = array($_POST['deleteImages']); + $Product->delete_images($deletes); + } + + if (!empty($_POST['images']) && is_array($_POST['images'])) { + $Product->link_images($_POST['images']); + $Product->save_imageorder($_POST['images']); + if (!empty($_POST['imagedetails'])) + $Product->update_images($_POST['imagedetails']); + } + + do_action_ref_array('shopp_product_saved',array(&$Product)); + + unset($Product); + return true; + } + + function product_downloads () { + $error = false; + if (isset($_FILES['Filedata']['error'])) $error = $_FILES['Filedata']['error']; + if ($error) die(json_encode(array("error" => $this->uploadErrors[$error]))); + + // Save the uploaded file + $File = new Asset(); + $File->parent = 0; + $File->context = "price"; + $File->datatype = "download"; + $File->name = $_FILES['Filedata']['name']; + $File->size = filesize($_FILES['Filedata']['tmp_name']); + $File->properties = array("mimetype" => file_mimetype($_FILES['Filedata']['tmp_name'],$File->name)); + $File->data = addslashes(file_get_contents($_FILES['Filedata']['tmp_name'])); + $File->save(); + unset($File->data); // Remove file contents from memory + + do_action('add_product_download',$File,$_FILES['Filedata']); + + echo json_encode(array("id"=>$File->id,"name"=>stripslashes($File->name),"type"=>$File->properties['mimetype'],"size"=>$File->size)); + } + + function add_images () { + $QualityValue = array(100,92,80,70,60); + + $error = false; + if (isset($_FILES['Filedata']['error'])) $error = $_FILES['Filedata']['error']; + if ($error) die(json_encode(array("error" => $this->uploadErrors[$error]))); + + require("{$this->basepath}/core/model/Image.php"); + + if (isset($_POST['product'])) { + $parent = $_POST['product']; + $context = "product"; + } + + if (isset($_POST['category'])) { + $parent = $_POST['category']; + $context = "category"; + } + + // Save the source image + $Image = new Asset(); + $Image->parent = $parent; + $Image->context = $context; + $Image->datatype = "image"; + $Image->name = $_FILES['Filedata']['name']; + list($width, $height, $mimetype, $attr) = getimagesize($_FILES['Filedata']['tmp_name']); + $Image->properties = array( + "width" => $width, + "height" => $height, + "mimetype" => image_type_to_mime_type($mimetype), + "attr" => $attr); + $Image->data = addslashes(file_get_contents($_FILES['Filedata']['tmp_name'])); + $Image->save(); + unset($Image->data); // Save memory for small image & thumbnail processing + + // Generate Small Size + $SmallSettings = array(); + $SmallSettings['width'] = $this->Settings->get('gallery_small_width'); + $SmallSettings['height'] = $this->Settings->get('gallery_small_height'); + $SmallSettings['sizing'] = $this->Settings->get('gallery_small_sizing'); + $SmallSettings['quality'] = $this->Settings->get('gallery_small_quality'); + + $Small = new Asset(); + $Small->parent = $Image->parent; + $Small->context = $context; + $Small->datatype = "small"; + $Small->src = $Image->id; + $Small->name = "small_".$Image->name; + $Small->data = file_get_contents($_FILES['Filedata']['tmp_name']); + $SmallSizing = new ImageProcessor($Small->data,$width,$height); + + switch ($SmallSettings['sizing']) { + // case "0": $SmallSizing->scaleToWidth($SmallSettings['width']); break; + // case "1": $SmallSizing->scaleToHeight($SmallSettings['height']); break; + case "0": $SmallSizing->scaleToFit($SmallSettings['width'],$SmallSettings['height']); break; + case "1": $SmallSizing->scaleCrop($SmallSettings['width'],$SmallSettings['height']); break; + } + $SmallSizing->UnsharpMask(75); + $Small->data = addslashes($SmallSizing->imagefile($QualityValue[$SmallSettings['quality']])); + $Small->properties = array(); + $Small->properties['width'] = $SmallSizing->Processed->width; + $Small->properties['height'] = $SmallSizing->Processed->height; + $Small->properties['mimetype'] = "image/jpeg"; + unset($SmallSizing); + $Small->save(); + unset($Small); + + // Generate Thumbnail + $ThumbnailSettings = array(); + $ThumbnailSettings['width'] = $this->Settings->get('gallery_thumbnail_width'); + $ThumbnailSettings['height'] = $this->Settings->get('gallery_thumbnail_height'); + $ThumbnailSettings['sizing'] = $this->Settings->get('gallery_thumbnail_sizing'); + $ThumbnailSettings['quality'] = $this->Settings->get('gallery_thumbnail_quality'); + + $Thumbnail = new Asset(); + $Thumbnail->parent = $Image->parent; + $Thumbnail->context = $context; + $Thumbnail->datatype = "thumbnail"; + $Thumbnail->src = $Image->id; + $Thumbnail->name = "thumbnail_".$Image->name; + $Thumbnail->data = file_get_contents($_FILES['Filedata']['tmp_name']); + $ThumbnailSizing = new ImageProcessor($Thumbnail->data,$width,$height); + + switch ($ThumbnailSettings['sizing']) { + // case "0": $ThumbnailSizing->scaleToWidth($ThumbnailSettings['width']); break; + // case "1": $ThumbnailSizing->scaleToHeight($ThumbnailSettings['height']); break; + case "0": $ThumbnailSizing->scaleToFit($ThumbnailSettings['width'],$ThumbnailSettings['height']); break; + case "1": $ThumbnailSizing->scaleCrop($ThumbnailSettings['width'],$ThumbnailSettings['height']); break; + } + $ThumbnailSizing->UnsharpMask(); + $Thumbnail->data = addslashes($ThumbnailSizing->imagefile($QualityValue[$ThumbnailSettings['quality']])); + $Thumbnail->properties = array(); + $Thumbnail->properties['width'] = $ThumbnailSizing->Processed->width; + $Thumbnail->properties['height'] = $ThumbnailSizing->Processed->height; + $Thumbnail->properties['mimetype'] = "image/jpeg"; + unset($ThumbnailSizing); + $Thumbnail->save(); + unset($Thumbnail->data); + + echo json_encode(array("id"=>$Thumbnail->id,"src"=>$Thumbnail->src)); + } + + /** + * Category flow handlers + **/ + function categories_list ($workflow=false) { + global $Shopp; + $db = DB::get(); + + if ( !current_user_can(SHOPP_USERLEVEL) ) + wp_die(__('You do not have sufficient permissions to access this page.')); + + $defaults = array( + 'pagenum' => 1, + 'per_page' => 20, + 's' => '' + ); + $args = array_merge($defaults,$_GET); + extract($args,EXTR_SKIP); + + $pagenum = absint( $pagenum ); + if ( empty($pagenum) ) + $pagenum = 1; + if( !$per_page || $per_page < 0 ) + $per_page = 20; + $start = ($per_page * ($pagenum-1)); + + $filters = array(); + // $filters['limit'] = "$start,$per_page"; + if (!empty($s)) + $filters['where'] = "cat.name LIKE '%$s%'"; + else $filters['where'] = "true"; + + $table = DatabaseObject::tablename(Category::$table); + $Catalog = new Catalog(); + $Catalog->outofstock = true; + if ($workflow) { + $filters['columns'] = "cat.id,cat.parent"; + $results = $Catalog->load_categories($filters,false,true); + return array_slice($results,$start,$per_page); + } else { + $filters['columns'] = "cat.id,cat.parent,cat.name,cat.description,cat.uri,cat.slug,cat.spectemplate,cat.facetedmenus,count(DISTINCT pd.id) AS total"; + + $Catalog->load_categories($filters); + $Categories = array_slice($Catalog->categories,$start,$per_page); + } + + $count = $db->query("SELECT count(*) AS total FROM $table"); + $num_pages = ceil($count->total / $per_page); + $page_links = paginate_links( array( + 'base' => add_query_arg( array('edit'=>null,'pagenum' => '%#%' )), + 'format' => '', + 'total' => $num_pages, + 'current' => $pagenum + )); + + include("{$this->basepath}/core/ui/categories/categories.php"); + } + + function categories_list_columns () { + shopp_register_column_headers('shopp_page_shopp-categories', array( + 'cb'=>'', + 'name'=>__('Name','Shopp'), + 'description'=>__('Description','Shopp'), + 'links'=>__('Products','Shopp'), + 'templates'=>__('Templates','Shopp'), + 'menus'=>__('Menus','Shopp')) + ); + } + + function category_editor_ui () { + global $Shopp; + include("{$this->basepath}/core/ui/categories/ui.php"); + } + + function category_editor () { + global $Shopp; + $db = DB::get(); + + if ( !current_user_can(SHOPP_USERLEVEL) ) + wp_die(__('You do not have sufficient permissions to access this page.')); + + if (empty($Shopp->Category)) $Category = new Category(); + else $Category = $Shopp->Category; + + $Price = new Price(); + $priceTypes = array( + array('value'=>'Shipped','label'=>__('Shipped','Shopp')), + array('value'=>'Virtual','label'=>__('Virtual','Shopp')), + array('value'=>'Download','label'=>__('Download','Shopp')), + array('value'=>'Donation','label'=>__('Donation','Shopp')), + array('value'=>'N/A','label'=>__('N/A','Shopp')) + ); + + + // Build permalink for slug editor + $permalink = trailingslashit($Shopp->link('catalog'))."category/"; + $Category->slug = apply_filters('editable_slug',$Category->slug); + if (!empty($Category->slug)) + $permalink .= substr($Category->uri,0,strpos($Category->uri,$Category->slug)); + + $pricerange_menu = array( + "disabled" => __('Price ranges disabled','Shopp'), + "auto" => __('Build price ranges automatically','Shopp'), + "custom" => __('Use custom price ranges','Shopp'), + ); + + $Images = array(); + if (!empty($Category->id)) { + $asset_table = DatabaseObject::tablename(Asset::$table); + $Images = $db->query("SELECT id,src,properties FROM $asset_table WHERE context='category' AND parent=$Category->id AND datatype='thumbnail' ORDER BY sortorder",AS_ARRAY); + } + + $categories_menu = $this->category_menu($Category->parent,$Category->id); + $categories_menu = ''.$categories_menu; + + $uploader = $Shopp->Settings->get('uploader_pref'); + if (!$uploader) $uploader = 'flash'; + + $workflows = array( + "continue" => __('Continue Editing','Shopp'), + "close" => __('Categories Manager','Shopp'), + "new" => __('New Category','Shopp'), + "next" => __('Edit Next','Shopp'), + "previous" => __('Edit Previous','Shopp') + ); + + include("{$this->basepath}/core/ui/categories/category.php"); + } + + function save_category ($Category) { + global $Shopp; + $db = DB::get(); + check_admin_referer('shopp-save-category'); + + if ( !current_user_can(SHOPP_USERLEVEL) ) + wp_die(__('You do not have sufficient permissions to access this page.')); + + $this->settings_save(); // Save workflow setting + + $Shopp->Catalog = new Catalog(); + $Shopp->Catalog->load_categories(array('where'=>'true')); + + if (!isset($_POST['slug']) && empty($Category->slug)) + $Category->slug = sanitize_title_with_dashes($_POST['name']); + if (isset($_POST['slug'])) unset($_POST['slug']); + + // Work out pathing + $paths = array(); + if (!empty($Category->slug)) $paths = array($Category->slug); // Include self + + $parentkey = -1; + // If we're saving a new category, lookup the parent + if ($_POST['parent'] > 0) { + array_unshift($paths,$Shopp->Catalog->categories[$_POST['parent']]->slug); + $parentkey = $Shopp->Catalog->categories[$_POST['parent']]->parent; + } + + while ($category_tree = $Shopp->Catalog->categories[$parentkey]) { + array_unshift($paths,$category_tree->slug); + $parentkey = $category_tree->parent; + } + + if (count($paths) > 1) $_POST['uri'] = join("/",$paths); + else $_POST['uri'] = $paths[0]; + + if (!empty($_POST['deleteImages'])) { + $deletes = array(); + if (strpos($_POST['deleteImages'],",")) $deletes = explode(',',$_POST['deleteImages']); + else $deletes = array($_POST['deleteImages']); + $Category->delete_images($deletes); + } + + if (!empty($_POST['images']) && is_array($_POST['images'])) { + $Category->link_images($_POST['images']); + $Category->save_imageorder($_POST['images']); + if (!empty($_POST['imagedetails']) && is_array($_POST['imagedetails'])) { + foreach($_POST['imagedetails'] as $i => $data) { + $Image = new Asset(); + unset($Image->_datatypes['data'],$Image->data); + $Image->load($data['id']); + $Image->properties['title'] = $data['title']; + $Image->properties['alt'] = $data['alt']; + $Image->save(); + } + } + } + + // Variation price templates + if (!empty($_POST['price']) && is_array($_POST['price'])) { + foreach ($_POST['price'] as &$pricing) { + $pricing['price'] = floatvalue($pricing['price']); + $pricing['saleprice'] = floatvalue($pricing['saleprice']); + $pricing['shipfee'] = floatvalue($pricing['shipfee']); + } + $Category->prices = stripslashes_deep($_POST['price']); + } else $Category->prices = array(); + + if (empty($_POST['specs'])) $Category->specs = array(); + else $_POST['specs'] = stripslashes_deep($_POST['specs']); + if (empty($_POST['options']) + || (count($_POST['options'])) == 1 && !isset($_POST['options'][1]['options'])) { + $_POST['options'] = $Category->options = array(); + $_POST['prices'] = $Category->prices = array(); + } else $_POST['options'] = stripslashes_deep($_POST['options']); + if (isset($_POST['content'])) $_POST['description'] = $_POST['content']; + + $Category->updates($_POST); + $Category->save(); + + do_action_ref_array('shopp_category_saved',array(&$Category)); + + $updated = ''.$Category->name.' '.__('category saved.','Shopp'); + + } + + function category_menu ($selection=false,$current=false) { + $db = DB::get(); + $table = DatabaseObject::tablename(Category::$table); + $categories = $db->query("SELECT id,name,parent FROM $table ORDER BY parent,name",AS_ARRAY); + $categories = sort_tree($categories); + + $options = ''; + foreach ($categories as $category) { + $padding = str_repeat(" ",$category->depth*3); + $selected = ($category->id == $selection)?' selected="selected"':''; + $disabled = ($current && $category->id == $current)?' disabled="disabled"':''; + $options .= ''; + } + return $options; + } + + function category_products () { + $db = DB::get(); + $catalog = DatabaseObject::tablename(Catalog::$table); + $category = DatabaseObject::tablename(Category::$table); + $products = DatabaseObject::tablename(Product::$table); + $results = $db->query("SELECT p.id,p.name FROM $catalog AS catalog LEFT JOIN $category AS cat ON cat.id = catalog.category LEFT JOIN $products AS p ON p.id=catalog.product WHERE cat.id={$_GET['category']} ORDER BY p.name ASC",AS_ARRAY); + $products = array(); + + $products[0] = __("Select a product…","Shopp"); + foreach ($results as $result) $products[$result->id] = $result->name; + return menuoptions($products,0,true); + + } + + function promotions_list () { + global $Shopp; + $db = DB::get(); + + if ( !current_user_can(SHOPP_USERLEVEL) ) + wp_die(__('You do not have sufficient permissions to access this page.')); + + require_once("{$this->basepath}/core/model/Promotion.php"); + + $defaults = array( + 'page' => false, + 'deleting' => false, + 'delete' => false, + 'pagenum' => 1, + 'per_page' => 20, + 's' => '' + ); + + $args = array_merge($defaults,$_GET); + extract($args,EXTR_SKIP); + + if ($page == "shopp-promotions" + && !empty($deleting) + && !empty($delete) + && is_array($delete)) { + foreach($delete as $deletion) { + $Promotion = new Promotion($deletion); + $Promotion->delete(); + } + } + + if (!empty($_POST['save'])) { + check_admin_referer('shopp-save-promotion'); + + if ($_POST['id'] != "new") { + $Promotion = new Promotion($_POST['id']); + } else $Promotion = new Promotion(); + + if (!empty($_POST['starts']['month']) && !empty($_POST['starts']['date']) && !empty($_POST['starts']['year'])) + $_POST['starts'] = mktime(0,0,0,$_POST['starts']['month'],$_POST['starts']['date'],$_POST['starts']['year']); + else $_POST['starts'] = 1; + + if (!empty($_POST['ends']['month']) && !empty($_POST['ends']['date']) && !empty($_POST['ends']['year'])) + $_POST['ends'] = mktime(23,59,59,$_POST['ends']['month'],$_POST['ends']['date'],$_POST['ends']['year']); + else $_POST['ends'] = 1; + if (isset($_POST['rules'])) $_POST['rules'] = stripslashes_deep($_POST['rules']); + + $Promotion->updates($_POST); + $Promotion->save(); + + do_action_ref_array('shopp_promo_saved',array(&$Promotion)); + + if ($Promotion->scope == "Catalog") + $Promotion->build_discounts(); + + // Reset cart promotions cache + // to force reload for these updates + $Shopp->Cart->data->Promotions = false; + } + + $pagenum = absint( $pagenum ); + if ( empty($pagenum) ) + $pagenum = 1; + if( !$per_page || $per_page < 0 ) + $per_page = 20; + $start = ($per_page * ($pagenum-1)); + + + $where = ""; + if (!empty($s)) $where = "WHERE name LIKE '%$s%'"; + + $table = DatabaseObject::tablename(Promotion::$table); + $promocount = $db->query("SELECT count(*) as total FROM $table $where"); + $Promotions = $db->query("SELECT * FROM $table $where",AS_ARRAY); + + $status = array( + 'enabled' => __('Enabled','Shopp'), + 'disabled' => __('Disabled','Shopp') + ); + + $num_pages = ceil($promocount->total / $per_page); + $page_links = paginate_links( array( + 'base' => add_query_arg( 'pagenum', '%#%' ), + 'format' => '', + 'total' => $num_pages, + 'current' => $pagenum + )); + + include("{$this->basepath}/core/ui/promotions/promotions.php"); + } + + function promotions_list_columns () { + shopp_register_column_headers('shopp_page_shopp-promotions', array( + 'cb'=>'', + 'name'=>__('Name','Shopp'), + 'discount'=>__('Discount','Shopp'), + 'applied'=>__('Applied To','Shopp'), + 'eff'=>__('Status','Shopp')) + ); + } + + function promotion_editor_ui () { + global $Shopp; + include("{$this->basepath}/core/ui/promotions/ui.php"); + } + + function promotion_editor () { + global $Shopp; + + if ( !current_user_can(SHOPP_USERLEVEL) ) + wp_die(__('You do not have sufficient permissions to access this page.')); + + require_once("{$this->basepath}/core/model/Promotion.php"); + + if ($_GET['id'] != "new") { + $Promotion = new Promotion($_GET['id']); + } else $Promotion = new Promotion(); + + $scopes = array( + 'Catalog' => __('Catalog','Shopp'), + 'Order' => __('Order','Shopp') + ); + + $types = array( + 'Percentage Off' => __('Percentage Off','Shopp'), + 'Amount Off' => __('Amount Off','Shopp'), + 'Free Shipping' => __('Free Shipping','Shopp'), + 'Buy X Get Y Free' => __('Buy X Get Y Free','Shopp') + ); + + include("{$this->basepath}/core/ui/promotions/editor.php"); + } + + /** + * Dashboard Widgets + */ + function dashboard_stats ($args=null) { + global $Shopp; + $db = DB::get(); + $defaults = array( + 'before_widget' => '', + 'before_title' => '', + 'widget_name' => '', + 'after_title' => '', + 'after_widget' => '' + ); + if (!$args) $args = array(); + $args = array_merge($defaults,$args); + if (!empty($args)) extract( $args, EXTR_SKIP ); + + echo $before_widget; + + echo $before_title; + echo $widget_name; + echo $after_title; + + $purchasetable = DatabaseObject::tablename(Purchase::$table); + + $results = $db->query("SELECT count(id) AS orders, SUM(total) AS sales, AVG(total) AS average, + SUM(IF(UNIX_TIMESTAMP(created) > UNIX_TIMESTAMP()-(86400*30),1,0)) AS wkorders, + SUM(IF(UNIX_TIMESTAMP(created) > UNIX_TIMESTAMP()-(86400*30),total,0)) AS wksales, + AVG(IF(UNIX_TIMESTAMP(created) > UNIX_TIMESTAMP()-(86400*30),total,null)) AS wkavg + FROM $purchasetable"); + + $orderscreen = add_query_arg('page',$this->Admin->orders,$Shopp->wpadminurl."admin.php"); + echo '
'; + echo ''; + + echo ''; + echo ''; + + echo ''; + echo ''; + + echo ''; + echo ''; + + echo '
'.__('Last 30 Days','Shopp').''.__('Lifetime','Shopp').'
'.$results->wkorders.''.__('Orders','Shopp').''.$results->orders.''.__('Orders','Shopp').'
'.money($results->wksales).''.__('Sales','Shopp').''.money($results->sales).''.__('Sales','Shopp').'
'.money($results->wkavg).''.__('Average Order','Shopp').''.money($results->average).''.__('Average Order','Shopp').'
'; + + echo $after_widget; + + } + + function dashboard_orders ($args=null) { + global $Shopp; + $db = DB::get(); + $defaults = array( + 'before_widget' => '', + 'before_title' => '', + 'widget_name' => '', + 'after_title' => '', + 'after_widget' => '' + ); + if (!$args) $args = array(); + $args = array_merge($defaults,$args); + if (!empty($args)) extract( $args, EXTR_SKIP ); + $statusLabels = $this->Settings->get('order_status'); + + echo $before_widget; + + echo $before_title; + echo $widget_name; + echo $after_title; + + $purchasetable = DatabaseObject::tablename(Purchase::$table); + $purchasedtable = DatabaseObject::tablename(Purchased::$table); + + $Orders = $db->query("SELECT p.*,count(i.id) as items FROM $purchasetable AS p LEFT JOIN $purchasedtable AS i ON i.purchase=p.id GROUP BY i.purchase ORDER BY created DESC LIMIT 6",AS_ARRAY); + + if (!empty($Orders)) { + echo ''; + echo ''; + echo ''; + $even = false; + foreach ($Orders as $Order) { + echo ''; + $even = !$even; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + echo '
'.__('Name','Shopp').''.__('Date','Shopp').''.__('Items','Shopp').''.__('Total','Shopp').''.__('Status','Shopp').'
'.((empty($Order->firstname) && empty($Order->lastname))?'(no contact name)':$Order->firstname.' '.$Order->lastname).''.date("Y/m/d",mktimestamp($Order->created)).''.$Order->items.''.money($Order->total).''.$statusLabels[$Order->status].'
'; + } else { + echo '

'.__('No orders, yet.','Shopp').'

'; + } + + echo $after_widget; + + } + + function dashboard_products ($args=null) { + global $Shopp; + $db = DB::get(); + $defaults = array( + 'before_widget' => '', + 'before_title' => '', + 'widget_name' => '', + 'after_title' => '', + 'after_widget' => '' + ); + + if (!$args) $args = array(); + $args = array_merge($defaults,$args); + if (!empty($args)) extract( $args, EXTR_SKIP ); + + echo $before_widget; + + echo $before_title; + echo $widget_name; + echo $after_title; + + $RecentBestsellers = new BestsellerProducts(array('where'=>'UNIX_TIMESTAMP(pur.created) > UNIX_TIMESTAMP()-(86400*30)','show'=>3)); + $RecentBestsellers->load_products(); + + echo ''; + echo ''; + + + $LifetimeBestsellers = new BestsellerProducts(array('show'=>3)); + $LifetimeBestsellers->load_products(); + echo ''; + echo '

'.__('Recent Bestsellers','Shopp').'

'; + echo '
    '; + foreach ($RecentBestsellers->products as $product) + echo '
  • '.$product->name.' ('.$product->sold.')
  • '; + echo '

'.__('Lifetime Bestsellers','Shopp').'

'; + echo '
    '; + foreach ($LifetimeBestsellers->products as $product) + echo '
  • '.$product->name.' ('.$product->sold.')
  • '; + echo '
'; + echo $after_widget; + + } + + /** + * Settings flow handlers + **/ + + function settings_general () { + global $Shopp; + if ( !current_user_can('manage_options') ) + wp_die(__('You do not have sufficient permissions to access this page.')); + + $country = (isset($_POST['settings']))?$_POST['settings']['base_operations']['country']:''; + $countries = array(); + $countrydata = $Shopp->Settings->get('countries'); + foreach ($countrydata as $iso => $c) { + if (isset($_POST['settings']) && $_POST['settings']['base_operations']['country'] == $iso) + $base_region = $c['region']; + $countries[$iso] = $c['name']; + } + + if (!empty($_POST['setup'])) { + $_POST['settings']['display_welcome'] = "off"; + $this->settings_save(); + } + + if (!empty($_POST['save'])) { + check_admin_referer('shopp-settings-general'); + $vat_countries = $Shopp->Settings->get('vat_countries'); + $zone = $_POST['settings']['base_operations']['zone']; + $_POST['settings']['base_operations'] = $countrydata[$_POST['settings']['base_operations']['country']]; + $_POST['settings']['base_operations']['country'] = $country; + $_POST['settings']['base_operations']['zone'] = $zone; + $_POST['settings']['base_operations']['currency']['format'] = + scan_money_format($_POST['settings']['base_operations']['currency']['format']); + if (in_array($_POST['settings']['base_operations']['country'],$vat_countries)) + $_POST['settings']['base_operations']['vat'] = true; + else $_POST['settings']['base_operations']['vat'] = false; + + $this->settings_save(); + $updated = __('Shopp settings saved.'); + } + + $operations = $Shopp->Settings->get('base_operations'); + if (!empty($operations['zone'])) { + $zones = $Shopp->Settings->get('zones'); + $zones = $zones[$operations['country']]; + } + + $targets = $Shopp->Settings->get('target_markets'); + if (!$targets) $targets = array(); + + $statusLabels = $Shopp->Settings->get('order_status'); + include(SHOPP_ADMINPATH."/settings/settings.php"); + } + + function settings_presentation () { + if ( !current_user_can('manage_options') ) + wp_die(__('You do not have sufficient permissions to access this page.')); + + if (isset($_POST['settings']['theme_templates']) && $_POST['settings']['theme_templates'] == "on") + $_POST['settings']['theme_templates'] = addslashes(template_path(STYLESHEETPATH.DIRECTORY_SEPARATOR."shopp")); + if (!empty($_POST['save'])) { + check_admin_referer('shopp-settings-presentation'); + if (empty($_POST['settings']['catalog_pagination'])) + $_POST['settings']['catalog_pagination'] = 0; + $this->settings_save(); + $updated = __('Shopp presentation settings saved.'); + } + + $builtin_path = $this->basepath.DIRECTORY_SEPARATOR."templates"; + $theme_path = template_path(STYLESHEETPATH.DIRECTORY_SEPARATOR."shopp"); + + // Copy templates to the current WordPress theme + if (!empty($_POST['install'])) { + check_admin_referer('shopp-settings-presentation'); + copy_shopp_templates($builtin_path,$theme_path); + } + + $status = "available"; + if (!is_dir($theme_path)) $status = "directory"; + else { + if (!is_writable($theme_path)) $status = "permissions"; + else { + $builtin = array_filter(scandir($builtin_path),"filter_dotfiles"); + $theme = array_filter(scandir($theme_path),"filter_dotfiles"); + if (empty($theme)) $status = "ready"; + else if (array_diff($builtin,$theme)) $status = "incomplete"; + } + } + + $category_views = array("grid" => __('Grid','Shopp'),"list" => __('List','Shopp')); + $row_products = array(2,3,4,5,6,7); + $productOrderOptions = Category::sortoptions(); + + $orderOptions = array("ASC" => __('Order','Shopp'), + "DESC" => __('Reverse Order','Shopp'), + "RAND" => __('Shuffle','Shopp')); + + $orderBy = array("sortorder" => __('Custom arrangement','Shopp'), + "name" => __('File name','Shopp'), + "created" => __('Upload date','Shopp')); + + $sizingOptions = array( __('Scale to fit','Shopp'), + __('Scale & crop','Shopp')); + + $qualityOptions = array(__('Highest quality, largest file size','Shopp'), + __('Higher quality, larger file size','Shopp'), + __('Balanced quality & file size','Shopp'), + __('Lower quality, smaller file size','Shopp'), + __('Lowest quality, smallest file size','Shopp')); + + include(SHOPP_ADMINPATH."/settings/presentation.php"); + } + + function settings_catalog () { + // check_admin_referer('shopp-settings-catalog'); + if ( !current_user_can('manage_options') ) + wp_die(__('You do not have sufficient permissions to access this page.')); + + if (!empty($_POST['save'])) $this->settings_save(); + include(SHOPP_ADMINPATH."/settings/catalog.php"); + } + + function settings_cart () { + if ( !current_user_can('manage_options') ) + wp_die(__('You do not have sufficient permissions to access this page.')); + + if (!empty($_POST['save'])) $this->settings_save(); + include(SHOPP_ADMINPATH."/settings/cart.php"); + } + + function settings_checkout () { + global $Shopp; + $db =& DB::get(); + if ( !current_user_can('manage_options') ) + wp_die(__('You do not have sufficient permissions to access this page.')); + + $purchasetable = DatabaseObject::tablename(Purchase::$table); + $next = $db->query("SELECT IF ((MAX(id)) > 0,(MAX(id)+1),1) AS id FROM $purchasetable LIMIT 1"); + $next_setting = $Shopp->Settings->get('next_order_id'); + + if ($next->id > $next_setting) $next_setting = $next->id; + + if (!empty($_POST['save'])) { + check_admin_referer('shopp-settings-checkout'); + if ($_POST['settings']['next_order_id'] != $next->id) { + if ($db->query("ALTER TABLE $purchasetable AUTO_INCREMENT={$_POST['settings']['next_order_id']}")) + $next->id = $_POST['settings']['next_order_id']; + } + + $this->settings_save(); + $updated = __('Shopp checkout settings saved.','Shopp'); + } + + + $downloads = array("1","2","3","5","10","15","25","100"); + $promolimit = array("1","2","3","4","5","6","7","8","9","10","15","20","25"); + $time = array( + '1800' => __('30 minutes','Shopp'), + '3600' => __('1 hour','Shopp'), + '7200' => __('2 hours','Shopp'), + '10800' => __('3 hours','Shopp'), + '21600' => __('6 hours','Shopp'), + '43200' => __('12 hours','Shopp'), + '86400' => __('1 day','Shopp'), + '172800' => __('2 days','Shopp'), + '259200' => __('3 days','Shopp'), + '604800' => __('1 week','Shopp'), + '2678400' => __('1 month','Shopp'), + '7952400' => __('3 months','Shopp'), + '15901200' => __('6 months','Shopp'), + '31536000' => __('1 year','Shopp'), + ); + + include(SHOPP_ADMINPATH."/settings/checkout.php"); + } + + function settings_shipping () { + global $Shopp; + + if ( !current_user_can('manage_options') ) + wp_die(__('You do not have sufficient permissions to access this page.')); + + if (!empty($_POST['save'])) { + check_admin_referer('shopp-settings-shipping'); + + // Sterilize $values + foreach ($_POST['settings']['shipping_rates'] as $i => &$method) { + $method['name'] = stripslashes($method['name']); + foreach ($method as $key => &$mr) { + if (!is_array($mr)) continue; + foreach ($mr as $id => &$v) { + if ($v == ">" || $v == "+" || $key == "services") continue; + $v = floatnum($v); + } + } + } + + $_POST['settings']['order_shipfee'] = floatnum($_POST['settings']['order_shipfee']); + + $this->settings_save(); + $updated = __('Shipping settings saved.','Shopp'); + $Shopp->ShipCalcs = new ShipCalcs($Shopp->path); + $rates = $Shopp->Settings->get('shipping_rates'); + + $Errors = &ShoppErrors(); + foreach ($rates as $rate) { + $process = ''; + $ShipCalcClass = $rate['method']; + if (strpos($rate['method'],'::') != false) + list($ShipCalcClass,$process) = explode("::",$rate['method']); + + if (isset($Shopp->ShipCalcs->modules[$ShipCalcClass]->requiresauth) + && $Shopp->ShipCalcs->modules[$ShipCalcClass]->requiresauth) { + $Shopp->ShipCalcs->modules[$ShipCalcClass]->verifyauth(); + if ($Errors->exist()) $autherrors = $Errors->get(); + } + } + + if (!empty($autherrors)) { + $updated = __('Shipping settings saved but there were errors: ','Shopp'); + foreach ((array)$autherrors as $error) $updated .= '

'.$error->message().'

'; + $Errors->reset(); + } + + } + + $methods = $Shopp->ShipCalcs->methods; + $base = $Shopp->Settings->get('base_operations'); + $regions = $Shopp->Settings->get('regions'); + $region = $regions[$base['region']]; + $useRegions = $Shopp->Settings->get('shipping_regions'); + + $areas = $Shopp->Settings->get('areas'); + if (is_array($areas[$base['country']]) && $useRegions == "on") + $areas = array_keys($areas[$base['country']]); + else $areas = array($base['country'] => $base['name']); + unset($countries,$regions); + + $rates = $Shopp->Settings->get('shipping_rates'); + if (!empty($rates)) ksort($rates); + + $lowstock = $Shopp->Settings->get('lowstock_level'); + if (empty($lowstock)) $lowstock = 0; + + include(SHOPP_ADMINPATH."/settings/shipping.php"); + } + + function settings_taxes () { + if ( !current_user_can('manage_options') ) + wp_die(__('You do not have sufficient permissions to access this page.')); + + if (!empty($_POST['save'])) { + check_admin_referer('shopp-settings-taxes'); + $this->settings_save(); + $updated = __('Shopp taxes settings saved.','Shopp'); + } + + $rates = $this->Settings->get('taxrates'); + $base = $this->Settings->get('base_operations'); + + $countries = array_merge(array('*' => __('All Markets','Shopp')), + $this->Settings->get('target_markets')); + + + $zones = $this->Settings->get('zones'); + + include(SHOPP_ADMINPATH."/settings/taxes.php"); + } + + function settings_payments () { + global $Shopp; + if ( !current_user_can('manage_options') ) + wp_die(__('You do not have sufficient permissions to access this page.')); + + // $gateway_dir = SHOPP_PATH.DIRECTORY_SEPARATOR."gateways".DIRECTORY_SEPARATOR; + $payment_gateway = gateway_path($this->Settings->get('payment_gateway')); + + if (!empty($_POST['save'])) { + check_admin_referer('shopp-settings-payments'); + + // Update the accepted credit card payment methods + if (!empty($_POST['settings']['payment_gateway']) + && file_exists(SHOPP_GATEWAYS.$_POST['settings']['payment_gateway'])) { + $gateway = $this->scan_gateway_meta(SHOPP_GATEWAYS.$_POST['settings']['payment_gateway']); + $ProcessorClass = $gateway->tags['class']; + // Load the gateway in case there are any save-time processes to be run + $Processor = $Shopp->gateway($_POST['settings']['payment_gateway'],true); + $_POST['settings']['gateway_cardtypes'] = $_POST['settings'][$ProcessorClass]['cards']; + } + if (is_array($_POST['settings']['xco_gateways'])) { + foreach($_POST['settings']['xco_gateways'] as &$gateway) { + $gateway = str_replace("\\","/",stripslashes($gateway)); + if (!file_exists(SHOPP_GATEWAYS.$gateway)) continue; + $meta = $this->scan_gateway_meta(SHOPP_GATEWAYS.$gateway); + $_POST['settings'][$ProcessorClass]['path'] = str_replace("\\","/",stripslashes($_POST['settings'][$ProcessorClass]['path'])); + $ProcessorClass = $meta->tags['class']; + // Load the gateway in case there are any save-time processes to be run + $Processor = $Shopp->gateway($gateway); + } + } + + do_action('shopp_save_payment_settings'); + + $this->settings_save(); + $payment_gateway = stripslashes($this->Settings->get('payment_gateway')); + + $updated = __('Shopp payments settings saved.','Shopp'); + } + + + // Get all of the installed gateways + $data = $this->settings_get_gateways(); + + $gateways = array(); + $LocalProcessors = array(); + $XcoProcessors = array(); + foreach ($data as $gateway) { + $ProcessorClass = $gateway->tags['class']; + include_once($gateway->file); + $processor = new $ProcessorClass(); + if (isset($processor->type) && strtolower($processor->type) == "xco") { + $XcoProcessors[] = $processor; + } else { + $gateways[gateway_path($gateway->file)] = $gateway->name; + $LocalProcessors[] = $processor; + } + } + + include(SHOPP_ADMINPATH."/settings/payments.php"); + } + + function settings_update () { + global $Shopp; + + if ( !current_user_can('manage_options') ) + wp_die(__('You do not have sufficient permissions to access this page.')); + + $ftpsupport = (function_exists('ftp_connect'))?true:false; + + $credentials = $this->Settings->get('ftp_credentials'); + if (!isset($credentials['password'])) $credentials['password'] = false; + $updatekey = $this->Settings->get('updatekey'); + if (empty($updatekey)) + $updatekey = array('key' => '','type' => 'single','status' => 'deactivated'); + + if (!empty($_POST['save'])) { + $updatekey['key'] = $_POST['updatekey']; + $_POST['settings']['updatekey'] = $updatekey; + $this->settings_save(); + } + + if (!empty($_POST['activation'])) { + check_admin_referer('shopp-settings-update'); + $updatekey['key'] = trim($_POST['updatekey']); + $_POST['settings']['updatekey'] = $updatekey; + + $this->settings_save(); + + $request = array( + "ShoppServerRequest" => $_POST['process'], + "ver" => '1.0', + "key" => $updatekey['key'], + "type" => $updatekey['type'], + "site" => get_bloginfo('siteurl') + ); + + $response = $this->callhome($request); + $response = explode("::",$response); + + if (count($response) == 1) + $activation = ''.$response[0].''; + + if ($_POST['process'] == "activate-key" && $response[0] == "1") { + $updatekey['type'] = $response[1]; + $type = $updatekey['type']; + $updatekey['key'] = $response[2]; + $updatekey['status'] = 'activated'; + $this->Settings->save('updatekey',$updatekey); + $activation = __('This key has been successfully activated.','Shopp'); + } + + if ($_POST['process'] == "deactivate-key" && $response[0] == "1") { + $updatekey['status'] = 'deactivated'; + if ($updatekey['type'] == "dev") $updatekey['key'] = ''; + $this->Settings->save('updatekey',$updatekey); + $activation = __('This key has been successfully de-activated.','Shopp'); + } + } else { + if ($updatekey['status'] == "activated") + $activation = __('This key has been successfully activated.','Shopp'); + else $activation = __('Enter your Shopp upgrade key and activate it to enable easy, automatic upgrades.','Shopp'); + } + + $type = "text"; + if ($updatekey['status'] == "activated" && $updatekey['type'] == "dev") $type = "password"; + + include(SHOPP_ADMINPATH."/settings/update.php"); + } + + function settings_system () { + global $Shopp; + if ( !current_user_can('manage_options') ) + wp_die(__('You do not have sufficient permissions to access this page.')); + + $error = false; + + // Image path processing + if (isset($_POST['settings']) && isset($_POST['settings']['image_storage_pref'])) + $_POST['settings']['image_storage'] = $_POST['settings']['image_storage_pref']; + $imagepath = $this->Settings->get('image_path'); + if (isset($_POST['settings']['products_path'])) $imagepath = $_POST['settings']['image_path']; + $imagepath_status = __("File system image hosting is enabled and working.","Shopp"); + if (!file_exists($imagepath)) $error = __("The current path does not exist. Using database instead.","Shopp"); + if (!is_dir($imagepath)) $error = __("The file path supplied is not a directory. Using database instead.","Shopp"); + if (!is_writable($imagepath) || !is_readable($imagepath)) + $error = __("Permissions error. This path must be writable by the web server. Using database instead.","Shopp"); + if (empty($imagepath)) $error = __("Enter the absolute path starting from the root of the server file system to your image storage directory.","Shopp"); + if ($error) { + $_POST['settings']['image_storage'] = 'db'; + $imagepath_status = ''.$error.''; + } + + // Product path processing + if (isset($_POST['settings']) && isset($_POST['settings']['product_storage_pref'])) + $_POST['settings']['product_storage'] = $_POST['settings']['product_storage_pref']; + $productspath = $this->Settings->get('products_path'); + if (isset($_POST['settings']['products_path'])) $productspath = $_POST['settings']['products_path']; + $error = ""; // Reset the error tracker + $productspath_status = __("File system product file hosting is enabled and working.","Shopp"); + if (!file_exists($productspath)) $error = __("The current path does not exist. Using database instead.","Shopp"); + if (!is_dir($productspath)) $error = __("The file path supplied is not a directory. Using database instead.","Shopp"); + if (!is_writable($productspath) || !is_readable($productspath)) + $error = __("Permissions error. This path must be writable by the web server. Using database instead.","Shopp"); + if (empty($productspath)) $error = __("Enter the absolute path starting from the root of the server file system to your product file storage directory.","Shopp"); + if ($error) { + $_POST['settings']['product_storage'] = 'db'; + $productspath_status = ''.$error.''; + } + + if (!empty($_POST['save'])) { + check_admin_referer('shopp-settings-system'); + + if (!isset($_POST['settings']['error_notifications'])) + $_POST['settings']['error_notifications'] = array(); + + $this->settings_save(); + + // Reinitialize Error System + $Shopp->Cart->data->Errors = new ShoppErrors(); + $Shopp->ErrorLog = new ShoppErrorLogging($this->Settings->get('error_logging')); + $Shopp->ErrorNotify = new ShoppErrorNotification($this->Settings->get('merchant_email'), + $this->Settings->get('error_notifications')); + + $updated = __('Shopp system settings saved.','Shopp'); + } + + if (isset($_POST['resetlog'])) $Shopp->ErrorLog->reset(); + + $notifications = $this->Settings->get('error_notifications'); + if (empty($notifications)) $notifications = array(); + + $notification_errors = array( + SHOPP_TRXN_ERR => __("Transaction Errors","Shopp"), + SHOPP_AUTH_ERR => __("Login Errors","Shopp"), + SHOPP_ADDON_ERR => __("Add-on Errors","Shopp"), + SHOPP_COMM_ERR => __("Communication Errors","Shopp"), + SHOPP_STOCK_ERR => __("Inventory Warnings","Shopp") + ); + + $errorlog_levels = array( + 0 => __("Disabled","Shopp"), + SHOPP_ERR => __("General Shopp Errors","Shopp"), + SHOPP_TRXN_ERR => __("Transaction Errors","Shopp"), + SHOPP_AUTH_ERR => __("Login Errors","Shopp"), + SHOPP_ADDON_ERR => __("Add-on Errors","Shopp"), + SHOPP_COMM_ERR => __("Communication Errors","Shopp"), + SHOPP_STOCK_ERR => __("Inventory Warnings","Shopp"), + SHOPP_ADMIN_ERR => __("Admin Errors","Shopp"), + SHOPP_DB_ERR => __("Database Errors","Shopp"), + SHOPP_PHP_ERR => __("PHP Errors","Shopp"), + SHOPP_ALL_ERR => __("All Errors","Shopp"), + SHOPP_DEBUG_ERR => __("Debugging Messages","Shopp") + ); + + $filesystems = array("db" => __("Database","Shopp"),"fs" => __("File System","Shopp")); + + $loading = array("shopp" => __('Load on Shopp-pages only','Shopp'),"all" => __('Load on entire site','Shopp')); + + if ($this->Settings->get('error_logging') > 0) + $recentlog = $Shopp->ErrorLog->tail(500); + + include(SHOPP_ADMINPATH."/settings/system.php"); + } + + function settings_get_gateways () { + $gateway_path = $this->basepath.DIRECTORY_SEPARATOR."gateways"; + + $gateways = array(); + $gwfiles = array(); + find_files(".php",$gateway_path,$gateway_path,$gwfiles); + if (empty($gwfiles)) return $gwfiles; + + foreach ($gwfiles as $file) { + if (! is_readable($gateway_path.$file)) continue; + if (! $gateway = $this->scan_gateway_meta($gateway_path.$file)) continue; + $gateways[$file] = $gateway; + } + + return $gateways; + } + + function validate_addons () { + $addons = array(); + + $gateway_path = $this->basepath.DIRECTORY_SEPARATOR."gateways"; + find_files(".php",$gateway_path,$gateway_path,$gateways); + foreach ($gateways as $file) { + if (in_array(basename($file),$this->coremods)) continue; + $addons[] = md5_file($gateway_path.$file); + } + + $shipping_path = $this->basepath.DIRECTORY_SEPARATOR."shipping"; + find_files(".php",$shipping_path,$shipping_path,$shipmods); + foreach ($shipmods as $file) { + if (in_array(basename($file),$this->coremods)) continue; + $addons[] = md5_file($shipping_path.$file); + } + return $addons; + } + + function scan_gateway_meta ($file) { + $metadata = array(); + + $meta = get_filemeta($file); + + if ($meta) { + $lines = explode("\n",substr($meta,1)); + foreach($lines as $line) { + preg_match("/^(?:[\s\*]*?\b([^@\*\/]*))/",$line,$match); + if (!empty($match[1])) $data[] = $match[1]; + preg_match("/^(?:[\s\*]*?@([^\*\/]+?)\s(.+))/",$line,$match); + if (!empty($match[1]) && !empty($match[2])) $tags[$match[1]] = $match[2]; + + } + $gateway = new stdClass(); + $gateway->file = $file; + $gateway->name = $data[0]; + $gateway->description = (!empty($data[1]))?$data[1]:""; + $gateway->tags = $tags; + $gateway->activated = false; + if ($this->Settings->get('payment_gateway') == $file) $module->activated = true; + return $gateway; + } + return false; + } + + function settings_save () { + if (empty($_POST['settings']) || !is_array($_POST['settings'])) return false; + foreach ($_POST['settings'] as $setting => $value) + $this->Settings->save($setting,$value); + } + + /** + * Installation, upgrade and initialization functions + */ + + function update () { + global $Shopp,$wp_version; + $db = DB::get(); + $log = array(); + + if (!isset($_POST['update'])) die("Update Failed: Update request is invalid. No update specified."); + if (!isset($_POST['type'])) die("Update Failed: Update request is invalid. Update type not specified"); + if (!isset($_POST['password'])) die("Update Failed: Update request is invalid. No FTP password provided."); + + $updatekey = $this->Settings->get('updatekey'); + + $credentials = $this->Settings->get('ftp_credentials'); + if (empty($credentials)) { + // Try to load from WordPress settings + $credentials = get_option('ftp_credentials'); + if (!$credentials) $credentials = array(); + } + + // Make sure we can connect to FTP + $ftp = new FTPClient($credentials['hostname'],$credentials['username'],$_POST['password']); + if (!$ftp->connected) die("ftp-failed"); + else $log[] = "Connected with FTP successfully."; + + // Get zip functions from WP Admin + if (class_exists('PclZip')) $log[] = "ZIP library available."; + else { + @require_once(ABSPATH.'wp-admin/includes/class-pclzip.php'); + $log[] = "ZIP library loaded."; + } + + // Put site in maintenance mode + if ($this->Settings->get('maintenance') != "on") { + $this->Settings->save("maintenance","on"); + $log[] = "Enabled maintenance mode."; + } + + // Find our temporary filesystem workspace + $tmpdir = defined('SHOPP_TEMP_PATH') ? SHOPP_TEMP_PATH : sys_get_temp_dir(); + $tmpdir = str_replace('\\', '/', $tmpdir); //Windows path sanitiation + + $log[] = "Found temp directory: $tmpdir"; + + // Download the new version of Shopp + $updatefile = tempnam($tmpdir,"shopp_update_"); + if (($download = fopen($updatefile, 'wb')) === false) { + $log[] = "A temporary file could not be created under $tmpdir, trying WordPress upload directory instead."; + $tmpdir = trailingslashit(WP_CONTENT_DIR."/uploads"); + $updatefile = tempnam($tmpdir,"shopp_update_"); + $log[] = "Found temp directory: $tmpdir"; + if (($download = fopen($updatefile, 'wb')) === false) + die(join("\n\n",$log)."\n\nUpdate Failed: Cannot save the Shopp update to the temporary workspace because of a write permission error."); + } + + $query = build_query_request(array( + "ShoppServerRequest" => "download-update", + "ver" => "1.0", + )); + + $data = build_query_request(array( + "key" => $updatekey['key'], + "core" => SHOPP_VERSION, + "wp" => $wp_version, + "site" => get_bloginfo('siteurl'), + "update" => $_POST['update'] + )); + + $connection = curl_init(); + curl_setopt($connection, CURLOPT_URL, SHOPP_HOME."?".$query); + curl_setopt($connection, CURLOPT_USERAGENT, SHOPP_GATEWAY_USERAGENT); + curl_setopt($connection, CURLOPT_HEADER, 0); + curl_setopt($connection, CURLOPT_POST, 1); + curl_setopt($connection, CURLOPT_POSTFIELDS, $data); + curl_setopt($connection, CURLOPT_TIMEOUT, 20); + curl_setopt($connection, CURLOPT_FILE, $download); + curl_exec($connection); + curl_close($connection); + fclose($download); + + $downloadsize = filesize($updatefile); + // Report error message returned by the server request + if (filesize($updatefile) < 256) die(join("\n\n",$log)."\nUpdate Failed: ".file_get_contents($updatefile)); + + // Nothing downloaded... couldn't reach the server? + if (filesize($updatefile) == 0) die(join("\n\n",$log)."\n\Update Failed: The download did not complete succesfully."); + + // Download successful, log the size + $log[] = "Downloaded update of ".number_format($downloadsize)." bytes"; + + // Extract data + $log[] = "Unpacking updates..."; + $archive = new PclZip($updatefile); + $files = $archive->extract(PCLZIP_OPT_EXTRACT_AS_STRING); + if (!is_array($files)) die(join("\n\n",$log)."\n\nUpdate Failed: The downloaded update did not complete or is corrupted and cannot be used."); + else unlink($updatefile); + $target = trailingslashit($tmpdir); + + // Move old updates that still exist in $tmpdir to a new location + if (file_exists($target.$files[0]['filename']) + && is_dir($target.$files[0]['filename'])) + rename($target.$files[0]['filename'],$updatefile.'_old_update'); + + // Create file structure in working path target + foreach ($files as $file) { + if (!$file['folder'] ) { + if (file_put_contents($target.$file['filename'], $file['content'])) + @chmod($target.$file['filename'], 0644); + } else { + if (!is_dir($target.$file['filename'])) { + if (!@mkdir($target.$file['filename'],0755,true)) + die(join("\n\n",$log)."\n\nUpdate Failed: Couldn't create directory $target{$file['filename']}"); + } + } + } + $log[] = "Successfully unpacked the update."; + + // FTP files to make it "easier" than dealing with permissions + $log[] = "Updating files via FTP connection"; + switch($_POST['type']) { + case "core": + $results = $ftp->update($target.$files[0]['filename'],$Shopp->path); + if (!empty($results)) die(join("\n\n",$log).join("\n\n",$results)."\n\nFTP transfer failed."); + break; + case "Payment Gateway": + $results = $ftp->update($target.$files[0]['filename'], + $Shopp->path.DIRECTORY_SEPARATOR."gateways".DIRECTORY_SEPARATOR.$files[0]['filename']); + if (!empty($results)) die(join("\n\n",$log).join("\n\n",$results)."\n\nFTP transfer failed."); + break; + case "Shipping Module": + $results = $ftp->update($target.$files[0]['filename'], + $Shopp->path.DIRECTORY_SEPARATOR."shipping".DIRECTORY_SEPARATOR.$files[0]['filename']); + if (!empty($results)) die(join("\n\n",$log).join("\n\n",$results)."\n\nFTP transfer failed."); + break; + } + + echo "updated"; // Report success! + exit(); + } + + function upgrade () { + global $Shopp,$table_prefix; + $db = DB::get(); + require_once(ABSPATH.'wp-admin/includes/upgrade.php'); + + // Check for the schema definition file + if (!file_exists(SHOPP_DBSCHEMA)) + die("Could not upgrade the Shopp database tables because the table definitions file is missing: ".SHOPP_DBSCHEMA); + + ob_start(); + include(SHOPP_DBSCHEMA); + $schema = ob_get_contents(); + ob_end_clean(); + + // Update the table schema + $tables = preg_replace('/;\s+/',';',$schema); + dbDelta($tables); + + $this->setup_regions(); + $this->setup_countries(); + $this->setup_zones(); + $this->setup_areas(); + $this->setup_vat(); + + // Update the version number + $settings = DatabaseObject::tablename(Settings::$table); + $db->query("UPDATE $settings SET value='".SHOPP_VERSION." WHERE name='version'"); + $db->query("DELETE FROM $settings WHERE name='data_model' OR name='shipcalc_lastscan"); + + return true; + } + + function callhome ($request=array(),$data=array()) { + $query = build_query_request($request); + $data = build_query_request($data); + + $connection = curl_init(); + curl_setopt($connection, CURLOPT_URL, SHOPP_HOME."?".$query); + curl_setopt($connection, CURLOPT_USERAGENT, SHOPP_GATEWAY_USERAGENT); + curl_setopt($connection, CURLOPT_HEADER, 0); + curl_setopt($connection, CURLOPT_POST, 1); + curl_setopt($connection, CURLOPT_POSTFIELDS, $data); + curl_setopt($connection, CURLOPT_TIMEOUT, 20); + curl_setopt($connection, CURLOPT_RETURNTRANSFER, 1); + $result = curl_exec($connection); + curl_close ($connection); + + return $result; + } + + + /** + * setup() + * Initialize default install settings and lists */ + function setup () { + + $this->setup_regions(); + $this->setup_countries(); + $this->setup_zones(); + $this->setup_areas(); + $this->setup_vat(); + + $this->Settings->save('show_welcome','on'); + $this->Settings->save('display_welcome','on'); + + // General Settings + $this->Settings->save('version',SHOPP_VERSION); + $this->Settings->save('shipping','on'); + $this->Settings->save('order_status',array('Pending','Completed')); + $this->Settings->save('shopp_setup','completed'); + $this->Settings->save('maintenance','off'); + $this->Settings->save('dashboard','on'); + + // Checkout Settings + $this->Settings->save('order_confirmation','ontax'); + $this->Settings->save('receipt_copy','1'); + $this->Settings->save('account_system','none'); + + // Presentation Settings + $this->Settings->save('theme_templates','off'); + $this->Settings->save('row_products','3'); + $this->Settings->save('catalog_pagination','25'); + $this->Settings->save('product_image_order','ASC'); + $this->Settings->save('product_image_orderby','sortorder'); + $this->Settings->save('gallery_small_width','240'); + $this->Settings->save('gallery_small_height','240'); + $this->Settings->save('gallery_small_sizing','1'); + $this->Settings->save('gallery_small_quality','2'); + $this->Settings->save('gallery_thumbnail_width','96'); + $this->Settings->save('gallery_thumbnail_height','96'); + $this->Settings->save('gallery_thumbnail_sizing','1'); + $this->Settings->save('gallery_thumbnail_quality','3'); + + // System Settinggs + $this->Settings->save('image_storage_pref','db'); + $this->Settings->save('product_storage_pref','db'); + $this->Settings->save('uploader_pref','flash'); + $this->Settings->save('script_loading','global'); + + // Payment Gateway Settings + $this->Settings->save('PayPalExpress',array('enabled'=>'off')); + $this->Settings->save('GoogleCheckout',array('enabled'=>'off')); + } + + function setup_regions () { + global $Shopp; + include_once("init.php"); + $this->Settings->save('regions',get_global_regions()); + } + + function setup_countries () { + global $Shopp; + include_once("init.php"); + $this->Settings->save('countries',addslashes(serialize(get_countries())),false); + } + + function setup_zones () { + global $Shopp; + include_once("init.php"); + $this->Settings->save('zones',get_country_zones(),false); + } + + function setup_areas () { + global $Shopp; + include_once("init.php"); + $this->Settings->save('areas',get_country_areas(),false); + } + + function setup_vat () { + global $Shopp; + include_once("init.php"); + $this->Settings->save('vat_countries',get_vat_countries(),false); + } + +} +?> \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/functions.php b/blog/wp-content/plugins/shopp/core/functions.php new file mode 100644 index 0000000..da113f1 --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/functions.php @@ -0,0 +1,1193 @@ + 0, "monday" => 1, "tuesday" => 2, "wednesday" => 3, "thursday" => 4, "friday" => 5, "saturday" => 6); + $weeks = array("first" => 1, "second" => 2, "third" => 3, "fourth" => 4, "last" => -1); + + if ($month == -1) $month = date ("n"); // No month provided, use current month + if ($year == -1) $year = date("Y"); // No year provided, use current year + + // Day of week is a string, look it up in the weekdays list + if (!is_numeric($dayOfWeek)) { + foreach ($weekdays as $dayName => $dayNum) { + if (strtolower($dayOfWeek) == substr($dayName,0,strlen($dayOfWeek))) { + $dayOfWeek = $dayNum; + break; + } + } + } + if ($dayOfWeek < 0 || $dayOfWeek > 6) return false; + + if (!is_numeric($week)) $week = $weeks[$week]; + + if ($week == -1) { + $lastday = date("t", mktime(0,0,0,$month,1,$year)); + $tmp = (date("w",mktime(0,0,0,$month,$lastday,$year)) - $dayOfWeek) % 7; + if ($tmp < 0) $tmp += 7; + $day = $lastday - $tmp; + } else { + $tmp = ($dayOfWeek - date("w",mktime(0,0,0,$month,1,$year))) % 7; + if ($tmp < 0) $tmp += 7; + $day = (7 * $week) - 6 + $tmp; + } + + return mktime(0,0,0,$month,$day,$year); +} + +/** + * Converts a datetime value from a MySQL datetime format to a Unix timestamp. */ +function mktimestamp ($datetime) { + $h = $mn = $s = 0; + list($Y, $M, $D, $h, $mn, $s) = sscanf($datetime,"%d-%d-%d %d:%d:%d"); + return mktime($h, $mn, $s, $M, $D, $Y); +} + +/** + * Converts a Unix timestamp value to a datetime format suitable for entry in a + * MySQL record. */ +function mkdatetime ($timestamp) { + return date("Y-m-d H:i:s",$timestamp); +} + +/** + * Returns the corresponding 24-hour $hour based on a 12-hour based $hour + * and the AM (Ante Meridiem) / PM (Post Meridiem) $meridiem. */ +function mk24hour ($hour, $meridiem) { + if ($hour < 12 && $meridiem == "PM") return $hour + 12; + if ($hour == 12 && $meridiem == "AM") return 0; + return $hour; +} + +/** + * Returns a string of the number of years, months, days, hours, + * minutes and even seconds from a specified date ($date). */ +function readableTime($date, $long = false) { + + $secs = time() - $date; + if (!$secs) return false; + $i = 0; $j = 1; + $desc = array(1 => 'second', + 60 => 'minute', + 3600 => 'hour', + 86400 => 'day', + + 604800 => 'week', + 2628000 => 'month', + 31536000 => 'year'); + + + while (list($k,) = each($desc)) $breaks[] = $k; + sort($breaks); + + while ($i < count($breaks) && $secs >= $breaks[$i]) $i++; + $i--; + $break = $breaks[$i]; + + $val = intval($secs / $break); + $retval = $val . ' ' . $desc[$break] . ($val>1?'s':''); + + if ($long && $i > 0) { + $rest = $secs % $break; + $break = $breaks[--$i]; + $rest = intval($rest/$break); + + if ($rest > 0) { + $resttime = $rest.' '.$desc[$break].($rest > 1?'s':''); + + $retval .= ", $resttime"; + } + } + + return $retval; +} + +function duration ($start,$end) { + return ceil(($end - $start) / 86400); +} + +/** + * Sends an e-mail message in the format of a specified e-mail + * template ($template) file providing variable substitution + * for variables appearing in the template as a bracketed + * [variable] with data from the coinciding $data['variable'] + * or $_POST['variable'] */ +function shopp_email ($template,$data=array()) { + + if (strpos($template,"\r\n") !== false) $f = explode("\r\n",$template); + else { + if (file_exists($template)) $f = file($template); + else new ShoppError(__("Could not open the email template because the file does not exist or is not readable.","Shopp"),'email_template',SHOPP_ADMIN_ERR,array('template'=>$template)); + } + + $replacements = array( + "$" => "\\\$", // Treat $ signs as literals + "€" => "€", // Fix euro symbols + "¥" => "¥", // Fix yen symbols + "£" => "£", // Fix pound symbols + "¤" => "¤" // Fix generic currency symbols + ); + + $debug = false; + $in_body = false; + $headers = ""; + $message = ""; + $protected = array("from","to","subject","cc","bcc"); + while ( list($linenum,$line) = each($f) ) { + $line = rtrim($line); + // Data parse + if ( preg_match_all("/\[(.+?)\]/",$line,$labels,PREG_SET_ORDER) ) { + while ( list($i,$label) = each($labels) ) { + $code = $label[1]; + if (empty($data)) $string = $_POST[$code]; + else $string = $data[$code]; + + $string = str_replace(array_keys($replacements),array_values($replacements),$string); + + if (isset($string) && !is_array($string)) $line = preg_replace("/\[".$code."\]/",$string,$line); + } + } + + // Header parse + if ( preg_match("/^(.+?):\s(.+)$/",$line,$found) && !$in_body ) { + $header = $found[1]; + $string = $found[2]; + if (in_array(strtolower($header),$protected)) // Protect against header injection + $string = str_replace(array("\r","\n"),"",urldecode($string)); + if ( strtolower($header) == "to" ) $to = $string; + else if ( strtolower($header) == "subject" ) $subject = $string; + else $headers .= $line."\n"; + } + + // Catches the first blank line to begin capturing message body + if ( empty($line) ) $in_body = true; + if ( $in_body ) $message .= $line."\n"; + } + + if (!$debug) return mail($to,$subject,$message,$headers); + else { + echo "
";
+		echo "To: $to\n";
+		echo "Subject: $subject\n\n";
+		echo "Message:\n$message\n";
+		echo "Headers:\n";
+		print_r($headers);
+		echo "
";
+		exit();		
+	}
+}
+
+/**
+ * Generates an RSS-compliant string from an associative 
+ * array ($data) with a specific RSS-structure. */
+function shopp_rss ($data) {
+	$xmlns = '';
+	if (is_array($data['xmlns']))
+		foreach ($data['xmlns'] as $key => $value)
+			$xmlns .= ' xmlns:'.$key.'="'.$value.'"';
+
+	$xml = "\n";
+	$xml .= "\n";
+	$xml .= "\n";
+
+	$xml .= ''."\n";
+	$xml .= "".$data['title']."\n";
+	$xml .= "".$data['description']."\n";
+	$xml .= "".htmlentities($data['link'])."\n";
+	$xml .= "en-us\n";
+	$xml .= "Copyright ".date('Y').", ".$data['sitename']."\n";
+	
+	if (is_array($data['items'])) {
+		foreach($data['items'] as $item) {
+			$xml .= "\n";
+			foreach ($item as $key => $value) {
+				$attrs = '';
+				if (is_array($value)) {
+					$data = $value;
+					$value = '';
+					foreach ($data as $name => $content) {
+						if (empty($name)) $value = $content;
+						else $attrs .= ' '.$name.'="'.$content.'"';
+					}
+				}
+				if (!empty($value)) $xml .= "<$key$attrs>$value\n";
+				else $xml .= "<$key$attrs />\n";
+			}
+			$xml .= "\n";
+		}
+	}
+	
+	$xml .= "\n";
+	$xml .= "\n";
+	
+	return $xml;
+}
+
+function shopp_image () {
+	$db =& DB::get();
+	require("model/Asset.php");
+	$table = DatabaseObject::tablename(Settings::$table);
+	$settings = $db->query("SELECT name,value FROM $table WHERE name='image_storage' OR name='image_path'",AS_ARRAY);
+	foreach ($settings as $setting) ${$setting->name} = $setting->value;
+
+	if (isset($_GET['shopp_image'])) $image = $_GET['shopp_image'];
+	elseif (preg_match('/\/images\/(\d+).*$/',$_SERVER['REQUEST_URI'],$matches)) 
+		$image = $matches[1];
+
+	if (empty($image)) die();
+	$Asset = new Asset($image);
+	header('Last-Modified: '.date('D, d M Y H:i:s', $Asset->created).' GMT'); 
+	header("Content-type: ".$Asset->properties['mimetype']);
+	header("Content-Disposition: inline; filename=".$Asset->name.""); 
+	header("Content-Description: Delivered by WordPress/Shopp ".SHOPP_VERSION);
+	if ($image_storage == "fs") {
+		header ("Content-length: ".@filesize(trailingslashit($image_path).$Asset->name)); 
+		readfile(trailingslashit($image_path).$Asset->name);
+	} else {
+		header ("Content-length: ".strlen($Asset->data)); 
+		echo $Asset->data;
+	} 
+	exit();
+}
+
+function shopp_catalog_css () {
+	$db =& DB::get();
+	$table = DatabaseObject::tablename(Settings::$table);
+	$settings = $db->query("SELECT name,value FROM $table WHERE name='gallery_thumbnail_width' OR name='row_products' OR name='row_products' OR name='gallery_small_width' OR name='gallery_small_height'",AS_ARRAY);
+	foreach ($settings as $setting) ${$setting->name} = $setting->value;
+
+	$pluginuri = WP_PLUGIN_URL."/".basename(dirname(dirname(__FILE__)))."/";
+	$pluginuri = force_ssl($pluginuri);
+
+	if (!isset($row_products)) $row_products = 3;
+	$products_per_row = floor((100/$row_products));
+	
+	ob_start();
+	include("ui/styles/catalog.css");
+	$file = ob_get_contents();
+	ob_end_clean();
+	header ("Content-type: text/css");
+	header ("Content-Disposition: inline; filename=catalog.css"); 
+	header ("Content-Description: Delivered by WordPress/Shopp ".SHOPP_VERSION);
+	header ("Content-length: ".strlen($file)); 
+	echo $file;
+	exit();
+}
+
+function shopp_settings_js ($dir="shopp") {
+	$db =& DB::get();
+	$table = DatabaseObject::tablename(Settings::$table);
+	$settings = $db->query("SELECT name,value FROM $table WHERE name='base_operations'",AS_ARRAY);
+	foreach ($settings as $setting) ${$setting->name} = $setting->value;
+	$base_operations = unserialize($base_operations);
+	
+	$path = array(PLUGINDIR,$dir,'lang');
+	load_plugin_textdomain('Shopp', join(DIRECTORY_SEPARATOR,$path));
+	
+	ob_start();
+	include("ui/behaviors/settings.js");
+	$file = ob_get_contents();
+	ob_end_clean();
+	header ("Content-type: text/javascript");
+	header ("Content-Disposition: inline; filename=settings.js"); 
+	header ("Content-Description: Delivered by WordPress/Shopp ".SHOPP_VERSION);
+	header ("Content-length: ".strlen($file)); 
+	echo $file;
+	exit();
+}
+
+/**
+ * Formats a number into a standardized telephone number format */
+function phone ($num) {
+	if (empty($num)) return "";
+	$num = preg_replace("/[A-Za-z\-\s\(\)]/","",$num);
+	
+	if (strlen($num) == 7) sscanf($num, "%3s%4s", $prefix, $exchange);
+	if (strlen($num) == 10) sscanf($num, "%3s%3s%4s", $area, $prefix, $exchange);
+	if (strlen($num) == 11) sscanf($num, "%1s%3s%3s%4s",$country, $area, $prefix, $exchange);
+	//if (strlen($num) > 11) sscanf($num, "%3s%3s%4s%s", $area, $prefix, $exchange, $ext);
+	
+	$string = "";
+	$string .= (isset($country))?"$country ":"";
+	$string .= (isset($area))?"($area) ":"";
+	$string .= (isset($prefix))?$prefix:"";
+	$string .= (isset($exchange))?"-$exchange":"";
+	$string .= (isset($ext))?" x$ext":"";
+	return $string;
+
+}
+
+/**
+ * Determines if the current client is a known web crawler bot */
+function is_robot() {
+	$bots = array("Googlebot","TeomaAgent","Zyborg","Gulliver","Architext spider","FAST-WebCrawler","Slurp","Ask Jeeves","ia_archiver","Scooter","Mercator","crawler@fast","Crawler","InfoSeek sidewinder","Lycos_Spider_(T-Rex)","Fluffy the Spider","Ultraseek","MantraAgent","Moget","MuscatFerret","VoilaBot","Sleek Spider","KIT_Fireball","WebCrawler");
+	foreach($bots as $bot) {
+		if (strpos(strtolower($_SERVER['HTTP_USER_AGENT']),strtolower($bot))) return true;
+	}
+	return false;
+}
+
+function shopp_prereqs () {
+	$errors = array();
+	// Check PHP version, this won't appear much since syntax errors in earlier
+	// PHP releases will cause this code to never be executed
+	if (!version_compare(PHP_VERSION, '5.0','>=')) 
+		$errors[] = __("Shopp requires PHP version 5.0+.  You are using PHP version ").PHP_VERSION;
+
+	if (version_compare(PHP_VERSION, '5.1.3','==')) 
+		$errors[] = __("Shopp will not work with PHP version 5.1.3 because of a critical bug in complex POST data structures.  Please upgrade PHP to version 5.1.4 or higher.");
+		
+	// Check WordPress version
+	if (!version_compare(get_bloginfo('version'),'2.6','>='))
+		$errors[] = __("Shopp requires WordPress version 2.6+.  You are using WordPress version ").get_bloginfo('version');
+	
+	// Check for cURL
+	if( !function_exists("curl_init") &&
+	      !function_exists("curl_setopt") &&
+	      !function_exists("curl_exec") &&
+	      !function_exists("curl_close") ) $errors[] = __("Shopp requires the cURL library for processing transactions securely. Your web hosting environment does not currently have cURL installed (or built into PHP).");
+	
+	// Check for GD
+	if (!function_exists("gd_info")) $errors[] = __("Shopp requires the GD image library with JPEG support for generating gallery and thumbnail images.  Your web hosting environment does not currently have GD installed (or built into PHP).");
+	else {
+		$gd = gd_info();
+		if (!$gd['JPG Support'] && !$gd['JPEG Support']) $errors[] = __("Shopp requires JPEG support in the GD image library.  Your web hosting environment does not currently have a version of GD installed that has JPEG support.");
+	}
+	
+	if (!empty($errors)) {
+		$string .= '';
+		
+		foreach ($errors as $error) $string .= "

$error

"; + + $string .= '

'.__('Sorry! You will not be able to use Shopp. For more information, see the online Shopp documentation.').'

'; + + trigger_error($string,E_USER_ERROR); + exit(); + } + return true; +} + +if( !function_exists('esc_url') ) { + /** + * Checks and cleans a URL. From WordPress 2.8.0+ Included for WordPress 2.7 Users of Shopp + * + * A number of characters are removed from the URL. If the URL is for displaying + * (the default behaviour) amperstands are also replaced. The 'esc_url' filter + * is applied to the returned cleaned URL. + * + * @since 2.8.0 + * @uses esc_url() + * @uses wp_kses_bad_protocol() To only permit protocols in the URL set + * via $protocols or the common ones set in the function. + * + * @param string $url The URL to be cleaned. + * @param array $protocols Optional. An array of acceptable protocols. + * Defaults to 'http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet' if not set. + * @return string The cleaned $url after the 'cleaned_url' filter is applied. + */ + function esc_url( $url, $protocols = null ) { + return clean_url( $url, $protocols, 'display' ); + } +} + +function shopp_debug ($object) { + global $Shopp; + ob_start(); + print_r($object); + $result = ob_get_contents(); + ob_end_clean(); + $Shopp->_debug->objects .= "

".str_replace("\n","
",$result); +} + +function _object_r ($object) { + global $Shopp; + ob_start(); + print_r($object); + $result = ob_get_contents(); + ob_end_clean(); + return $result; +} + +function shopp_pagename ($page) { + global $is_IIS; + $prefix = strpos($page,"index.php/"); + if ($prefix !== false) return substr($page,$prefix+10); + else return $page; +} + +function shopp_redirect ($uri) { + if (class_exists('ShoppError')) new ShoppError('Redirecting to: '.$uri,'shopp_redirect',SHOPP_DEBUG_ERR); + wp_redirect($uri); + exit(); +} + +function get_filemeta ($file) { + if (!file_exists($file)) return false; + if (!is_readable($file)) return false; + + $meta = false; + $string = ""; + + $f = @fopen($file, "r"); + if (!$f) return false; + while (!feof($f)) { + $buffer = fgets($f,80); + if (preg_match("/\/\*/",$buffer)) $meta = true; + if ($meta) $string .= $buffer; + if (preg_match("/\*\//",$buffer)) break; + } + fclose($f); + + return $string; +} + +/** + * Recursively searches directories and one-level deep of + * sub-directories for files with a specific extension + * NOTE: Files are saved to the $found parameter, + * an array passed by reference, not a returned value */ +function find_files ($extension, $directory, $root, &$found) { + if (is_dir($directory)) { + + $Directory = @dir($directory); + if ($Directory) { + while (( $file = $Directory->read() ) !== false) { + if (substr($file,0,1) == "." || substr($file,0,1) == "_") continue; // Ignore .dot files and _directories + if (is_dir($directory.DIRECTORY_SEPARATOR.$file) && $directory == $root) // Scan one deep more than root + find_files($extension,$directory.DIRECTORY_SEPARATOR.$file,$root, $found); // but avoid recursive scans + if (substr($file,strlen($extension)*-1) == $extension) + $found[] = substr($directory,strlen($root)).DIRECTORY_SEPARATOR.$file; // Add the file to the found list + } + return true; + } + } + return false; +} + +if (!function_exists('json_encode')) { + function json_encode ($a = false) { + if (is_null($a)) return 'null'; + if ($a === false) return 'false'; + if ($a === true) return 'true'; + if (is_scalar($a)) { + if (is_float($a)) { + // Always use "." for floats. + return floatval(str_replace(",", ".", strval($a))); + } + + if (is_string($a)) { + static $jsonReplaces = array(array("\\", "/", "\n", "\t", "\r", "\b", "\f", '"'), array('\\\\', '\\/', '\\n', '\\t', '\\r', '\\b', '\\f', '\"')); + return '"' . str_replace($jsonReplaces[0], $jsonReplaces[1], $a) . '"'; + } else return $a; + } + + $isList = true; + for ($i = 0, reset($a); $i < count($a); $i++, next($a)) { + if (key($a) !== $i) { + $isList = false; + break; + } + } + + $result = array(); + if ($isList) { + foreach ($a as $v) $result[] = json_encode($v); + return '[' . join(',', $result) . ']'; + } else { + foreach ($a as $k => $v) $result[] = json_encode($k).':'.json_encode($v); + return '{' . join(',', $result) . '}'; + } + } +} + + +/** + * List files and directories inside the specified path */ +if(!function_exists('scandir')) { + function scandir($dir, $sortorder = 0) { + if(is_dir($dir) && $dirlist = @opendir($dir)) { + $files = array(); + while(($file = readdir($dirlist)) !== false) $files[] = $file; + closedir($dirlist); + ($sortorder == 0) ? asort($files) : rsort($files); + return $files; + } else return false; + } +} + +function filter_dotfiles ($name) { + return (substr($name,0,1) != "."); +} + +/** + * Checks an object for a declared property + * if() checks to see if the function is already available (as in PHP 5) */ +if (!function_exists('property_exists')) { + function property_exists($object, $property) { + return array_key_exists($property, get_object_vars($object)); + } +} + +if (!function_exists('attribute_escape_deep')) { + function attribute_escape_deep($value) { + $value = is_array($value) ? + array_map('attribute_escape_deep', $value) : + attribute_escape($value); + return $value; + } +} + +function auto_ranges ($avg,$max,$min) { + $ranges = array(); + if ($avg == 0 || $max == 0) return $ranges; + $power = floor(log10($avg)); + $scale = pow(10,$power); + $median = round($avg/$scale)*$scale; + $range = $max-$min; + + if ($range == 0) return $ranges; + + $steps = floor($range/$scale); + if ($steps > 7) $steps = 7; + elseif ($steps < 2) { + $scale = $scale/2; + $steps = ceil($range/$scale); + if ($steps > 7) $steps = 7; + elseif ($steps < 2) $steps = 2; + } + + $base = $median-($scale*floor(($steps-1)/2)); + for ($i = 0; $i < $steps; $i++) { + $range = array("min" => 0,"max" => 0); + if ($i == 0) $range['max'] = $base; + else if ($i+1 >= $steps) $range['min'] = $base; + else $range = array("min" => $base, "max" => $base+$scale); + $ranges[] = $range; + if ($i > 0) $base += $scale; + } + return $ranges; +} + +function floatvalue($value, $format=true) { + $value = preg_replace("/[^\d,\.]/","",$value); // Remove any non-numeric string data + $value = preg_replace("/,/",".",$value); // Replace commas with periods + $value = preg_replace("/[^0-9\.]/","", $value); // Get rid of everything but numbers and periods + $value = preg_replace("/\.(?=.*\..*$)/s","",$value); // Replace all but the last period + $value = preg_replace('#^([-]*[0-9\.,\' ]+?)((\.|,){1}([0-9-]{1,2}))*$#e', "str_replace(array('.', ',', \"'\", ' '), '', '\\1') . '.' . sprintf('%02d','\\4')", $value); + if($format) return number_format(floatval($value),2); + else return floatval($value); +} + +/** + * sort_tree + * Sorts a heirarchical tree of data */ +function sort_tree ($items,$parent=0,$key=-1,$depth=-1) { + $depth++; + $result = array(); + if ($items) { + foreach ($items as $item) { + if ($item->parent == $parent) { + $item->parentkey = $key; + $item->depth = $depth; + $result[] = $item; + $children = sort_tree($items, $item->id, count($result)-1, $depth); + $result = array_merge($result,$children); // Add children in as they are found + } + } + } + $depth--; + return $result; +} + +/** + * file_mimetype + * Tries a variety of methods to determine a file's mimetype */ +function file_mimetype ($file,$name=false) { + if (!$name) $name = basename($file); + if (function_exists('finfo_open')) { + // Try using PECL module + $f = finfo_open(FILEINFO_MIME); + list($mime,$charset) = explode(";",finfo_file($f, $file)); + finfo_close($f); + new ShoppError('File mimetype detection (finfo_open): '.$mime,false,SHOPP_DEBUG_ERR); + return $mime; + } elseif (class_exists('finfo')) { + // Or class + $f = new finfo(FILEINFO_MIME); + new ShoppError('File mimetype detection (finfo class): '.$f->file($file),false,SHOPP_DEBUG_ERR); + return $f->file($file); + } elseif (strlen($mime=trim(@shell_exec('file -bI "'.escapeshellarg($file).'"')))!=0) { + new ShoppError('File mimetype detection (shell file command): '.$mime,false,SHOPP_DEBUG_ERR); + // Use shell if allowed + return trim($mime); + } elseif (strlen($mime=trim(@shell_exec('file -bi "'.escapeshellarg($file).'"')))!=0) { + new ShoppError('File mimetype detection (shell file command, alt options): '.$mime,false,SHOPP_DEBUG_ERR); + // Use shell if allowed + return trim($mime); + } elseif (function_exists('mime_content_type') && $mime = mime_content_type($file)) { + // Try with magic-mime if available + new ShoppError('File mimetype detection (mime_content_type()): '.$mime,false,SHOPP_DEBUG_ERR); + return $mime; + } else { + if (!preg_match('/\.([a-z0-9]{2,4})$/i', $name, $extension)) return false; + + switch (strtolower($extension[1])) { + // misc files + case 'txt': return 'text/plain'; + case 'htm': case 'html': case 'php': return 'text/html'; + case 'css': return 'text/css'; + case 'js': return 'application/javascript'; + case 'json': return 'application/json'; + case 'xml': return 'application/xml'; + case 'swf': return 'application/x-shockwave-flash'; + + // images + case 'jpg': case 'jpeg': case 'jpe': return 'image/jpg'; + case 'png': case 'gif': case 'bmp': case 'tiff': return 'image/'.strtolower($matches[1]); + case 'tif': return 'image/tif'; + case 'svg': case 'svgz': return 'image/svg+xml'; + + // archives + case 'zip': return 'application/zip'; + case 'rar': return 'application/x-rar-compressed'; + case 'exe': case 'msi': return 'application/x-msdownload'; + case 'tar': return 'application/x-tar'; + case 'cab': return 'application/vnd.ms-cab-compressed'; + + // audio/video + case 'flv': return 'video/x-flv'; + case 'mpeg': case 'mpg': case 'mpe': return 'video/mpeg'; + case 'mp4s': return 'application/mp4'; + case 'mp3': return 'audio/mpeg3'; + case 'wav': return 'audio/wav'; + case 'aiff': case 'aif': return 'audio/aiff'; + case 'avi': return 'video/msvideo'; + case 'wmv': return 'video/x-ms-wmv'; + case 'mov': case 'qt': return 'video/quicktime'; + + // ms office + case 'doc': case 'docx': return 'application/msword'; + case 'xls': case 'xlt': case 'xlm': case 'xld': case 'xla': case 'xlc': case 'xlw': case 'xll': return 'application/vnd.ms-excel'; + case 'ppt': case 'pps': return 'application/vnd.ms-powerpoint'; + case 'rtf': return 'application/rtf'; + + // adobe + case 'pdf': return 'application/pdf'; + case 'psd': return 'image/vnd.adobe.photoshop'; + case 'ai': case 'eps': case 'ps': return 'application/postscript'; + + // open office + case 'odt': return 'application/vnd.oasis.opendocument.text'; + case 'ods': return 'application/vnd.oasis.opendocument.spreadsheet'; + } + + return false; + } +} + +/** + * Returns a list marked-up as drop-down menu options */ +function menuoptions ($list,$selected=null,$values=false,$extend=false) { + if (!is_array($list)) return ""; + $string = ""; + // Extend the options if the selected value doesn't exist + if ((!in_array($selected,$list) && !isset($list[$selected])) && $extend) + $string .= ""; + foreach ($list as $value => $text) { + if ($values) { + if ($value == $selected) $string .= ""; + else $string .= ""; + } else { + if ($text == $selected) $string .= ""; + else $string .= ""; + } + } + return $string; +} + +function scan_money_format ($format) { + $f = array( + "cpos" => true, + "currency" => "", + "precision" => 0, + "decimals" => "", + "thousands" => "" + ); + + $ds = strpos($format,'#'); $de = strrpos($format,'#')+1; + $df = substr($format,$ds,($de-$ds)); + + if ($df == "#,##,###.##") $f['indian'] = true; + + $f['cpos'] = true; + if ($de == strlen($format)) $f['currency'] = substr($format,0,$ds); + else { + $f['currency'] = substr($format,$de); + $f['cpos'] = false; + } + + $i = 0; $dd = 0; + $dl = array(); + $sdl = ""; + $uniform = true; + while($i < strlen($df)) { + $c = substr($df,$i++,1); + if ($c != "#") { + if(empty($sdl)) $sdl = $c; + else if($sdl != $c) $uniform = false; + $dl[] = $c; + $dd = 0; + } else $dd++; + } + if(!$uniform) $f['precision'] = $dd; + + if (count($dl) > 1) { + if ($dl[0] == "t") { + $f['thousands'] = $dl[1]; + $f['precision'] = 0; + } + else { + $f['decimals'] = $dl[count($dl)-1]; + $f['thousands'] = $dl[0]; + } + } else $f['decimals'] = $dl[0]; + + return $f; +} + +function money ($amount,$format=false) { + global $Shopp; + $locale = $Shopp->Settings->get('base_operations'); + if (!$format) $format = $locale['currency']['format']; + if (empty($format['currency'])) + $format = array("cpos"=>true,"currency"=>"$","precision"=>2,"decimals"=>".","thousands" => ","); + + if (isset($format['indian'])) $number = indian_number($amount,$format); + else $number = number_format($amount, $format['precision'], $format['decimals'], $format['thousands']); + if ($format['cpos']) return $format['currency'].$number; + else return $number.$format['currency']; +} + +function percentage ($amount,$format=false) { + global $Shopp; + + $locale = $Shopp->Settings->get('base_operations'); + if (!$format) { + $format = $locale['currency']['format']; + $format['precision'] = 0; + } + if (!$format) $format = array("precision"=>1,"decimals"=>".","thousands" => ","); + if (isset($format['indian'])) return indian_number($amount,$format); + return number_format(round($amount), $format['precision'], $format['decimals'], $format['thousands']).'%'; +} + +function indian_number ($number,$format=false) { + if (!$format) $format = array("precision"=>1,"decimals"=>".","thousands" => ","); + + $d = explode(".",$number); + $number = ""; + $digits = substr($d[0],0,-3); // Get rid of the last 3 + + if (strlen($d[0]) > 3) $number = substr($d[0],-3); + else $number = $d[0]; + + for ($i = 0; $i < (strlen($digits) / 2); $i++) + $number = substr($digits,(-2*($i+1)),2).((strlen($number) > 0)?$format['thousands'].$number:$number); + if ($format['precision'] > 0) + $number = $number.$format['decimals'].substr(number_format('0.'.$d[1],$format['precision']),2); + return $number; + +} + +function floatnum ($number) { + $number = preg_replace("/,/",".",$number); // Replace commas with periods + $number = preg_replace("/[^0-9\.]/","", $number); // Get rid of everything but numbers and periods + $number = preg_replace("/\.(?=.*\..+$)/s","",$number); // Replace all but the last period + return $number; +} + +function value_is_true ($value) { + switch (strtolower($value)) { + case "yes": case "true": case "1": case "on": return true; + default: return false; + } +} + +function valid_input ($type) { + $inputs = array("text","hidden","checkbox","radio","button","submit"); + if (in_array($type,$inputs) !== false) return true; + return false; +} + +function _d($format,$timestamp=false) { + $tokens = array( + 'D' => array('Mon' => __('Mon','Shopp'),'Tue' => __('Tue','Shopp'), + 'Wed' => __('Wed','Shopp'),'Thu' => __('Thu','Shopp'), + 'Fri' => __('Fri','Shopp'),'Sat' => __('Sat','Shopp'), + 'Sun' => __('Sun','Shopp')), + 'l' => array('Monday' => __('Monday','Shopp'),'Tuesday' => __('Tuesday','Shopp'), + 'Wednesday' => __('Wednesday','Shopp'),'Thursday' => __('Thursday','Shopp'), + 'Friday' => __('Friday','Shopp'),'Saturday' => __('Saturday','Shopp'), + 'Sunday' => __('Sunday','Shopp')), + 'F' => array('January' => __('January','Shopp'),'February' => __('February','Shopp'), + 'March' => __('March','Shopp'),'April' => __('April','Shopp'), + 'May' => __('May','Shopp'),'June' => __('June','Shopp'), + 'July' => __('July','Shopp'),'August' => __('August','Shopp'), + 'September' => __('September','Shopp'),'October' => __('October','Shopp'), + 'November' => __('November','Shopp'),'December' => __('December','Shopp')), + 'M' => array('Jan' => __('Jan','Shopp'),'Feb' => __('Feb','Shopp'), + 'Mar' => __('Mar','Shopp'),'Apr' => __('Apr','Shopp'), + 'May' => __('May','Shopp'),'Jun' => __('Jun','Shopp'), + 'Jul' => __('Jul','Shopp'),'Aug' => __('Aug','Shopp'), + 'Sep' => __('Sep','Shopp'),'Oct' => __('Oct','Shopp'), + 'Nov' => __('Nov','Shopp'),'Dec' => __('Dec','Shopp')) + ); + + if (!$timestamp) $date = date($format); + else $date = date($format,$timestamp); + + foreach ($tokens as $token => $strings) { + if ($pos = strpos($format,$token) === false) continue; + $string = (!$timestamp)?date($token):date($token,$timestamp); + $date = str_replace($string,$strings[$string],$date); + } + return $date; +} + +function shopp_taxrate ($override=null,$taxprice=true) { + global $Shopp; + $rated = false; + $taxrate = 0; + $base = $Shopp->Settings->get('base_operations'); + + if ($base['vat']) $rated = true; + if (!is_null($override)) $rated = (value_is_true($override)); + if (!value_is_true($taxprice)) $rated = false; + + if ($rated) $taxrate = $Shopp->Cart->taxrate(); + return $taxrate; +} + +function inputattrs ($options,$allowed=array()) { + if (!is_array($options)) return ""; + if (empty($allowed)) { + $allowed = array("accesskey","alt","checked","class","disabled","format", + "minlength","maxlength","readonly","required","size","src","tabindex", + "title","value"); + } + $string = ""; + $classes = ""; + if (isset($options['label'])) $options['value'] = $options['label']; + foreach ($options as $key => $value) { + if (!in_array($key,$allowed)) continue; + switch($key) { + case "class": $classes .= " $value"; break; + case "disabled": $classes .= " disabled"; $string .= ' disabled="disabled"'; break; + case "readonly": $classes .= " readonly"; $string .= ' readonly="readonly"'; break; + case "required": $classes .= " required"; break; + case "minlength": $classes .= " min$value"; break; + case "format": $classes .= " $value"; break; + default: + $string .= ' '.$key.'="'.$value.'"'; + } + } + $string .= ' class="'.trim($classes).'"'; + return $string; +} + +function build_query_request ($request=array()) { + $query = ""; + foreach ($request as $name => $value) { + if (strlen($query) > 0) $query .= "&"; + $query .= "$name=$value"; + } + return $query; +} + +function readableFileSize($bytes,$precision=1) { + $units = array(__("bytes","Shopp"),"KB","MB","GB","TB","PB"); + $sized = $bytes*1; + if ($sized == 0) return $sized; + $unit = 0; + while ($sized > 1024 && ++$unit) $sized = $sized/1024; + return round($sized,$precision)." ".$units[$unit]; +} + +// From WP 2.7.0 for backwards compatibility +function shopp_print_column_headers( $type, $id = true ) { + global $wp_version; + if (version_compare($wp_version,"2.7.0",">=")) + return print_column_headers($type,$id); + + $type = str_replace('.php', '', $type); + $columns = shopp_get_column_headers( $type ); + $hidden = array(); + $styles = array(); + + foreach ( $columns as $column_key => $column_display_name ) { + $class = ' class="manage-column'; + $class .= " column-$column_key"; + + if ( 'cb' == $column_key ) $class .= ' check-column'; + elseif ( in_array($column_key, array('posts', 'comments', 'links')) ) + $class .= ' num'; + + $class .= '"'; + + $style = ''; + if ( in_array($column_key, $hidden) ) + $style = 'display:none;'; + + if ( isset($styles[$type]) && isset($styles[$type][$column_key]) ) + $style .= ' ' . $styles[$type][$column_key]; + $style = ' style="' . $style . '"'; +?> + > +=")) + return register_column_headers($screen,$columns); + + global $_wp_column_headers; + + if ( !isset($_wp_column_headers) ) + $_wp_column_headers = array(); + + $_wp_column_headers[$screen] = $columns; +} + +// Adapted from WP 2.7.0 for backwards compatibility +function shopp_get_column_headers($page) { + global $_wp_column_headers; + + if ( !isset($_wp_column_headers) ) + $_wp_column_headers = array(); + + // Store in static to avoid running filters on each call + if ( isset($_wp_column_headers[$page]) ) + return $_wp_column_headers[$page]; + + return array(); +} + +function copy_shopp_templates ($src,$target) { + $builtin = array_filter(scandir($src),"filter_dotfiles"); + foreach ($builtin as $template) { + $target_file = $target.DIRECTORY_SEPARATOR.$template; + if (!file_exists($target_file)) { + $src_file = file_get_contents($src.DIRECTORY_SEPARATOR.$template); + $file = fopen($target_file,'w'); + $src_file = preg_replace('/^<\?php\s\/\*\*\s+(.*?\s)*?\*\*\/\s\?>\s/','',$src_file); + fwrite($file,$src_file); + fclose($file); + chmod($target_file,0666); + } + } +} + +/** + * Determines if the requested page is a Shopp page or if it matches a given Shopp page + * + * @param string $page (optional) Page name to look for in Shopp's page registry + * @return boolean + * @author Jonathan Davis + **/ +function is_shopp_page ($page=false) { + global $Shopp,$wp_query; + + if ($wp_query->post->post_type != "page") return false; + + $pages = $Shopp->Settings->get('pages'); + + // Detect if the requested page is a Shopp page + if (!$page) { + foreach ($pages as $page) + if ($page['id'] == $wp_query->post->ID) return true; + return false; + } + + // Determine if the visitor's requested page matches the provided page + if (!isset($pages[strtolower($page)])) return false; + $page = $pages[strtolower($page)]; + if ($page['id'] == $wp_query->post->ID) return true; + return false; +} + +function is_shopp_secure () { + return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on"); +} + +function template_path ($path) { + if (DIRECTORY_SEPARATOR == "\\") $path = str_replace("/","\\",$path); + return $path; +} + +function gateway_path ($file) { + return basename(dirname($file)).DIRECTORY_SEPARATOR.basename($file); +} + +function force_ssl ($url) { + if(isset($_SERVER['HTTPS']) && $_SERVER["HTTPS"] == "on") + $url = str_replace('http://', 'https://', $url); + return $url; +} + +if ( !function_exists('sys_get_temp_dir')) { + // For PHP 5 (pre-5.2.1) + function sys_get_temp_dir() { + if (!empty($_ENV['TMP'])) return realpath($_ENV['TMP']); + if (!empty($_ENV['TMPDIR'])) return realpath( $_ENV['TMPDIR']); + if (!empty($_ENV['TEMP'])) return realpath( $_ENV['TEMP']); + $tempfile = tempnam(uniqid(rand(),TRUE),''); + if (file_exists($tempfile)) { + unlink($tempfile); + return realpath(dirname($tempfile)); + } + } +} + +class FTPClient { + var $connected = false; + var $log = array(); + var $remapped = false; + + function FTPClient ($host, $user, $password) { + $this->connect($host, $user, $password); + if ($this->connected) ftp_pasv($this->connection,true); + else return false; + return true; + } + + /** + * Connects to the FTP server */ + function connect($host, $user, $password) { + $this->connection = @ftp_connect($host,0,20); + if (!$this->connection) return false; + $this->connected = @ftp_login($this->connection,$user,$password); + if (!$this->connected) return false; + return true; + } + + /** + * update() + * Recursively copies files from a src $path to the $remote path */ + function update ($path,$remote) { + if (is_dir($path)){ + $path = trailingslashit($path); + // $this->log[] = "The source path is $path"; + $files = scandir($path); + $remote = trailingslashit($remote); + // $this->log[] = "The destination path is $remote"; + } else { + $files = array(basename($path)); + $path = trailingslashit(dirname($path)); + // $this->log[] = "The source path is $path"; + $remote = trailingslashit(dirname($remote)); + // $this->log[] = "The destination path is $remote"; + } + + if (!$this->remapped) $remote = $this->remappath($remote); + // $this->log[] = "The remapped destination path is $remote"; + + $excludes = array(".",".."); + foreach ((array)$files as $file) { + if (in_array($file,$excludes)) continue; + if (is_dir($path.$file)) { + if (!@ftp_chdir($this->connection,$remote.$file)) + $this->mkdir($remote.$file); + $this->update($path.$file,$remote.$file); + } else $this->put($path.$file,$remote.$file); + } + return $this->log; + } + + /** + * delete() + * Delete the target file, recursively delete directories */ + function delete ($file) { + if (empty($file)) return false; + if (!$this->isdir($file)) return @ftp_delete($this->connection, $file); + $files = $this->scan($file); + if (!empty($files)) foreach ($files as $target) $this->delete($target); + return @ftp_rmdir($this->connection, $file); + } + + /** + * put() + * Copies the target file to the remote location */ + function put ($file,$remote) { + if (@ftp_put($this->connection,$remote,$file,FTP_BINARY)) + return @ftp_chmod($this->connection, 0644, $remote); + else $this->log[] = "Could not move the file from $file to $remote"; + } + + /** + * mkdir() + * Makes a new remote directory with correct permissions */ + function mkdir ($path) { + if (@ftp_mkdir($this->connection,$path)) + @ftp_chmod($this->connection,0755,$path); + else $this->log[] = "Could not create the directory $path"; + } + + /** + * mkdir() + * Gets the current directory */ + function pwd () { + return ftp_pwd($this->connection); + } + + /** + * scan() + * Gets a list of files in a directory/current directory */ + function scan ($path=false) { + if (!$path) $path = $this->pwd(); + return @ftp_nlist($this->connection,$path); + } + + /** + * isdir() + * Determines if the file is a directory or a file */ + function isdir ($file=false) { + if (!$file) $file = $this->pwd(); + if (@ftp_size($this->connection, $file) == '-1') + return true; // Directory + else return false; // File + } + + /** + * remappath() + * Remap a given path to the root path of the FTP server + * to take into account root jails common in FTP setups */ + function remappath ($path) { + $files = $this->scan(); + foreach ($files as $file) { + $filepath = trailingslashit($this->pwd()).basename($file); + if (!$this->isdir($filepath)) continue; + $index = strrpos($path,$filepath); + if ($index !== false) { + $this->remapped = true; + return substr($path,$index); + } + } + // No remapping needed + return $path; + } + +} + +if (function_exists('date_default_timezone_set')) + date_default_timezone_set(get_option('timezone_string')); + +shopp_prereqs(); // Run by default at include + +?> \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/init.php b/blog/wp-content/plugins/shopp/core/init.php new file mode 100644 index 0000000..fb45881 --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/init.php @@ -0,0 +1,214 @@ +__('Canada','Shopp'),'currency'=>array('code'=>'CAD','format'=>'$#,###.##'),'units'=>'metric','region'=>0); + $countries['US'] = array('name'=>__('USA','Shopp'),'currency'=>array('code'=>'USD','format'=>'$#,###.##'),'units'=>'imperial','region'=>0); + $countries['GB'] = array('name'=>__('United Kingdom','Shopp'),'currency'=>array('code'=>'GBP','format'=>'£#,###.##'),'units'=>'metric','region'=>3); + $countries['AR'] = array('name'=>__('Argentina','Shopp'),'currency'=>array('code'=>'ARS','format'=>'$#.###,##'),'units'=>'metric','region'=>7); + $countries['AU'] = array('name'=>__('Australia','Shopp'),'currency'=>array('code'=>'AUD','format'=>'$# ###.##'),'units'=>'metric','region'=>7); + $countries['AT'] = array('name'=>__('Austria','Shopp'),'currency'=>array('code'=>'EUR','format'=>'€#,###.##'),'units'=>'metric','region'=>3); + $countries['BS'] = array('name'=>__('Bahamas','Shopp'),'currency'=>array('code'=>'BSD','format'=>'$#,###.##'),'units'=>'metric','region'=>0); + $countries['BE'] = array('name'=>__('Belgium','Shopp'),'currency'=>array('code'=>'EUR','format'=>'€#.###,##'),'units'=>'metric','region'=>3); + $countries['BR'] = array('name'=>__('Brazil','Shopp'),'currency'=>array('code'=>'BRL','format'=>'R$ #.###,##'),'units'=>'metric','region'=>2); + $countries['BG'] = array('name'=>__('Bulgaria','Shopp'),'currency'=>array('code'=>'BGN','format'=>'# ###,## лв.'),'units'=>'metric','region'=>3); + $countries['CL'] = array('name'=>__('Chile','Shopp'),'currency'=>array('code'=>'CLP','format'=>'$#.###'),'units'=>'metric','region'=>2); + $countries['CN'] = array('name'=>__('China','Shopp'),'currency'=>array('code'=>'CNY','format'=>'¥#,###.##'),'units'=>'metric','region'=>6); + $countries['CO'] = array('name'=>__('Colombia','Shopp'),'currency'=>array('code'=>'COP','format'=>'$#.###,##'),'units'=>'metric','region'=>2); + $countries['CR'] = array('name'=>__('Costa Rica','Shopp'),'currency'=>array('code'=>'CRC','format'=>'¢ #.###,##'),'units'=>'metric','region'=>1); + $countries['HR'] = array('name'=>__('Croatia','Shopp'),'currency'=>array('code'=>'HRK','format'=>'#.###,## kn'),'units'=>'metric','region'=>3); + $countries['CY'] = array('name'=>__('Cyprus','Shopp'),'currency'=>array('code'=>'CYP','format'=>'£#.###,##'),'units'=>'metric','region'=>3); + $countries['CZ'] = array('name'=>__('Czech Republic','Shopp'),'currency'=>array('code'=>'CZK','format'=>'#.###,## Kc'),'units'=>'metric','region'=>3); + $countries['DK'] = array('name'=>__('Denmark','Shopp'),'currency'=>array('code'=>'DKK','format'=>'DKK #.###,##'),'units'=>'metric','region'=>3); + $countries['EC'] = array('name'=>__('Ecuador','Shopp'),'currency'=>array('code'=>'ESC','format'=>'$#,###.##'),'units'=>'metric','region'=>2); + $countries['EE'] = array('name'=>__('Estonia','Shopp'),'currency'=>array('code'=>'EEK','format'=>'# ###,## EEK'),'units'=>'metric','region'=>3); + $countries['FI'] = array('name'=>__('Finland','Shopp'),'currency'=>array('code'=>'EUR','format'=>'€#,###.##'),'units'=>'metric','region'=>3); + $countries['FR'] = array('name'=>__('France','Shopp'),'currency'=>array('code'=>'EUR','format'=>'€#,###.##'),'units'=>'metric','region'=>3); + $countries['DE'] = array('name'=>__('Germany','Shopp'),'currency'=>array('code'=>'EUR','format'=>'€#,###.##'),'units'=>'metric','region'=>3); + $countries['GR'] = array('name'=>__('Greece','Shopp'),'currency'=>array('code'=>'EUR','format'=>'€#,###.##'),'units'=>'metric','region'=>3); + $countries['GP'] = array('name'=>__('Guadeloupe','Shopp'),'currency'=>array('code'=>'EUR','format'=>'€#,###.##'),'units'=>'metric','region'=>3); + $countries['HK'] = array('name'=>__('Hong Kong','Shopp'),'currency'=>array('code'=>'HKD','format'=>'HK$#,###.##'),'units'=>'metric','region'=>6); + $countries['HU'] = array('name'=>__('Hungary','Shopp'),'currency'=>array('code'=>'HUF','format'=>'#t.### Ft'),'units'=>'metric','region'=>3); + $countries['IS'] = array('name'=>__('Iceland','Shopp'),'currency'=>array('code'=>'ISK','format'=>'#t.### kr.'),'units'=>'metric','region'=>3); + $countries['IN'] = array('name'=>__('India','Shopp'),'currency'=>array('code'=>'INR','format'=>'Rs. #,##,###.##'),'units'=>'metric','region'=>6); + $countries['ID'] = array('name'=>__('Indonesia','Shopp'),'currency'=>array('code'=>'IDR','format'=>'Rp. #.###,##'),'units'=>'metric','region'=>7); + $countries['IE'] = array('name'=>__('Ireland','Shopp'),'currency'=>array('code'=>'EUR','format'=>'€#,###.##'),'units'=>'metric','region'=>3); + $countries['IL'] = array('name'=>__('Israel','Shopp'),'currency'=>array('code'=>'ILS','format'=>'#,###.## NIS'),'units'=>'metric','region'=>4); + $countries['IT'] = array('name'=>__('Italy','Shopp'),'currency'=>array('code'=>'EUR','format'=>'€#,###.##'),'units'=>'metric','region'=>3); + $countries['JM'] = array('name'=>__('Jamaica','Shopp'),'currency'=>array('code'=>'JMD','format'=>'$#,###.##'),'units'=>'metric','region'=>0); + $countries['JP'] = array('name'=>__('Japan','Shopp'),'currency'=>array('code'=>'JPY','format'=>'¥#,###'),'units'=>'metric','region'=>6); + $countries['LV'] = array('name'=>__('Latvia','Shopp'),'currency'=>array('code'=>'LVL','format'=>'Ls #,###.##'),'units'=>'metric','region'=>3); + $countries['LT'] = array('name'=>__('Lithuania','Shopp'),'currency'=>array('code'=>'LTL','format'=>'# ###,## Lt'),'units'=>'metric','region'=>3); + $countries['LU'] = array('name'=>__('Luxembourg','Shopp'),'currency'=>array('code'=>'EUR','format'=>'€#,###.##'),'units'=>'metric','region'=>3); + $countries['MY'] = array('name'=>__('Malaysia','Shopp'),'currency'=>array('code'=>'MYR','format'=>'RM#,###.##'),'units'=>'metric','region'=>6); + $countries['MT'] = array('name'=>__('Malta','Shopp'),'currency'=>array('code'=>'MTL','format'=>'Lm#,###.##'),'units'=>'metric','region'=>3); + $countries['MX'] = array('name'=>__('Mexico','Shopp'),'currency'=>array('code'=>'MXN','format'=>'$ #,###.##'),'units'=>'metric','region'=>0); + $countries['NL'] = array('name'=>__('Netherlands','Shopp'),'currency'=>array('code'=>'EUR','format'=>'€#.###,##'),'units'=>'metric','region'=>3); + $countries['NZ'] = array('name'=>__('New Zealand','Shopp'),'currency'=>array('code'=>'NZD','format'=>'$#,###.##'),'units'=>'metric','region'=>7); + $countries['NO'] = array('name'=>__('Norway','Shopp'),'currency'=>array('code'=>'NOK','format'=>'kr #.###,##'),'units'=>'metric','region'=>3); + $countries['PE'] = array('name'=>__('Peru','Shopp'),'currency'=>array('code'=>'PEN','format'=>'S/. #,###.##'),'units'=>'metric','region'=>2); + $countries['PH'] = array('name'=>__('Philippines','Shopp'),'currency'=>array('code'=>'PHP','format'=>'PHP#,###.##'),'units'=>'metric','region'=>6); + $countries['PL'] = array('name'=>__('Poland','Shopp'),'currency'=>array('code'=>'PLZ','format'=>'#.###,## zł'),'units'=>'metric','region'=>3); + $countries['PT'] = array('name'=>__('Portugal','Shopp'),'currency'=>array('code'=>'EUR','format'=>'€#,###.##'),'units'=>'metric','region'=>3); + $countries['PR'] = array('name'=>__('Puerto Rico','Shopp'),'currency'=>array('code'=>'USD','format'=>'$#,###.##'),'units'=>'imperial','region'=>0); + $countries['RO'] = array('name'=>__('Romania','Shopp'),'currency'=>array('code'=>'ROL','format'=>'#.###,## lei'),'units'=>'metric','region'=>3); + $countries['RU'] = array('name'=>__('Russia','Shopp'),'currency'=>array('code'=>'RUB','format'=>'RUB#.###,##'),'units'=>'metric','region'=>6); + $countries['SG'] = array('name'=>__('Singapore','Shopp'),'currency'=>array('code'=>'SGD','format'=>'$#,###.##'),'units'=>'metric','region'=>6); + $countries['SK'] = array('name'=>__('Slovakia','Shopp'),'currency'=>array('code'=>'EUR','format'=>'€#,###.##'),'units'=>'metric','region'=>3); + $countries['SI'] = array('name'=>__('Slovenia','Shopp'),'currency'=>array('code'=>'EUR','format'=>'€#,###.##'),'units'=>'metric','region'=>3); + $countries['ZA'] = array('name'=>__('South Africa','Shopp'),'currency'=>array('code'=>'ZAR','format'=>'R # ###.##'),'units'=>'metric','region'=>5); + $countries['KR'] = array('name'=>__('South Korea','Shopp'),'currency'=>array('code'=>'KRW','format'=>'₩#,###'),'units'=>'metric','region'=>6); + $countries['ES'] = array('name'=>__('Spain','Shopp'),'currency'=>array('code'=>'EUR','format'=>'€#,###.##'),'units'=>'metric','region'=>3); + $countries['VC'] = array('name'=>__('St. Vincent','Shopp'),'currency'=>array('code'=>'XCD','format'=>'$#,###.##'),'units'=>'metric','region'=>6); + $countries['SE'] = array('name'=>__('Sweden','Shopp'),'currency'=>array('code'=>'SEK','format'=>'#.###,## kr'),'units'=>'metric','region'=>3); + $countries['CH'] = array('name'=>__('Switzerland','Shopp'),'currency'=>array('code'=>'CHF','format'=>"SFr. #'###.##"),'units'=>'metric','region'=>3); + $countries['SY'] = array('name'=>__('Syria','Shopp'),'currency'=>array('code'=>'SYP','format'=>'#,###.## SYP'),'units'=>'metric','region'=>4); + $countries['TW'] = array('name'=>__('Taiwan','Shopp'),'currency'=>array('code'=>'TWD','format'=>'$#,###.##'),'units'=>'metric','region'=>6); + $countries['TH'] = array('name'=>__('Thailand','Shopp'),'currency'=>array('code'=>'THB','format'=>'#,###.## Bt'),'units'=>'metric','region'=>6); + $countries['TT'] = array('name'=>__('Trinidad and Tobago','Shopp'),'currency'=>array('code'=>'TTD','format'=>'TT$#,###.##'),'units'=>'metric','region'=>0); + $countries['TR'] = array('name'=>__('Turkey','Shopp'),'currency'=>array('code'=>'TRL','format'=>'#,###.## TL'),'units'=>'metric','region'=>4); + $countries['AE'] = array('name'=>__('United Arab Emirates','Shopp'),'currency'=>array('code'=>'AED','format'=>'Dhs. #,###.##'),'units'=>'metric','region'=>4); + $countries['UY'] = array('name'=>__('Uruguay','Shopp'),'currency'=>array('code'=>'UYP','format'=>'$#,###.##'),'units'=>'metric','region'=>2); + $countries['VE'] = array('name'=>__('Venezuela','Shopp'),'currency'=>array('code'=>'VUB','format'=>'Bs. #,###.##'),'units'=>'metric','region'=>2); + return $countries; +} + +/** + * State/Province/Territory zone names + * 2 KB in the database */ +function get_country_zones() { + $zones = array(); + $zones['AU'] = array(); + $zones['AU']['NSW'] = 'New South Wales'; + $zones['AU']['NT'] = 'Northern Territory'; + $zones['AU']['QLD'] = 'Queensland'; + $zones['AU']['SA'] = 'South Australia'; + $zones['AU']['TAS'] = 'Tasmania'; + $zones['AU']['VIC'] = 'Victoria'; + $zones['AU']['WA'] = 'Western Australia'; + + $zones['CA'] = array(); + $zones['CA']['AB'] = 'Alberta'; + $zones['CA']['BC'] = 'British Columbia'; + $zones['CA']['MB'] = 'Manitoba'; + $zones['CA']['NB'] = 'New Brunswick'; + $zones['CA']['NF'] = 'Newfoundland'; + $zones['CA']['NT'] = 'Northwest Territories'; + $zones['CA']['NS'] = 'Nova Scotia'; + $zones['CA']['NU'] = 'Nunavut'; + $zones['CA']['ON'] = 'Ontario'; + $zones['CA']['PE'] = 'Prince Edward Island'; + $zones['CA']['PQ'] = 'Quebec'; + $zones['CA']['SK'] = 'Saskatchewan'; + $zones['CA']['YT'] = 'Yukon Territory'; + + $zones['US'] = array(); + $zones['US']['AL'] = 'Alabama'; + $zones['US']['AK'] = 'Alaska '; + $zones['US']['AZ'] = 'Arizona'; + $zones['US']['AR'] = 'Arkansas'; + $zones['US']['CA'] = 'California '; + $zones['US']['CO'] = 'Colorado'; + $zones['US']['CT'] = 'Connecticut'; + $zones['US']['DE'] = 'Delaware'; + $zones['US']['DC'] = 'District Of Columbia '; + $zones['US']['FL'] = 'Florida'; + $zones['US']['GA'] = 'Georgia '; + $zones['US']['HI'] = 'Hawaii'; + $zones['US']['ID'] = 'Idaho'; + $zones['US']['IL'] = 'Illinois'; + $zones['US']['IN'] = 'Indiana'; + $zones['US']['IA'] = 'Iowa'; + $zones['US']['KS'] = 'Kansas'; + $zones['US']['KY'] = 'Kentucky'; + $zones['US']['LA'] = 'Louisiana'; + $zones['US']['ME'] = 'Maine'; + $zones['US']['MD'] = 'Maryland'; + $zones['US']['MA'] = 'Massachusetts'; + $zones['US']['MI'] = 'Michigan'; + $zones['US']['MN'] = 'Minnesota'; + $zones['US']['MS'] = 'Mississippi'; + $zones['US']['MO'] = 'Missouri'; + $zones['US']['MT'] = 'Montana'; + $zones['US']['NE'] = 'Nebraska'; + $zones['US']['NV'] = 'Nevada'; + $zones['US']['NH'] = 'New Hampshire'; + $zones['US']['NJ'] = 'New Jersey'; + $zones['US']['NM'] = 'New Mexico'; + $zones['US']['NY'] = 'New York'; + $zones['US']['NC'] = 'North Carolina'; + $zones['US']['ND'] = 'North Dakota'; + $zones['US']['OH'] = 'Ohio'; + $zones['US']['OK'] = 'Oklahoma'; + $zones['US']['OR'] = 'Oregon'; + $zones['US']['PA'] = 'Pennsylvania'; + $zones['US']['RI'] = 'Rhode Island'; + $zones['US']['SC'] = 'South Carolina'; + $zones['US']['SD'] = 'South Dakota'; + $zones['US']['TN'] = 'Tennessee'; + $zones['US']['TX'] = 'Texas'; + $zones['US']['UT'] = 'Utah'; + $zones['US']['VT'] = 'Vermont'; + $zones['US']['VA'] = 'Virginia'; + $zones['US']['WA'] = 'Washington'; + $zones['US']['WV'] = 'West Virginia'; + $zones['US']['WI'] = 'Wisconsin'; + $zones['US']['WY'] = 'Wyoming'; + return $zones; +} + +/** + * Domestic areas for US and Canada mapped by postcode + * 3 KB in the database */ +function get_country_areas () { + $areas = array(); + $areas['CA'] = array(); + $areas['CA']['Northern Canada'] = array('YT'=>array('Y'),'NT'=>array('X'),'NU'=>array('X')); + $areas['CA']['Western Canada'] = array('BC'=>array('V'),'AB'=>array('T'),'SK'=>array('S'),'MB'=>array('R')); + $areas['CA']['Eastern Canada'] = array('OT'=>array('K','L','M','N','P'),'PQ'=>array('G','H','J'),'NB'=>array('E'),'PE'=>array('C'),'NS'=>array('B'),'NF'=>array('A')); + + $areas['US'] = array(); + $areas['US']['Northeast US'] = array('MA'=>array('01000','02799','05500','05599'),'RI'=>array('02800','02999'),'NH'=>array('03000','03999'),'ME'=>array('03900','04999'),'VT'=>array('05000','05999'),'CT'=>array('06000','06999'),'NJ'=>array('07000','08999'),'NY'=>array('09000','14999','00500','00599','06300','06399'),'PA'=>array('15000','19699')); + $areas['US']['Midwest US'] = array('OH'=>array('43000','45999'),'IN'=>array('46000','47999'),'MI'=>array('48000','49999'),'IA'=>array('50000','52899'),'WI'=>array('53000','54999'),'MN'=>array('55000','56799'),'SD'=>array('57000','57799'),'ND'=>array('58000','58899'),'IL'=>array('60000','62999'),'MO'=>array('63000','65899'),'KS'=>array('66000','67999'),'NE'=>array('68000','69399')); + $areas['US']['South US'] =array('DE'=>array('19700','19999'),'DC'=>array('20000','20599'),'MD'=>array('20600','21999'),'VA'=>array('22000','24699','20100','20199'),'WV'=>array('24700','26899'),'NC'=>array('26900','28999'),'SC'=>array('29000','29999'),'GA'=>array('30000','31999','39800','39999'),'FL'=>array('32000','34999'),'AL'=>array('35000','36999'),'TN'=>array('37000','38599'),'MS'=>array('38600','39799'),'KY'=>array('40000','42799'),'LA'=>array('70000','71499'),'AR'=>array('71600','72999','75500','75599'),'OK'=>array('73000','74999'),'TX'=>array('75000','79999','88500','88599')); + $areas['US']['West US'] =array('MT'=>array('59000','59999'),'CO'=>array('80000','81699'),'WY'=>array('82000','83199'),'ID'=>array('83200','83899'),'UT'=>array('84000','84799'),'AZ'=>array('85000','86599'),'NM'=>array('87000','88499'),'NV'=>array('88900','89899'),'CA'=>array('90000','96699'),'HI'=>array('96700','96899'),'OR'=>array('97000','97999'),'WA'=>array('98000','99499'),'AK'=>array('99500','99999')); + return $areas; +} + +function get_vat_countries () { + $vat = array( + 'BE','BG','CZ','DK','DE','EE','GR','ES','FR', + 'IE','IT','CY','LV','LT','LU','HU','MT','NL', + 'AT','PL','PT','RO','SI','SK','FI','SE','GB' + ); + return $vat; +} + +?> \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/install.php b/blog/wp-content/plugins/shopp/core/install.php new file mode 100644 index 0000000..b17150b --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/install.php @@ -0,0 +1,62 @@ +loaddata($schema); +unset($schema); + +$parent = 0; +foreach ($this->Flow->Pages as $key => &$page) { + if (!empty($this->Flow->Pages['catalog']['id'])) $parent = $this->Flow->Pages['catalog']['id']; + $query = "INSERT $wpdb->posts SET post_title='{$page['title']}', + post_name='{$page['name']}', + post_content='{$page['content']}', + post_parent='$parent', + post_author='1', + post_status='publish', + post_type='page', + post_date=now(), + post_date_gmt=utc_timestamp(), + post_modified=now(), + post_modified_gmt=utc_timestamp(), + comment_status='closed', + ping_status='closed', + post_excerpt='', + to_ping='', + pinged='', + post_content_filtered='', + menu_order=0"; + + + $wpdb->query($query); + $page['id'] = $wpdb->insert_id; + $page['permalink'] = get_permalink($page['id']); + if ($key == "checkout") $page['permalink'] = str_replace("http://","https://",$page['permalink']); + $wpdb->query("UPDATE $wpdb->posts SET guid='{$page['permalink']}' WHERE ID={$page['id']}"); + $page['permalink'] = preg_replace('|https?://[^/]+/|i','',$page['permalink']); +} + +$this->Settings->save("pages",$this->Flow->Pages); + +?> \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/model/Asset.php b/blog/wp-content/plugins/shopp/core/model/Asset.php new file mode 100644 index 0000000..10e9134 --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/model/Asset.php @@ -0,0 +1,212 @@ +init(self::$table); + if ($this->load($id,$key)) return true; + else return false; + } + + function setstorage ($type=false) { + global $Shopp; + if (!$type) $type = $this->datatype; + switch ($type) { + case "image": + case "small": + case "thumbnail": + $this->storage = $Shopp->Settings->get('image_storage'); + $this->path = trailingslashit($Shopp->Settings->get('image_path')); + break; + case "download": + $this->storage = $Shopp->Settings->get('product_storage'); + $this->path = trailingslashit($Shopp->Settings->get('products_path')); + break; + } + } + + /** + * Save a record, updating when we have a value for the primary key, + * inserting a new record when we don't */ + function save () { + $db =& DB::get(); + + $data = $db->prepare($this); + $id = $this->{$this->_key}; + + $this->setstorage(); + + // Hook for outputting files to filesystem + if ($this->storage == "fs") { + if (!$this->savefile()) return false; + unset($data['data']); // Keep from duplicating data in DB + } + + // Update record + if (!empty($id)) { + if (isset($data['modified'])) $data['modified'] = "now()"; + $dataset = $this->dataset($data); + $db->query("UPDATE $this->_table SET $dataset WHERE $this->_key=$id"); + return true; + // Insert new record + } else { + if (isset($data['created'])) $data['created'] = "now()"; + if (isset($data['modified'])) $data['modified'] = "now()"; + $dataset = $this->dataset($data); + $this->id = $db->query("INSERT $this->_table SET $dataset"); + return $this->id; + } + } + + function savedata ($file) { + $db =& DB::get(); + + $id = $this->{$this->_key}; + if (!$id) return false; + + $handle = fopen($file, "r"); + while (!feof($handle)) { + $buffer = mysql_real_escape_string(fread($handle, 65535)); + $query = "UPDATE $this->_table SET data=CONCAT(data,'$buffer') WHERE $this->_key=$id"; + $db->query($query); + } + fclose($handle); + } + + function savefile () { + if (empty($this->data)) return true; + if (file_put_contents($this->path.$this->name,stripslashes($this->data)) > 0) return true; + return false; + } + + function deleteset ($keys,$type="image") { + $db =& DB::get(); + + if ($type == "image") $this->setstorage('image'); + if ($type == "download") $this->setstorage('download'); + + if ($this->storage == "fs") $this->deletefiles($keys); + + $selection = ""; + foreach ($keys as $value) + $selection .= ((!empty($selection))?" OR ":"")."{$this->_key}=$value OR src=$value"; + + $query = "DELETE LOW_PRIORITY FROM $this->_table WHERE $selection"; + $db->query($query); + } + + /** + * deletefiles () + * Remove files from the file system only when 1 reference to the file exists + * in file references in the database, otherwise, leave them **/ + function deletefiles ($keys) { + $db =& DB::get(); + + $selection = ""; + foreach ($keys as $value) + $selection .= ((!empty($selection))?" OR ":"")."f.{$this->_key}=$value OR f.src=$value"; + + $query = "SELECT f.name,count(DISTINCT links.id) AS refs FROM $this->_table AS f LEFT JOIN $this->_table AS links ON f.name=links.name WHERE $selection GROUP BY links.name"; + $files = $db->query($query,AS_ARRAY); + + foreach ($files as $file) + if ($file->refs == 1 && file_exists($this->path.$file->name)) + unlink($this->path.$file->name); + + return true; + } + + function download ($dkey=false) { + $this->setstorage('download'); + // Close the session in case of long download + @session_write_close(); + + // Don't want interference from the server + if (function_exists('apache_setenv')) @apache_setenv('no-gzip', 1); + @ini_set('zlib.output_compression', 0); + + set_time_limit(0); // Don't timeout on long downloads + ob_end_clean(); // End any automatic output buffering + + header("Pragma: public"); + header("Cache-Control: maxage=1"); + header("Content-type: application/octet-stream"); + header("Content-Disposition: attachment; filename=\"".$this->name."\""); + header("Content-Description: Delivered by WordPress/Shopp ".SHOPP_VERSION); + + // File System based download - handles very large files, supports resumable downloads + if ($this->storage == "fs") { + if (!empty($this->value)) $filepath = join("/",array($this->path,$this->value,$this->name)); + else $filepath = join("/",array($this->path,$this->name)); + + if (!is_file($filepath)) { + header("Status: 404 Forbidden"); // File not found?! + return false; + } + + $size = @filesize($filepath); + + // Handle resumable downloads + if (isset($_SERVER['HTTP_RANGE'])) { + list($units, $reqrange) = explode('=', $_SERVER['HTTP_RANGE'], 2); + if ($units == 'bytes') { + // Use first range - http://tools.ietf.org/id/draft-ietf-http-range-retrieval-00.txt + list($range, $extra) = explode(',', $reqrange, 2); + } else $range = ''; + } else $range = ''; + + // Determine download chunk to grab + list($start, $end) = explode('-', $range, 2); + + // Set start and end based on range (if set), or set defaults + // also check for invalid ranges. + $end = (empty($end)) ? ($size - 1) : min(abs(intval($end)),($size - 1)); + $start = (empty($start) || $end < abs(intval($start))) ? 0 : max(abs(intval($start)),0); + + // Only send partial content header if downloading a piece of the file (IE workaround) + if ($start > 0 || $end < ($size - 1)) header('HTTP/1.1 206 Partial Content'); + + header('Accept-Ranges: bytes'); + header('Content-Range: bytes '.$start.'-'.$end.'/'.$size); + header('Content-length: '.($end-$start+1)); + + // WebKit/Safari resumable download support headers + header('Last-modified: '.date('D, d M Y H:i:s O',$this->modified)); + if (isset($dkey)) header('ETag: '.$dkey); + + $file = fopen($filepath, 'rb'); + fseek($file, $start); + $packet = 1024*1024; + while(!feof($file)) { + if (connection_status() !== 0) return false; + $buffer = fread($file,$packet); + if (!empty($buffer)) echo $buffer; + ob_flush(); flush(); + } + fclose($file); + return true; + } else { + // Database file download - short and sweet + header ("Content-length: ".$this->size); + echo $this->data; + return true; + } + + } + +} // end Asset class + +?> \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/model/Billing.php b/blog/wp-content/plugins/shopp/core/model/Billing.php new file mode 100644 index 0000000..1716782 --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/model/Billing.php @@ -0,0 +1,35 @@ +init(self::$table); + if ($this->load($id,$key)) return true; + else return false; + } + + function exportcolumns () { + $prefix = "b."; + return array( + $prefix.'address' => __('Billing Street Address','Shopp'), + $prefix.'xaddress' => __('Billing Street Address 2','Shopp'), + $prefix.'city' => __('Billing City','Shopp'), + $prefix.'state' => __('Billing State/Province','Shopp'), + $prefix.'country' => __('Billing Country','Shopp'), + $prefix.'postcode' => __('Billing Postal Code','Shopp'), + ); + } + +} // end Billing class + +?> \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/model/Cart.php b/blog/wp-content/plugins/shopp/core/model/Cart.php new file mode 100644 index 0000000..e58c8b6 --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/model/Cart.php @@ -0,0 +1,1904 @@ +_table = DatabaseObject::tablename('cart'); + + // Close out any early session calls + if(session_id()) session_write_close(); + + $this->handlers = session_set_save_handler( + array( &$this, 'open' ), // Open + array( &$this, 'close' ), // Close + array( &$this, 'load' ), // Read + array( &$this, 'save' ), // Write + array( &$this, 'unload' ), // Destroy + array( &$this, 'trash' ) // Garbage Collection + ); + register_shutdown_function('session_write_close'); + + define('SHOPP_SECURE_KEY','shopp_sec_'.COOKIEHASH); + + $this->data = new stdClass(); // Session data + $this->data->Totals = new stdClass(); // Cart totals + $this->data->Totals->subtotal = 0; // Subtotal of item totals + $this->data->Totals->quantity = 0; // Total quantity of all items + $this->data->Totals->discount = 0; // Total discount applied + $this->data->Totals->shipping = 0; // Total shipping cost + $this->data->Totals->tax = 0; // Total tax cost + $this->data->Totals->taxrate = 0; // Current tax rate + $this->data->Totals->total = 0; // Grand total + + $this->data->Order = new stdClass(); // Order object + $this->data->Order->data = array(); // Custom order data registry + $this->data->Order->Customer = false; // Order's customer record + $this->data->Order->Billing = false; // Order's billing address record + $this->data->Order->Shipping = false; // Order's shipping address record + + $this->data->added = 0; // Recently added item index + $this->data->login = false; // Customer logged in flag + $this->data->secure = false; // Security flag + + $this->data->Errors = new ShoppErrors(); // Tracks errors + $this->data->Shipping = false; // Cart has shipped items + $this->data->ShippingDisabled = false; // Shipping is disabled + $this->data->Estimates = false; // Order needs shipping estimates + $this->data->Promotions = array(); // Promotions available (cache) + $this->data->PromosApplied = array(); // Promotions applied to order + $this->data->PromoCode = false; // Recent promo code attempt + $this->data->PromoCodes = array(); // Promo codes applied + $this->data->PromoCodeResult = false; // Result of recent promo code attempt + $this->data->ShipCosts = array(); // Shipping method costs + $this->data->ShippingPostcode = false; // Shipping calcs require postcode + $this->data->ShippingPostcodeError = false; // Postal code invalid error + $this->data->Purchase = false; // Final purchase receipt + $this->data->Category = array(); // Session related category settings + $this->data->Search = false; // Search processed + + // Total the cart once, and only if there are changes + add_action('parse_request',array(&$this,'totals'),99); + + } + + /* open() + * Initializing routine for the session management. */ + function open ($path,$name) { + $this->path = $path; + if (empty($this->path)) $this->path = dirname(realpath(tempnam('','tmp_'))); + $this->trash(); // Clear out any residual session information before loading new data + if (empty($this->session)) $this->session = session_id(); // Grab our session id + $this->ip = $_SERVER['REMOTE_ADDR']; // Save the IP address making the request + return true; + } + + /* close() + * Placeholder function as we are working with a persistant + * database as opposed to file handlers. */ + function close () { return true; } + + /* load() + * Gets data from the session data table and loads Member + * objects into the User from the loaded data. */ + function load ($id) { + global $Shopp; + $db = DB::get(); + + if (is_robot()) return true; + + $query = "SELECT * FROM $this->_table WHERE session='$this->session'"; + // echo "$query".BR; + + if ($result = $db->query($query)) { + if (substr($result->data,0,1) == "!") { + $key = $_COOKIE[SHOPP_SECURE_KEY]; + $readable = $db->query("SELECT AES_DECRYPT('". + mysql_real_escape_string( + base64_decode( + substr($result->data,1) + ) + )."','$key') AS data"); + $result->data = $readable->data; + } + $this->ip = $result->ip; + $this->data = unserialize($result->data); + if (empty($result->contents)) $this->contents = array(); + else $this->contents = unserialize($result->contents); + $this->created = mktimestamp($result->created); + $this->modified = mktimestamp($result->modified); + } else { + $db->query("INSERT INTO $this->_table (session, ip, data, contents, created, modified) + VALUES ('$this->session','$this->ip','','',now(),now())"); + } + + if (empty($this->data->Errors)) $this->data->Errors = new ShoppErrors(); + if ($Shopp->Settings->get('shipping') == "off") $this->data->ShippingDisabled = true; + + // Read standard session data + if (file_exists("$this->path/sess_$id")) + return (string) file_get_contents("$this->path/sess_$id"); + + return true; + } + + /* unload() + * Deletes the session data from the database, unregisters the + * session and releases all the objects. */ + function unload () { + $db = DB::get(); + if (!$db->query("DELETE FROM $this->_table WHERE session='$this->session'")) + trigger_error("Could not clear session data."); + unset($this->session,$this->ip,$this->data,$this->contents); + return true; + } + + /* save() + * Save the session data to our session table in the database. */ + function save ($id,$session) { + global $Shopp; + $db = DB::get(); + + if (!$Shopp->Settings->unavailable) { + $data = $db->escape(addslashes(serialize($this->data))); + $contents = $db->escape(serialize($this->contents)); + + if ($this->secured() && is_shopp_secure()) { + if (!isset($_COOKIE[SHOPP_SECURE_KEY])) $key = $this->securekey(); + else $key = isset($_COOKIE[SHOPP_SECURE_KEY])?$_COOKIE[SHOPP_SECURE_KEY]:''; + if (!empty($key)) { + $secure = $db->query("SELECT AES_ENCRYPT('$data','$key') AS data"); + $data = "!".base64_encode($secure->data); + } + } + $query = "UPDATE $this->_table SET ip='$this->ip',data='$data',contents='$contents',modified=now() WHERE session='$this->session'"; + if (!$db->query($query)) + trigger_error("Could not save session updates to the database."); + + } + + // Save standard session data for compatibility + if (!empty($session)) { + if ($sf = fopen("$this->path/sess_$id","w")) { + $result = fwrite($sf, $session); + fclose($sf); + return $result; + } return false; + } + return true; + } + + /* trash() + * Garbage collection routine for cleaning up old and expired + * sessions. */ + function trash () { + $db = DB::get(); + + // 1800 seconds = 30 minutes, 3600 seconds = 1 hour + if (!$db->query("DELETE LOW_PRIORITY FROM $this->_table WHERE UNIX_TIMESTAMP(NOW())-UNIX_TIMESTAMP(modified) > ".SHOPP_SESSION_TIMEOUT)) + trigger_error("Could not delete cached session data."); + return true; + } + + /** + * add() + * Adds a product as an item to the cart */ + function add ($quantity,&$Product,&$Price,$category,$data=array()) { + + $NewItem = new Item($Product,$Price,$category,$data); + if (!$NewItem->valid()) return false; + + if (($item = $this->hasitem($NewItem)) !== false) { + $this->contents[$item]->add($quantity); + $this->added = $this->contents[$item]; + $this->data->added = $item; + } else { + $NewItem->quantity($quantity); + $this->contents[] = $NewItem; + $this->data->added = count($this->contents)-1; + $this->added = $this->contents[$this->data->added]; + if ($NewItem->shipping && !$this->data->ShippingDisabled) + $this->data->Shipping = true; + } + do_action_ref_array('shopp_cart_add_item',array(&$NewItem)); + + $this->updated(); + return true; + } + + /** + * remove() + * Removes an item from the cart */ + function remove ($item) { + array_splice($this->contents,$item,1); + $this->updated(); + return true; + } + + /** + * update() + * Changes the quantity of an item in the cart */ + function update ($item,$quantity) { + if (empty($this->contents)) return false; + if ($quantity == 0) return $this->remove($item); + elseif (isset($this->contents[$item])) { + $this->contents[$item]->quantity($quantity); + if ($this->contents[$item]->quantity == 0) $this->remove($item); + $this->updated(); + } + return true; + } + + /** + * updated() + * Changes the quantity of an item in the cart */ + function updated () { + $this->updated = true; + } + + /** + * clear() + * Empties the contents of the cart */ + function clear () { + $this->contents = array(); + return true; + } + + /** + * reset() + * Resets the entire session */ + function reset () { + $this->unload(); + $this->session = session_regenerate_id(); + } + + /** + * change() + * Changes an item to a different product/price variation */ + function change ($item,&$Product,$pricing) { + // Don't change anything if everything is the same + if ($this->contents[$item]->product == $Product->id && + $this->contents[$item]->price == $pricing) return true; + + // If the updated product and price variation match + // add the updated quantity of this item to the other item + // and remove this one + foreach ($this->contents as $id => $thisitem) { + if ($thisitem->product == $Product->id && $thisitem->price == $pricing) { + $this->update($id,$thisitem->quantity+$this->contents[$item]->quantity); + $this->remove($item); + $this->updated(); + return true; + } + } + + // No existing item, so change this one + $qty = $this->contents[$item]->quantity; + $category = $this->contents[$item]->category; + $this->contents[$item] = new Item($Product,$pricing,$category); + $this->contents[$item]->quantity($qty); + $this->updated(); + if ($this->contents[$item]->shipping && !$this->data->ShippingDisabled) + $this->data->Shipping = true; + + return true; + } + + /** + * hasitem() + * Determines if a specified item is already in this cart */ + function hasitem($NewItem) { + $i = 0; + foreach ($this->contents as $Item) { + if ($Item->product == $NewItem->product && + $Item->price == $NewItem->price && + (empty($NewItem->data) || + (serialize($Item->data) == serialize($NewItem->data)))) + return $i; + $i++; + } + return false; + } + + /** + * shipzone() + * Sets the shipping address location + * for calculating shipping estimates */ + function shipzone ($data) { + if (!isset($this->data->Order->Shipping)) + $this->data->Order->Shipping = new Shipping(); + $this->data->Order->Shipping->updates($data); + + // Update state if postcode changes for tax updates + if (isset($data['postcode'])) + $this->data->Order->Shipping->postarea(); + + if (!isset($this->data->Order->Billing)) + $this->data->Order->Billing = new Billing(); + $this->data->Order->Billing->updates($data); + + if (isset($data['region'])) { + $this->data->Order->Shipping->region = $data['region']; + $this->data->Order->Billing->region = $data['region']; + } + + if (!empty($data)) $this->updated(); + } + + /** + * shipping() + * Calulates shipping costs based on the contents + * of the cart and the currently available shipping + * location set with shipzone() */ + function shipping () { + if (!$this->data->Order->Shipping) return false; + if ($this->freeshipping) return 0; + + global $Shopp; + + $ShipCosts = &$this->data->ShipCosts; + $Shipping = $this->data->Order->Shipping; + $base = $Shopp->Settings->get('base_operations'); + $handling = $Shopp->Settings->get('order_shipfee'); + $methods = $Shopp->Settings->get('shipping_rates'); + if (!is_array($methods)) return 0; + + if (empty($Shipping->country)) $Shipping->country = $base['country']; + + if (!$this->retotal) { + + $fees = 0; + + // Calculate any product-specific shipping fee markups + $shipflag = false; + foreach ($this->contents as $Item) { + if ($Item->shipping) $shipflag = true; + if ($Item->shipfee > 0) $fees += ($Item->quantity * $Item->shipfee); + } + if ($shipflag) $this->data->Shipping = true; + else { + $this->data->Shipping = false; + return 0; + } + + // Add order handling fee + if ($handling > 0) $fees += $handling; + + $estimate = false; + foreach ($methods as $id => $option) { + if (isset($option['postcode-required'])) { + $this->data->ShippingPostcode = true; + if (empty($Shipping->postcode)) { + $this->data->ShippingPostcodeError = true; + new ShoppError(__('A postal code for calculating shipping estimates and taxes is required before you can proceed to checkout.','Shopp','cart_required_postcode',SHOPP_ERR)); + return null; + } else $this->data->ShippingPostcodeError = false; + } else { + $this->data->ShippingPostcode = false; + $this->data->ShippingPostcodeError = false; + } + + if ($Shipping->country == $base['country']) { + // Use country/domestic region + if (isset($option[$base['country']])) + $column = $base['country']; // Use the country rate + else $column = $Shipping->postarea(); // Try to get domestic regional rate + } else if (isset($option[$Shipping->region])) { + // Global region rate + $column = $Shipping->region; + } else { + // Worldwide shipping rate, last rate entry + end($option); + $column = key($option); + } + + list($ShipCalcClass,$process) = explode("::",$option['method']); + if (isset($Shopp->ShipCalcs->modules[$ShipCalcClass])) + $estimated = apply_filters('shopp_shipping_estimate', $Shopp->ShipCalcs->modules[$ShipCalcClass]->calculate( + $this, $fees, $option, $column)); + + if ($estimated === false) continue; // Skip the cost estimates + if (!$estimate || $estimated['cost'] < $estimate['cost']) + $estimate = $estimated; // Get lowest estimate + + } // end foreach ($methods) + + } // end if (!$this->retotal) + + if (!isset($ShipCosts[$this->data->Order->Shipping->method])) + $this->data->Order->Shipping->method = false; + + if (!empty($this->data->Order->Shipping->method)) + return $ShipCosts[$this->data->Order->Shipping->method]['cost']; + + $this->data->Order->Shipping->method = $estimate['name']; + + return $estimate['cost']; + } + + /** + * promotions() + * Matches, calculates and applies promotion discounts */ + function promotions () { + global $Shopp; + $db = DB::get(); + $limit = $Shopp->Settings->get('promo_limit'); + + // Load promotions if they've not yet been loaded + if (empty($this->data->Promotions)) { + $promo_table = DatabaseObject::tablename(Promotion::$table); + // Add date-based lookup too + $this->data->Promotions = $db->query("SELECT * FROM $promo_table WHERE scope='Order' AND ((status='enabled' AND UNIX_TIMESTAMP(starts) > 0 AND UNIX_TIMESTAMP(starts) < UNIX_TIMESTAMP() AND UNIX_TIMESTAMP(ends) > UNIX_TIMESTAMP()) OR status='enabled')",AS_ARRAY); + } + + $PromoCodeFound = false; $PromoCodeExists = false; $PromoLimit = false; + $this->data->PromosApplied = array(); + foreach ($this->data->Promotions as &$promo) { + if (!is_array($promo->rules)) + $promo->rules = unserialize($promo->rules); + + // Add quantity rule automatically for buy x get y promos + if ($promo->type == "Buy X Get Y Free") { + $promo->search = "all"; + if ($promo->rules[count($promo->rules)-1]['property'] != "Item quantity") { + $qtyrule = array( + 'property' => 'Item quantity', + 'logic' => "Is greater than", + 'value' => $promo->buyqty); + $promo->rules[] = $qtyrule; + } + } + + $items = array(); + + $match = false; + $rulematches = 0; + foreach ($promo->rules as $rule) { + $rulematch = false; + switch($rule['property']) { + case "Item name": + foreach ($this->contents as $id => &$Item) { + if (Promotion::match_rule($Item->name,$rule['logic'],$rule['value'], $rule['property'])) { + $items[$id] = &$Item; + $rulematch = true; + } + } + break; + case "Item quantity": + foreach ($this->contents as $id => &$Item) { + if (Promotion::match_rule(number_format($Item->quantity,0),$rule['logic'],$rule['value'], $rule['property'])) { + $items[$id] = &$Item; + $rulematch = true; + } + } + break; + case "Item amount": + foreach ($this->contents as $id => &$Item) { + if (Promotion::match_rule(number_format($Item->total,2),$rule['logic'],$rule['value'], $rule['property'])) { + $items[$id] = &$Item; + $rulematch = true; + } + } + break; + case "Total quantity": + if (Promotion::match_rule(number_format($this->data->Totals->quantity,0),$rule['logic'],$rule['value'], $rule['property'])) { + $rulematch = true; + } + break; + case "Shipping amount": + if (Promotion::match_rule(number_format($this->data->Totals->shipping,2),$rule['logic'],$rule['value'], $rule['property'])) { + $rulematch = true; + } + break; + case "Subtotal amount": + if (Promotion::match_rule(number_format($this->data->Totals->subtotal,2),$rule['logic'],$rule['value'], $rule['property'])) { + $rulematch = true; + } + break; + case "Promo code": + // Match previously applied codes + if (is_array($this->data->PromoCodes) && in_array($rule['value'],$this->data->PromoCodes)) { + $rulematch = true; + break; + } + // Match a new code + if (!empty($this->data->PromoCode)) { + if (Promotion::match_rule($this->data->PromoCode,$rule['logic'],$rule['value'], $rule['property'])) { + if (is_array($this->data->PromoCodes) && + !in_array($this->data->PromoCode, $this->data->PromoCodes)) { + $this->data->PromoCodes[] = $rule['value']; + $PromoCodeFound = $rule['value']; + } else $PromoCodeExists = true; + $this->data->PromoCode = false; + $rulematch = true; + } + } + break; + } + + if ($rulematch && $promo->search == "all") $rulematches++; + if ($rulematch && $promo->search == "any") { + $match = true; + break; // One matched, no need to match any more + } + } // end foreach ($promo->rules) + + if ($promo->search == "all" && $rulematches == count($promo->rules)) + $match = true; + + // Everything matches up, apply the promotion + if ($match && !$PromoLimit) { + // echo "Matched $promo->name".BR; + if (!empty($items)) { + $freeshipping = 0; + // Apply promo calculation to specific cart items + foreach ($items as $item) { + switch ($promo->type) { + case "Percentage Off": $this->data->Totals->discount += $item->total*($promo->discount/100); break; + case "Amount Off": $this->data->Totals->discount += $promo->discount; break; + case "Buy X Get Y Free": $this->data->Totals->discount += floor($item->quantity / ($promo->buyqty + $promo->getqty))*($item->unitprice); break; + case "Free Shipping": $freeshipping++; break; + } + } + if ($freeshipping == count($this->contents) && $promo->scope == "Order") $this->freeshipping = true; + else $this->freeshipping = false; + } else { + // Apply promo calculation to entire order + switch ($promo->type) { + case "Percentage Off": $this->data->Totals->discount += $this->data->Totals->subtotal*($promo->discount/100); break; + case "Amount Off": $this->data->Totals->discount += $promo->discount; break; + case "Free Shipping": $this->freeshipping = true; break; + } + } + $this->data->PromosApplied[] = $promo; + if ($limit > 0 && count($this->data->PromosApplied)+1 > $limit) { + $PromoLimit = true; + break; + } + } + + if ($match && $promo->exclusive == "on") break; + + } // end foreach ($Promotions) + + // Promo code found, but ran into promotion limits + if (!empty($this->data->PromoCode) && $PromoLimit) { + $this->data->PromoCodeResult = __("No additional codes can be applied.","Shopp"); + $this->data->PromoCodes = array_diff($this->data->PromoCodes,array($PromoCodeFound)); + $this->data->PromoCode = false; + } + + // Promo code not found + if (!empty($this->data->PromoCode) && !$PromoCodeFound && !$PromoCodeExists) { + $this->data->PromoCodeResult = $this->data->PromoCode.' '.__("is not a valid code.","Shopp"); + $this->data->PromoCodes = array_diff($this->data->PromoCodes,array($this->data->PromoCode)); + $this->data->PromoCode = false; + } + } + + /** + * taxrate() + * Determines the taxrate based on the currently + * available shipping information set by shipzone() */ + function taxrate () { + global $Shopp; + if ($Shopp->Settings->get('taxes') == "off") return false; + + $taxrates = $Shopp->Settings->get('taxrates'); + $base = $Shopp->Settings->get('base_operations'); + if (!is_array($taxrates)) return false; + + if (!empty($this->data->Order->Shipping->country)) $country = $this->data->Order->Shipping->country; + elseif (!empty($this->data->Order->Billing->country)) $country = $this->data->Order->Billing->country; + else $country = $base['country']; + + if (!empty($this->data->Order->Shipping->state)) $zone = $this->data->Order->Shipping->state; + elseif (!empty($this->data->Order->Billing->state)) $zone = $this->data->Order->Billing->state; + else $zone = $base['zone']; + + $global = false; + foreach ($taxrates as $setting) { + // Grab the global setting if found + if ($setting['country'] == "*") { + $global = $setting; + continue; + } + + if (isset($setting['zone'])) { + if ($country == $setting['country'] && + $zone == $setting['zone']) + return apply_filters('shopp_cart_taxrate',$setting['rate']/100); + } elseif ($country == $setting['country']) { + return apply_filters('shopp_cart_taxrate',$setting['rate']/100); + } + } + + if ($global) return apply_filters('shopp_cart_taxrate',$global['rate']/100); + + } + + /** + * totals() + * Calculates subtotal, shipping, tax and + * order total amounts */ + function totals () { + global $Shopp; + if (!$this->retotal && !$this->updated) return true; + + $shippingTaxed = ($Shopp->Settings->get('tax_shipping') == "on"); + $Totals =& $this->data->Totals; + $Totals->quantity = 0; + $Totals->subtotal = 0; + $Totals->discount = 0; + $Totals->shipping = 0; + $Totals->taxed = 0; + $Totals->tax = 0; + $Totals->total = 0; + + $Totals->taxrate = $this->taxrate(); + + $freeshipping = true; // Assume free shipping for the cart unless proven wrong + foreach ($this->contents as $key => $Item) { + + // Add the item to the shipped list + if ($Item->shipping && !$Item->freeshipping) $this->shipped[$key] = $Item; + + // Item does not have free shipping, + // so the cart shouldn't have free shipping + if (!$Item->freeshipping) $freeshipping = false; + + $Totals->quantity += $Item->quantity; + $Totals->subtotal += $Item->total; + + // Tabulate the taxable total to be calculated after discounts + if ($Item->taxable && $Totals->taxrate > 0) + $Totals->taxed += $Item->total; + } + $this->freeshipping = $freeshipping; + if ($this->data->ShippingDisabled) $this->freeshipping = false; + + // Calculate discounts + $this->promotions(); + $discount = ($Totals->discount > $Totals->subtotal)?$Totals->subtotal:$Totals->discount; + + // Calculate shipping + if (!$this->data->ShippingDisabled && $this->data->Shipping && !$this->freeshipping) + $Totals->shipping = $this->shipping(); + + // Calculate taxes + if ($discount > $Totals->taxed) $Totals->taxed = 0; + else $Totals->taxed -= $discount; + if($shippingTaxed) $Totals->taxed += $Totals->shipping; + $Totals->tax = round($Totals->taxed*$Totals->taxrate,2); + + // Calculate final totals + $Totals->total = round($Totals->subtotal - round($discount,2) + + $Totals->shipping + $Totals->tax,2); + + do_action_ref_array('shopp_cart_retotal',array(&$Totals)); + } + + /** + * logins () + * Handle login processing */ + function logins () { + global $Shopp; + if (!$this->data->Order->Customer) { + $this->data->Order->Customer = new Customer(); + $this->data->Order->Billing = new Billing(); + $this->data->Order->Shipping = new Shipping(); + } + + $authentication = $Shopp->Settings->get('account_system'); + + if (isset($_GET['acct']) && isset($this->data->Order->Customer) + && $_GET['acct'] == "logout") { + if ($authentication == "wordpress" && $this->data->login) + add_action('shopp_logout','wp_clear_auth_cookie'); + return $this->logout(); + } + + switch ($authentication) { + case "wordpress": + if ($this->data->login) add_action('wp_logout',array(&$this,'logout')); + + // See if the wordpress user is already logged in + $user = wp_get_current_user(); + + if (!empty($user->ID) && !$this->data->login) { + if ($Account = new Customer($user->ID,'wpuser')) { + $this->loggedin($Account); + $this->data->Order->Customer->wpuser = $user->ID; + break; + } + } + + if (empty($_POST['process-login'])) return false; + + if (!empty($_POST['account-login'])) { + if (strpos($_POST['account-login'],'@') !== false) $mode = "email"; + else $mode = "loginname"; + $loginname = $_POST['account-login']; + } else { + new ShoppError(__('You must provide a valid login name or email address to proceed.'), 'missing_account', SHOPP_AUTH_ERR); + } + + if ($loginname) { + $this->auth($loginname,$_POST['password-login'],$mode); + } + break; + case "shopp": + if (!isset($_POST['process-login'])) return false; + if ($_POST['process-login'] != "true") return false; + $mode = "loginname"; + if (!empty($_POST['account-login']) && strpos($_POST['account-login'],'@') !== false) + $mode = "email"; + $this->auth($_POST['account-login'],$_POST['password-login'],$mode); + break; + } + + } + + /** + * auth () + * Authorize login credentials */ + function auth ($id,$password,$type='email') { + global $Shopp; + $db = DB::get(); + $authentication = $Shopp->Settings->get('account_system'); + switch($authentication) { + case "shopp": + $Account = new Customer($id,'email'); + + if (empty($Account)) { + new ShoppError(__("No customer account was found with that email.","Shopp"),'invalid_account',SHOPP_AUTH_ERR); + return false; + } + + if (!wp_check_password($password,$Account->password)) { + new ShoppError(__("The password is incorrect.","Shopp"),'invalid_password',SHOPP_AUTH_ERR); + return false; + } + + break; + + case "wordpress": + if($type == 'email'){ + $user = get_user_by_email($id); + if ($user) $loginname = $user->user_login; + else { + new ShoppError(__("No customer account was found with that email.","Shopp"),'invalid_account',SHOPP_AUTH_ERR); + return false; + } + } else $loginname = $id; + $user = wp_authenticate($loginname,$password); + if (!is_wp_error($user)) { + wp_set_auth_cookie($user->ID, false, $Shopp->secure); + do_action('wp_login', $loginname); + + if ($Account = new Customer($user->ID,'wpuser')) { + $this->loggedin($Account); + $this->data->Order->Customer->wpuser = $user->ID; + add_action('wp_logout',array(&$this,'logout')); + } + return true; + } else { // WordPress User Authentication failed + $_e = $user->get_error_code(); + if($_e == 'invalid_username') new ShoppError(__("No customer account was found with that login.","Shopp"),'invalid_account',SHOPP_AUTH_ERR); + else if($_e == 'incorrect_password') new ShoppError(__("The password is incorrect.","Shopp"),'invalid_password',SHOPP_AUTH_ERR); + else new ShoppError(__('Unknown login error: ').$_e,false,SHOPP_AUTH_ERR); + return false; + } + break; + default: return false; + } + + $this->loggedin($Account); + + } + + /** + * loggedin() + * Initialize login data */ + function loggedin ($Account) { + $this->data->login = true; + $this->data->Order->Customer = $Account; + unset($this->data->Order->Customer->password); + $this->data->Order->Billing = new Billing($Account->id,'customer'); + $this->data->Order->Billing->card = ""; + $this->data->Order->Billing->cardexpires = ""; + $this->data->Order->Billing->cardholder = ""; + $this->data->Order->Billing->cardtype = ""; + $this->data->Order->Shipping = new Shipping($Account->id,'customer'); + if (empty($this->data->Order->Shipping->id)) + $this->data->Order->Shipping->copydata($this->data->Order->Billing); + do_action_ref_array('shopp_login',array(&$Account)); + } + + /** + * logout() + * Clear the session account data */ + function logout () { + do_action('shopp_logout'); + $this->data->login = false; + $this->data->Order->wpuser = false; + $this->data->Order->Customer->id = false; + $this->data->Order->Billing->id = false; + $this->data->Order->Billing->customer = false; + $this->data->Order->Shipping->id = false; + $this->data->Order->Shipping->customer = false; + session_commit(); + } + + /** + * secured() + * Check or set the security setting for the cart */ + function secured ($setting=null) { + if (is_null($setting)) return $this->data->secure; + $this->data->secure = ($setting); + if (SHOPP_DEBUG) { + if ($this->data->secure) new ShoppError('Switching the cart to secure mode.',false,SHOPP_DEBUG_ERR); + else new ShoppError('Switching the cart to unsecure mode.',false,SHOPP_DEBUG_ERR); + } + return $this->data->secure; + } + + /** + * securekey() + * Generate the security key */ + function securekey () { + global $Shopp; + require_once(ABSPATH . WPINC . '/pluggable.php'); + if (!is_shopp_secure()) return false; + $expiration = time()+SHOPP_SESSION_TIMEOUT; + if (defined('SECRET_AUTH_KEY') && SECRET_AUTH_KEY != '') $key = SECRET_AUTH_KEY; + else $key = md5(serialize($this->data).time()); + $content = hash_hmac('sha256', $this->session . '|' . $expiration, $key); + if ( version_compare(phpversion(), '5.2.0', 'ge') ) + setcookie(SHOPP_SECURE_KEY,$content,0,'/','',true,true); + else setcookie(SHOPP_SECURE_KEY,$content,0,'/','',true); + return $content; + } + + /** + * request() + * Processes cart requests and updates the cart + * accordingly */ + function request () { + global $Shopp; + do_action('shopp_cart_request'); + + if (isset($_REQUEST['checkout'])) shopp_redirect($Shopp->link('checkout',true)); + + if (isset($_REQUEST['shopping'])) shopp_redirect($Shopp->link('catalog')); + + if (isset($_REQUEST['shipping'])) { + $countries = $Shopp->Settings->get('countries'); + $regions = $Shopp->Settings->get('regions'); + $_REQUEST['shipping']['region'] = $regions[$countries[$_REQUEST['shipping']['country']]['region']]; + if (!empty($_REQUEST['shipping']['postcode'])) // Protect input field from XSS + $_REQUEST['shipping']['postcode'] = attribute_escape($_REQUEST['shipping']['postcode']); + unset($countries,$regions); + $this->shipzone($_REQUEST['shipping']); + } else if (!isset($this->data->Order->Shipping->country)) { + $base = $Shopp->Settings->get('base_operations'); + $_REQUEST['shipping']['country'] = $base['country']; + $this->shipzone($_REQUEST['shipping']); + } + + if (!empty($_REQUEST['promocode'])) { + $this->data->PromoCodeResult = ""; + if (!in_array($_REQUEST['promocode'],$this->data->PromoCodes)) { + $this->data->PromoCode = attribute_escape($_REQUEST['promocode']); // Protect from XSS + $this->updated(); + } else $this->data->PromoCodeResult = __("That code has already been applied.","Shopp"); + } + + if (isset($_REQUEST['remove'])) $_REQUEST['cart'] = "remove"; + if (isset($_REQUEST['update'])) $_REQUEST['cart'] = "update"; + if (isset($_REQUEST['empty'])) $_REQUEST['cart'] = "empty"; + + if (!isset($_REQUEST['quantity'])) $_REQUEST['quantity'] = 1; + + switch($_REQUEST['cart']) { + case "add": + if (isset($_REQUEST['product'])) { + + $quantity = (empty($product['quantity']) && + $product['quantity'] !== 0)?1:$product['quantity']; // Add 1 by default + $Product = new Product($_REQUEST['product']); + $pricing = false; + if (!empty($_REQUEST['options']) && !empty($_REQUEST['options'][0])) + $pricing = $_REQUEST['options']; + else $pricing = $_REQUEST['price']; + + $category = false; + if (!empty($_REQUEST['category'])) $category = $_REQUEST['category']; + + if (isset($_REQUEST['data'])) $data = $_REQUEST['data']; + else $data = array(); + + if (isset($_REQUEST['item'])) $result = $this->change($_REQUEST['item'],$Product,$pricing); + else $result = $this->add($quantity,$Product,$pricing,$category,$data); + + } + + if (isset($_REQUEST['products']) && is_array($_REQUEST['products'])) { + foreach ($_REQUEST['products'] as $id => $product) { + $quantity = (empty($product['quantity']) && + $product['quantity'] !== 0)?1:$product['quantity']; // Add 1 by default + $Product = new Product($product['product']); + $pricing = false; + if (!empty($product['options']) && !empty($product['options'][0])) + $pricing = $product['options']; + elseif (isset($product['price'])) $pricing = $product['price']; + + $category = false; + if (!empty($product['category'])) $category = $product['category']; + + if (!empty($product['data'])) $data = $product['data']; + else $data = array(); + + if (!empty($Product->id)) { + if (isset($product['item'])) $result = $this->change($product['item'],$Product,$pricing); + else $result = $this->add($quantity,$Product,$pricing,$category,$data); + } + } + + } + break; + case "remove": + if (!empty($this->contents)) $this->remove(current($_REQUEST['remove'])); + break; + case "empty": + $this->clear(); + break; + default: + if (isset($_REQUEST['item']) && isset($_REQUEST['quantity'])) { + $this->update($_REQUEST['item'],$_REQUEST['quantity']); + } elseif (!empty($_REQUEST['items'])) { + foreach ($_REQUEST['items'] as $id => $item) { + if (isset($item['quantity'])) { + $item['quantity'] = ceil(preg_replace('/[^\d\.]+/','',$item['quantity'])); + if (!empty($item['quantity'])) $this->update($id,$item['quantity']); + if (isset($_REQUEST['remove'][$id])) $this->remove($_REQUEST['remove'][$id]); + } + if (isset($item['product']) && isset($item['price']) && + $item['product'] == $this->contents[$id]->product && + $item['price'] != $this->contents[$id]->price) { + $Product = new Product($item['product']); + $this->change($id,$Product,$item['price']); + } + } + } + } + + do_action('shopp_cart_updated',$this); + } + + /** + * ajax() + * Handles AJAX-based cart request responses */ + function ajax () { + global $Shopp; + + if ($_REQUEST['response'] == "html") { + echo $this->tag('sidecart'); + exit(); + } + $AjaxCart = new StdClass(); + $AjaxCart->url = $Shopp->link('cart'); + $AjaxCart->Totals = clone($this->data->Totals); + $AjaxCart->Contents = array(); + foreach($this->contents as $item) { + $cartitem = clone($item); + unset($cartitem->options); + $AjaxCart->Contents[] = $cartitem; + } + if (isset($this->data->added)) + $AjaxCart->Item = clone($this->contents[$this->data->added]); + else $AjaxCart->Item = new Item(); + unset($AjaxCart->Item->options); + + echo json_encode($AjaxCart); + exit(); + } + + /** + * validate() + * Validate checkout form order data before processing */ + function validate () { + global $Shopp; + $authentication = $Shopp->Settings->get('account_system'); + + if (empty($_POST['firstname'])) + return new ShoppError(__('You must provide your first name.','Shopp'),'cart_validation'); + + if (empty($_POST['lastname'])) + return new ShoppError(__('You must provide your last name.','Shopp'),'cart_validation'); + + $rfc822email = '([^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+|\\x22([^\\x0d'. + '\\x22\\x5c\\x80-\\xff]|\\x5c[\\x00-\\x7f])*\\x22)(\\x2e([^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e'. + '\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+|\\x22([^\\x0d\\x22\\x5c\\x80-\\xff]|\\x5c[\\x00-\\x7f])*'. + '\\x22))*\\x40([^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+'. + '|\\x5b([^\\x0d\\x5b-\\x5d\\x80-\\xff]|\\x5c[\\x00-\\x7f])*\\x5d)(\\x2e([^\\x00-\\x20\\x22\\x28'. + '\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+|\\x5b([^\\x0d\\x5b-\\x5d\\x80-\\xff]'. + '|\\x5c[\\x00-\\x7f])*\\x5d))*'; + if(!preg_match("!^$rfc822email$!", $_POST['email'])) + return new ShoppError(__('You must provide a valid e-mail address.','Shopp'),'cart_validation'); + + if ($authentication == "wordpress" && !$this->data->login) { + require_once(ABSPATH."/wp-includes/registration.php"); + + // Validate possible wp account names for availability + if(isset($_POST['login'])){ + if(username_exists($_POST['login'])) + return new ShoppError(__('The login name you provided is not available. Try logging in if you have previously created an account.'), 'cart_validation'); + } else { // need to find a usuable login + list($handle,$domain) = explode("@",$_POST['email']); + if(!username_exists($handle)) $_POST['login'] = $handle; + + $handle = $_POST['firstname'].substr($_POST['lastname'],0,1); + if(!isset($_POST['login']) && !username_exists($handle)) $_POST['login'] = $handle; + + $handle = substr($_POST['firstname'],0,1).$_POST['lastname']; + if(!isset($_POST['login']) && !username_exists($handle)) $_POST['login'] = $handle; + + $handle .= rand(1000,9999); + if(!isset($_POST['login']) && !username_exists($handle)) $_POST['login'] = $handle; + + if(!isset($_POST['login'])) return new ShoppError(__('A login is not available for creation with the information you provided. Please try a different email address or name, or try logging in if you previously created an account.'),'cart_validation'); + } + if(SHOPP_DEBUG) new ShoppError('Login set to '. $_POST['login'] . ' for WordPress account creation.',false,SHOPP_DEBUG_ERR); + $ExistingCustomer = new Customer($_POST['email'],'email'); + if (email_exists($_POST['email']) || !empty($ExistingCustomer->id)) + return new ShoppError(__('The email address you entered is already in use. Try logging in if you previously created an account, or enter another email address to create your new account.','Shopp'),'cart_validation'); + } elseif ($authentication == "shopp" && !$this->data->login) { + $ExistingCustomer = new Customer($_POST['email'],'email'); + if (!empty($ExistingCustomer->id)) + return new ShoppError(__('The email address you entered is already in use. Try logging in if you previously created an account, or enter another email address to create a new account.','Shopp'),'cart_validation'); + } + + // Validate WP account + if (isset($_POST['login']) && empty($_POST['login'])) + return new ShoppError(__('You must enter a login name for your account.','Shopp'),'cart_validation'); + + if (isset($_POST['login'])) { + require_once(ABSPATH."/wp-includes/registration.php"); + if (username_exists($_POST['login'])) + return new ShoppError(__('The login name you provided is already in use. Try logging in if you previously created that account, or enter another login name for your new account.','Shopp'),'cart_validation'); + } + + if (isset($_POST['password'])) { + if (empty($_POST['password']) || empty($_POST['confirm-password'])) + return new ShoppError(__('You must provide a password for your account and confirm it to ensure correct spelling.','Shopp'),'cart_validation'); + if ($_POST['password'] != $_POST['confirm-password']) { + $_POST['password'] = ""; $_POST['confirm-password'] = ""; + return new ShoppError(__('The passwords you entered do not match. Please re-enter your passwords.','Shopp'),'cart_validation'); + } + } + + if (empty($_POST['billing']['address']) || strlen($_POST['billing']['address']) < 4) + return new ShoppError(__('You must enter a valid street address for your billing information.','Shopp'),'cart_validation'); + + if (empty($_POST['billing']['postcode'])) + return new ShoppError(__('You must enter a valid postal code for your billing information.','Shopp'),'cart_validation'); + + if (empty($_POST['billing']['country'])) + return new ShoppError(__('You must select a country for your billing information.','Shopp'),'cart_validation'); + + // Skip validating billing details for free purchases + // and remote checkout systems + if ((int)$this->data->Totals->total == 0 + || !empty($_GET['shopp_xco'])) return apply_filters('shopp_validate_checkout',true); + + if (empty($_POST['billing']['card'])) + return new ShoppError(__('You did not provide a credit card number.','Shopp'),'cart_validation'); + + if (empty($_POST['billing']['cardtype'])) + return new ShoppError(__('You did not select a credit card type.','Shopp'),'cart_validation'); + + // credit card validation + switch(strtolower($_POST['billing']['cardtype'])) { + case "american express": + case "amex": $pattern = '/^3[4,7]\d{13}$/'; break; + case "diner's club": + case "diners club": $pattern = '/^3[0,6,8]\d{12}$/'; break; + case "discover": $pattern = '/^6011-?\d{4}-?\d{4}-?\d{4}$/'; break; + case "mastercard": $pattern = '/^5[1-5]\d{2}-?\d{4}-?\d{4}-?\d{4}$/'; break; + case "visa": $pattern = '/^4\d{3}-?\d{4}-?\d{4}-?\d{4}$/'; break; + default: $pattern = false; + } + if ($pattern && !preg_match($pattern,$_POST['billing']['card'])) + return new ShoppError(__('The credit card number you provided is invalid.','Shopp'),'cart_validation'); + + // credit card checksum validation + $cs = 0; + $cc = str_replace("-","",$_POST['billing']['card']); + $code = strrev(str_replace("-","",$_POST['billing']['card'])); + for ($i = 0; $i < strlen($code); $i++) { + $d = intval($code[$i]); + if ($i & 1) $d *= 2; + $cs += $d % 10; + if ($d > 9) $cs += 1; + } + if ($cs % 10 != 0) + return new ShoppError(__('The credit card number you provided is not valid.','Shopp'),'cart_validation'); + + if (empty($_POST['billing']['cardexpires-mm'])) + return new ShoppError(__('You did not enter the month the credit card expires.','Shopp'),'cart_validation'); + + if (empty($_POST['billing']['cardexpires-yy'])) + return new ShoppError(__('You did not enter the year the credit card expires.','Shopp'),'cart_validation'); + + if (!empty($_POST['billing']['cardexpires-mm']) && !empty($_POST['billing']['cardexpires-yy']) + && $_POST['billing']['cardexpires-mm'] < date('n') && $_POST['billing']['cardexpires-yy'] <= date('y')) + return new ShoppError(__('The credit card expiration date you provided has already expired.','Shopp'),'cart_validation'); + + if (strlen($_POST['billing']['cardholder']) < 2) + return new ShoppError(__('You did not enter the name on the credit card you provided.','Shopp'),'cart_validation'); + + if (strlen($_POST['billing']['cvv']) < 3) + return new ShoppError(__('You did not enter a valid security ID for the card you provided. The security ID is a 3 or 4 digit number found on the back of the credit card.','Shopp'),'cart_validation'); + + return apply_filters('shopp_validate_checkout',true); + } + + /** + * validorder() + * Validates order data during checkout processing to verify that sufficient information exists to process. */ + function validorder () { + $Order = $this->data->Order; + $Customer = $Order->Customer; + $Shipping = $this->data->Order->Shipping; + + if(empty($this->contents)) return false; // No items + if(empty($Order)) return false; // No order data + if(!$Customer) return false; // No Customer + + // Always require name and email + if( empty($Customer->firstname) || empty($Customer->lastname)) return false; + if( empty($Customer->email) ) return false; + + // Check for shipped items but no Shipping information + if ($this->data->Shipping) { + if(empty($Shipping->address)) return false; + if(empty($Shipping->city)) return false; + if(empty($Shipping->country)) return false; + if(empty($Shipping->postcode)) return false; + } + return true; + } + + /** + * orderisfree() + * Determines if the current order has no cost */ + function orderisfree() { + $status = (count($this->contents) > 0 && number_format($this->data->Totals->total,2) == 0)?true:false; + return apply_filters('shopp_free_order',$status); + } + + function tag ($property,$options=array()) { + global $Shopp; + $submit_attrs = array('title','value','disabled','tabindex','accesskey'); + + // Return strings with no options + switch ($property) { + case "url": return $Shopp->link('cart'); break; + case "hasitems": return (count($this->contents) > 0); break; + case "totalitems": return $this->data->Totals->quantity; break; + case "items": + if (!$this->looping) { + reset($this->contents); + $this->looping = true; + } else next($this->contents); + + if (current($this->contents)) return true; + else { + $this->looping = false; + reset($this->contents); + return false; + } + case "lastitem": return $this->contents[$this->data->added]; break; + case "totalpromos": return count($this->data->PromosApplied); break; + case "haspromos": return (count($this->data->PromosApplied) > 0); break; + case "promos": + if (!$this->looping) { + reset($this->data->PromosApplied); + $this->looping = true; + } else next($this->data->PromosApplied); + + if (current($this->data->PromosApplied)) return true; + else { + $this->looping = false; + reset($this->data->PromosApplied); + return false; + } + case "promo-name": + $promo = current($this->data->PromosApplied); + return $promo->name; + break; + case "promo-discount": + $promo = current($this->data->PromosApplied); + if (empty($options['label'])) $options['label'] = __('Off!','Shopp'); + if (!empty($options['before'])) $string = $options['before']; + switch($promo->type) { + case "Free Shipping": $string .= $Shopp->Settings->get('free_shipping_text'); + case "Percentage Off": $string .= percentage($promo->discount)." ".$options['label']; + case "Amount Off": $string .= money($promo->discount)." ".$options['label']; + case "Buy X Get Y Free": return ""; + } + if (!empty($options['after'])) $string = $options['after']; + return $string; + + break; + case "function": + $result = ''; + if (!$this->data->Errors->exist()) return $result; + $errors = $this->data->Errors->get(SHOPP_COMM_ERR); + foreach ((array)$errors as $error) + if (!empty($error)) $result .= '

'.$error->message().'

'; + $this->data->Errors->reset(); // Reset after display + return $result; + break; + case "empty-button": + if (!isset($options['value'])) $options['value'] = __('Empty Cart','Shopp'); + return ''; + break; + case "update-button": + if (!isset($options['value'])) $options['value'] = __('Update Subtotal','Shopp'); + return ''; + break; + case "sidecart": + ob_start(); + include(SHOPP_TEMPLATES."/sidecart.php"); + $content = ob_get_contents(); + ob_end_clean(); + return $content; + break; + case "hasdiscount": return ($this->data->Totals->discount > 0); break; + case "discount": return money($this->data->Totals->discount); break; + } + + $result = ""; + switch ($property) { + case "promos-available": + if (empty($this->data->Promotions)) return false; + // Skip if the promo limit has been reached + if ($Shopp->Settings->get('promo_limit') > 0 && + count($this->data->PromosApplied) >= $Shopp->Settings->get('promo_limit')) return false; + return true; + break; + case "promo-code": + // Skip if no promotions exist + if (empty($this->data->Promotions)) return false; + // Skip if the promo limit has been reached + if ($Shopp->Settings->get('promo_limit') > 0 && + count($this->data->PromosApplied) >= $Shopp->Settings->get('promo_limit')) return false; + if (!isset($options['value'])) $options['value'] = __("Apply Promo Code","Shopp"); + $result .= '
  • '; + + if (!empty($this->data->PromoCodeResult)) { + $result .= '

    '.$this->data->PromoCodeResult.'

    '; + $this->data->PromoCodeResult = ""; + } + + $result .= ''; + $result .= ''; + $result .= '
'; + return $result; + case "has-shipping-methods": + return (!$this->data->ShippingDisabled + && count($this->data->ShipCosts) > 1 + && $this->data->Shipping); break; + case "needs-shipped": return $this->data->Shipping; break; + case "hasshipcosts": + case "has-ship-costs": return ($this->data->Totals->shipping > 0); break; + case "needs-shipping-estimates": + $markets = $Shopp->Settings->get('target_markets'); + return ($this->data->Shipping && ($this->data->ShippingPostcode || count($markets) > 1)); + break; + case "shipping-estimates": + if (!$this->data->Shipping) return ""; + $base = $Shopp->Settings->get('base_operations'); + $markets = $Shopp->Settings->get('target_markets'); + if (empty($markets)) return ""; + foreach ($markets as $iso => $country) $countries[$iso] = $country; + if (!empty($this->data->Order->Shipping->country)) $selected = $this->data->Order->Shipping->country; + else $selected = $base['country']; + $result .= '
  • '; + if ((isset($options['postcode']) && value_is_true($options['postcode'])) || + $this->data->ShippingPostcode) { + $result .= ''; + $result .= ' '; + $result .= ''; + } + if (count($countries) > 1) { + $result .= ''; + $result .= ''; + $result .= ''; + } else $result .= ''; + $result .= '
'; + return $result; + break; + } + + $result = ""; + switch ($property) { + case "subtotal": $result = $this->data->Totals->subtotal; break; + case "shipping": + if (!$this->data->Shipping) return ""; + if (isset($options['label'])) { + $options['currency'] = "false"; + if ($this->data->Totals->shipping === 0) { + $result = $Shopp->Settings->get('free_shipping_text'); + if (empty($result)) $result = __('Free Shipping!','Shopp'); + } + + else $result = $options['label']; + } else { + if ($this->data->Totals->shipping === null) + return __("Enter Postal Code","Shopp"); + elseif ($this->data->Totals->shipping === false) + return __("Not Available","Shopp"); + else $result = $this->data->Totals->shipping; + } + break; + case "hastaxes": + case "has-taxes": + return ($this->data->Totals->tax > 0); break; + case "tax": + if ($this->data->Totals->tax > 0) { + if (isset($options['label'])) { + $options['currency'] = "false"; + $result = $options['label']; + } else $result = $this->data->Totals->tax; + } else $options['currency'] = "false"; + break; + case "total": + $result = $this->data->Totals->total; + break; + } + + if (isset($options['currency']) && !value_is_true($options['currency'])) return $result; + else return ''.money($result).''; + + return false; + } + + function itemtag ($property,$options=array()) { + if ($this->looping) { + $Item = current($this->contents); + if ($Item !== false) { + $id = key($this->contents); + if ($property == "id") return $id; + return $Item->tag($id,$property,$options); + } + } else return false; + } + + + /** + * shippingtag() + * shopp('shipping','...') + * Used primarily in the summary.php template + **/ + function shippingtag ($property,$options=array()) { + global $Shopp; + $ShipCosts =& $this->data->ShipCosts; + $result = ""; + + switch ($property) { + case "hasestimates": return (count($ShipCosts) > 0); break; + case "methods": + if (!isset($this->sclooping)) $this->sclooping = false; + if (!$this->sclooping) { + reset($ShipCosts); + $this->sclooping = true; + } else next($ShipCosts); + + if (current($ShipCosts)) return true; + else { + $this->sclooping = false; + reset($ShipCosts); + return false; + } + break; + case "method-name": + return key($ShipCosts); + break; + case "method-cost": + $method = current($ShipCosts); + return money($method['cost']); + break; + case "method-selector": + $method = current($ShipCosts); + + $checked = ''; + if ((isset($this->data->Order->Shipping->method) && + $this->data->Order->Shipping->method == $method['name']) || + ($method['cost'] == $this->data->Totals->shipping)) + $checked = ' checked="checked"'; + + $result .= ''; + return $result; + + break; + case "method-delivery": + $periods = array("h"=>3600,"d"=>86400,"w"=>604800,"m"=>2592000); + $method = current($ShipCosts); + $estimates = explode("-",$method['delivery']); + $format = get_option('date_format'); + if ($estimates[0] == $estimates[1]) $estimates = array($estimates[0]); + $result = ""; + for ($i = 0; $i < count($estimates); $i++){ + list($interval,$p) = sscanf($estimates[$i],'%d%s'); + if (!empty($result)) $result .= "—"; + $result .= _d($format,mktime()+($interval*$periods[$p])); + } + return $result; + } + } + + function checkouttag ($property,$options=array()) { + global $Shopp,$wp; + $gateway = $Shopp->Settings->get('payment_gateway'); + $xcos = $Shopp->Settings->get('xco_gateways'); + $pages = $Shopp->Settings->get('pages'); + $base = $Shopp->Settings->get('base_operations'); + $countries = $Shopp->Settings->get('target_markets'); + $process = get_query_var('shopp_proc'); + $xco = get_query_var('shopp_xco'); + + $select_attrs = array('title','required','class','disabled','required','size','tabindex','accesskey'); + $submit_attrs = array('title','class','value','disabled','tabindex','accesskey'); + + + if (!isset($options['mode'])) $options['mode'] = "input"; + + switch ($property) { + case "url": + $ssl = true; + // Test Mode will not require encrypted checkout + if (strpos($gateway,"TestMode.php") !== false + || isset($_GET['shopp_xco']) + || $this->orderisfree() + || SHOPP_NOSSL) + $ssl = false; + $link = $Shopp->link('checkout',$ssl); + + // Pass any arguments along + $args = $_GET; + if (isset($args['page_id'])) unset($args['page_id']); + $link = esc_url(add_query_arg($args,$link)); + if ($process == "confirm-order") $link = apply_filters('shopp_confirm_url',$link); + else $link = apply_filters('shopp_checkout_url',$link); + return $link; + break; + case "function": + if (!isset($options['shipcalc'])) $options['shipcalc'] = ''; + $regions = $Shopp->Settings->get('zones'); + $base = $Shopp->Settings->get('base_operations'); + $output = ''."\n"; + if (!empty($options['value'])) $value = $options['value']; + else $value = "process"; + $output .= '
'; + if ($value == "confirmed") $output = apply_filters('shopp_confirm_form',$output); + else $output = apply_filters('shopp_checkout_form',$output); + return $output; + break; + case "error": + $result = ""; + if (!$this->data->Errors->exist(SHOPP_COMM_ERR)) return false; + $errors = $this->data->Errors->get(SHOPP_COMM_ERR); + foreach ((array)$errors as $error) if (!empty($error)) $result .= $error->message(); + return $result; + // if (isset($options['show']) && $options['show'] == "code") return $this->data->OrderError->code; + // return $this->data->OrderError->message; + break; + case "cart-summary": + ob_start(); + include(SHOPP_TEMPLATES."/summary.php"); + $content = ob_get_contents(); + ob_end_clean(); + return $content; + break; + case "loggedin": return $this->data->login; break; + case "notloggedin": return (!$this->data->login && $Shopp->Settings->get('account_system') != "none"); break; + case "email-login": // Deprecating + case "loginname-login": // Deprecating + case "account-login": + if (!empty($_POST['account-login'])) + $options['value'] = $_POST['account-login']; + return ''; + break; + case "password-login": + if (!empty($_POST['password-login'])) + $options['value'] = $_POST['password-login']; + return ''; + break; + case "submit-login": // Deprecating + case "login-button": + $string = ''; + $string .= ''; + return $string; + break; + + case "firstname": + if ($options['mode'] == "value") return $this->data->Order->Customer->firstname; + if (!empty($this->data->Order->Customer->firstname)) + $options['value'] = $this->data->Order->Customer->firstname; + return ''; + break; + case "lastname": + if ($options['mode'] == "value") return $this->data->Order->Customer->lastname; + if (!empty($this->data->Order->Customer->lastname)) + $options['value'] = $this->data->Order->Customer->lastname; + return ''; + break; + case "email": + if ($options['mode'] == "value") return $this->data->Order->Customer->email; + if (!empty($this->data->Order->Customer->email)) + $options['value'] = $this->data->Order->Customer->email; + return ''; + break; + case "loginname": + if ($options['mode'] == "value") return $this->data->Order->Customer->login; + if (!empty($this->data->Order->Customer->login)) + $options['value'] = $this->data->Order->Customer->login; + return ''; + break; + case "password": + if ($options['mode'] == "value") return $this->data->Order->Customer->password; + if (!empty($this->data->Order->Customer->password)) + $options['value'] = $this->data->Order->Customer->password; + return ''; + break; + case "confirm-password": + if (!empty($this->data->Order->Customer->confirm_password)) + $options['value'] = $this->data->Order->Customer->confirm_password; + return ''; + break; + case "phone": + if ($options['mode'] == "value") return $this->data->Order->Customer->phone; + if (!empty($this->data->Order->Customer->phone)) + $options['value'] = $this->data->Order->Customer->phone; + return ''; + break; + case "organization": + case "company": + if ($options['mode'] == "value") return $this->data->Order->Customer->company; + if (!empty($this->data->Order->Customer->company)) + $options['value'] = $this->data->Order->Customer->company; + return ''; + break; + case "customer-info": + $allowed_types = array("text","password","hidden","checkbox","radio"); + if (empty($options['type'])) $options['type'] = "hidden"; + if (isset($options['name']) && $options['mode'] == "value") + return $this->data->Order->Customer->info[$options['name']]; + if (isset($options['name']) && in_array($options['type'],$allowed_types)) { + if (isset($this->data->Order->Customer->info[$options['name']])) + $options['value'] = $this->data->Order->Customer->info[$options['name']]; + return ''; + } + break; + + // SHIPPING TAGS + case "shipping": return $this->data->Shipping; + case "shipping-address": + if ($options['mode'] == "value") return $this->data->Order->Shipping->address; + if (!empty($this->data->Order->Shipping->address)) + $options['value'] = $this->data->Order->Shipping->address; + return ''; + break; + case "shipping-xaddress": + if ($options['mode'] == "value") return $this->data->Order->Shipping->xaddress; + if (!empty($this->data->Order->Shipping->xaddress)) + $options['value'] = $this->data->Order->Shipping->xaddress; + return ''; + break; + case "shipping-city": + if ($options['mode'] == "value") return $this->data->Order->Shipping->city; + if (!empty($this->data->Order->Shipping->city)) + $options['value'] = $this->data->Order->Shipping->city; + return ''; + break; + case "shipping-province": + case "shipping-state": + if ($options['mode'] == "value") return $this->data->Order->Shipping->state; + if (!isset($options['selected'])) $options['selected'] = false; + if (!empty($this->data->Order->Shipping->state)) { + $options['selected'] = $this->data->Order->Shipping->state; + $options['value'] = $this->data->Order->Shipping->state; + } + + $country = $base['country']; + if (!empty($this->data->Order->Shipping->country)) + $country = $this->data->Order->Shipping->country; + if (!array_key_exists($country,$countries)) $country = key($countries); + + if (empty($options['type'])) $options['type'] = "menu"; + $regions = $Shopp->Settings->get('zones'); + $states = $regions[$country]; + if (is_array($states) && $options['type'] == "menu") { + $label = (!empty($options['label']))?$options['label']:''; + $output = ''; + } else $output .= ''; + return $output; + break; + case "shipping-postcode": + if ($options['mode'] == "value") return $this->data->Order->Shipping->postcode; + if (!empty($this->data->Order->Shipping->postcode)) + $options['value'] = $this->data->Order->Shipping->postcode; + return ''; break; + case "shipping-country": + if ($options['mode'] == "value") return $this->data->Order->Shipping->country; + if (!empty($this->data->Order->Shipping->country)) + $options['selected'] = $this->data->Order->Shipping->country; + else if (empty($options['selected'])) $options['selected'] = $base['country']; + $output = ''; + return $output; + break; + case "same-shipping-address": + $label = __("Same shipping address","Shopp"); + if (isset($options['label'])) $label = $options['label']; + $checked = ' checked="checked"'; + if (isset($options['checked']) && !value_is_true($options['checked'])) $checked = ''; + $output = ''; + return $output; + break; + + // BILLING TAGS + case "billing-required": + if ($this->data->Totals->total == 0) return false; + if (isset($_GET['shopp_xco'])) { + $xco = join(DIRECTORY_SEPARATOR,array($Shopp->path,'gateways',$_GET['shopp_xco'].".php")); + if (file_exists($xco)) { + $meta = $Shopp->Flow->scan_gateway_meta($xco); + $PaymentSettings = $Shopp->Settings->get($meta->tags['class']); + return ($PaymentSettings['billing-required'] != "off"); + } + } + return ($this->data->Totals->total > 0); break; + case "billing-address": + if ($options['mode'] == "value") return $this->data->Order->Billing->address; + if (!empty($this->data->Order->Billing->address)) + $options['value'] = $this->data->Order->Billing->address; + return ''; + break; + case "billing-xaddress": + if ($options['mode'] == "value") return $this->data->Order->Billing->xaddress; + if (!empty($this->data->Order->Billing->xaddress)) + $options['value'] = $this->data->Order->Billing->xaddress; + return ''; + break; + case "billing-city": + if (!empty($this->data->Order->Billing->city)) + $options['value'] = $this->data->Order->Billing->city; + return ''; + break; + case "billing-province": + case "billing-state": + if ($options['mode'] == "value") return $this->data->Order->Billing->state; + if (!isset($options['selected'])) $options['selected'] = false; + if (!empty($this->data->Order->Billing->state)) { + $options['selected'] = $this->data->Order->Billing->state; + $options['value'] = $this->data->Order->Billing->state; + } + if (empty($options['type'])) $options['type'] = "menu"; + + $country = $base['country']; + if (!empty($this->data->Order->Billing->country)) + $country = $this->data->Order->Billing->country; + if (!array_key_exists($country,$countries)) $country = key($countries); + + $regions = $Shopp->Settings->get('zones'); + $states = $regions[$country]; + if (is_array($states) && $options['type'] == "menu") { + $label = (!empty($options['label']))?$options['label']:''; + $output = ''; + } else $output .= ''; + return $output; + break; + case "billing-postcode": + if ($options['mode'] == "value") return $this->data->Order->Billing->postcode; + if (!empty($this->data->Order->Billing->postcode)) + $options['value'] = $this->data->Order->Billing->postcode; + return ''; + break; + case "billing-country": + if ($options['mode'] == "value") return $this->data->Order->Billing->country; + if (!empty($this->data->Order->Billing->country)) + $options['selected'] = $this->data->Order->Billing->country; + else if (empty($options['selected'])) $options['selected'] = $base['country']; + $output = ''; + return $output; + break; + case "billing-card": + if ($options['mode'] == "value") + return str_repeat('X',strlen($this->data->Order->Billing->card)-4) + .substr($this->data->Order->Billing->card,-4); + if (!empty($this->data->Order->Billing->card)) { + $options['value'] = $this->data->Order->Billing->card; + $this->data->Order->Billing->card = ""; + } + return ''; + break; + case "billing-cardexpires-mm": + if ($options['mode'] == "value") return date("m",$this->data->Order->Billing->cardexpires); + if (!empty($this->data->Order->Billing->cardexpires)) + $options['value'] = date("m",$this->data->Order->Billing->cardexpires); + return ''; + break; + case "billing-cardexpires-yy": + if ($options['mode'] == "value") return date("y",$this->data->Order->Billing->cardexpires); + if (!empty($this->data->Order->Billing->cardexpires)) + $options['value'] = date("y",$this->data->Order->Billing->cardexpires); + return ''; + break; + case "billing-cardtype": + if ($options['mode'] == "value") return $this->data->Order->Billing->cardtype; + if (!isset($options['selected'])) $options['selected'] = false; + if (!empty($this->data->Order->Billing->cardtype)) + $options['selected'] = $this->data->Order->Billing->cardtype; + $cards = $Shopp->Settings->get('gateway_cardtypes'); + $label = (!empty($options['label']))?$options['label']:''; + $output = ''; + return $output; + break; + case "billing-cardholder": + if ($options['mode'] == "value") return $this->data->Order->Billing->cardholder; + if (!empty($this->data->Order->Billing->cardholder)) + $options['value'] = $this->data->Order->Billing->cardholder; + return ''; + break; + case "billing-cvv": + if (!empty($this->data->Order->Billing->cardholder)) + $options['value'] = $_POST['billing']['cvv']; + return ''; + break; + case "billing-xco": + if (isset($_GET['shopp_xco'])) { + if ($this->data->Totals->total == 0) return false; + $xco = join(DIRECTORY_SEPARATOR,array($Shopp->path,'gateways',$_GET['shopp_xco'].".php")); + if (file_exists($xco)) { + $meta = $Shopp->Flow->scan_gateway_meta($xco); + $ProcessorClass = $meta->tags['class']; + include_once($xco); + $Payment = new $ProcessorClass(); + if (method_exists($Payment,'billing')) return $Payment->billing($options); + } + } + break; + + case "has-data": + case "hasdata": return (is_array($this->data->Order->data) && count($this->data->Order->data) > 0); break; + case "order-data": + case "orderdata": + if (isset($options['name']) && $options['mode'] == "value") + return $this->data->Order->data[$options['name']]; + $allowed_types = array("text","hidden",'password','checkbox','radio','textarea'); + $value_override = array("text","hidden","password","textarea"); + if (empty($options['type'])) $options['type'] = "hidden"; + if (isset($options['name']) && in_array($options['type'],$allowed_types)) { + if (!isset($options['title'])) $options['title'] = $options['name']; + if (in_array($options['type'],$value_override) && isset($this->data->Order->data[$options['name']])) + $options['value'] = $this->data->Order->data[$options['name']]; + if (!isset($options['cols'])) $options['cols'] = "30"; + if (!isset($options['rows'])) $options['rows'] = "3"; + if ($options['type'] == "textarea") + return ''; + return ''; + } + + // Looping for data value output + if (!$this->dataloop) { + reset($this->data->Order->data); + $this->dataloop = true; + } else next($this->data->Order->data); + + if (current($this->data->Order->data) !== false) return true; + else { + $this->dataloop = false; + return false; + } + + break; + case "data": + if (!is_array($this->data->Order->data)) return false; + $data = current($this->data->Order->data); + $name = key($this->data->Order->data); + if (isset($options['name'])) return $name; + return $data; + break; + case "submit": + if (!isset($options['value'])) $options['value'] = __('Submit Order','Shopp'); + return ''; break; + case "confirm-button": + if (!isset($options['value'])) $options['value'] = __('Confirm Order','Shopp'); + return ''; break; + case "local-payment": + return (!empty($gateway)); break; + case "xco-buttons": + if (!is_array($xcos)) return false; + $buttons = ""; + foreach ($xcos as $xco) { + $xco = str_replace('/',DIRECTORY_SEPARATOR,$xco); + $xcopath = join(DIRECTORY_SEPARATOR,array($Shopp->path,'gateways',$xco)); + if (!file_exists($xcopath)) continue; + $meta = $Shopp->Flow->scan_gateway_meta($xcopath); + $ProcessorClass = $meta->tags['class']; + if (!empty($ProcessorClass)) { + $PaymentSettings = $Shopp->Settings->get($ProcessorClass); + if ($PaymentSettings['enabled'] == "on") { + include_once($xcopath); + $Payment = new $ProcessorClass(); + $buttons .= $Payment->tag('button',$options); + } + } + } + return $buttons; + break; + } + } + +} // end Cart class + +?> \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/model/Catalog.php b/blog/wp-content/plugins/shopp/core/model/Catalog.php new file mode 100644 index 0000000..2d13e3c --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/model/Catalog.php @@ -0,0 +1,612 @@ +init(self::$table); + $this->type = $type; + $this->outofstock = ($Shopp->Settings->get('outofstock_catalog') == "on"); + } + + function load_categories ($filtering=false,$showsmart=false,$results=false) { + $db = DB::get(); + + if (empty($filtering['columns'])) $filtering['columns'] = "cat.id,cat.parent,cat.name,cat.description,cat.uri,cat.slug,count(DISTINCT pd.id) AS total,IF(SUM(IF(pt.inventory='off',1,0) OR pt.inventory IS NULL)>0,'off','on') AS inventory, SUM(pt.stock) AS stock"; + if (!empty($filtering['limit'])) $filtering['limit'] = "LIMIT ".$filtering['limit']; + else $filtering['limit'] = ""; + + // if (!$this->outofstock) $filtering['where'] .= (empty($filtering['where'])?"":" AND ")."(pt.inventory='off' OR (pt.inventory='on' AND pt.stock > 0))"; + if (empty($filtering['where'])) $filtering['where'] = "true"; + + if (empty($filtering['orderby'])) $filtering['orderby'] = "name"; + switch(strtolower($filtering['orderby'])) { + case "id": $orderby = "cat.id"; break; + case "slug": $orderby = "cat.slug"; break; + case "count": $orderby = "total"; break; + default: $orderby = "cat.name"; + } + + if (empty($filtering['order'])) $filtering['order'] = "ASC"; + switch(strtoupper($filtering['order'])) { + case "DESC": $order = "DESC"; break; + default: $order = "ASC"; + } + + $category_table = DatabaseObject::tablename(Category::$table); + $product_table = DatabaseObject::tablename(Product::$table); + $price_table = DatabaseObject::tablename(Price::$table); + $query = "SELECT {$filtering['columns']} FROM $category_table AS cat LEFT JOIN $this->_table AS sc ON sc.category=cat.id LEFT JOIN $product_table AS pd ON sc.product=pd.id LEFT JOIN $price_table AS pt ON pt.product=pd.id AND pt.type != 'N/A' WHERE {$filtering['where']} GROUP BY cat.id ORDER BY cat.parent DESC,$orderby $order {$filtering['limit']}"; + $categories = $db->query($query,AS_ARRAY); + + if (count($categories) > 1) $categories = sort_tree($categories); + if ($results) return $categories; + + foreach ($categories as $category) { + $category->outofstock = false; + if (isset($category->inventory)) { + if ($category->inventory == "on" && $category->stock == 0) + $category->outofstock = true; + + if (!$this->outofstock && $category->outofstock) continue; + } + + $this->categories[$category->id] = new Category(); + $this->categories[$category->id]->populate($category); + + if (isset($category->depth)) + $this->categories[$category->id]->depth = $category->depth; + else $this->categories[$category->id]->depth = 0; + + if (isset($category->total)) + $this->categories[$category->id]->total = $category->total; + else $this->categories[$category->id]->total = 0; + + if (isset($category->stock)) + $this->categories[$category->id]->stock = $category->stock; + else $this->categories[$category->id]->stock = 0; + + + if (isset($category->outofstock)) + $this->categories[$category->id]->outofstock = $category->outofstock; + + $this->categories[$category->id]->children = false; + if ($category->total > 0 && isset($this->categories[$category->parent])) + $this->categories[$category->parent]->children = true; + } + + if ($showsmart == "before" || $showsmart == "after") + $this->smart_categories($showsmart); + + return true; + } + + function smart_categories ($method) { + foreach ($this->smarts as $SmartCategory) { + $category = new $SmartCategory(array("noload" => true)); + switch($method) { + case "before": array_unshift($this->categories,$category); break; + default: array_push($this->categories,$category); + } + } + } + + function load_tags ($limits=false) { + $db = DB::get(); + + if ($limits) $limit = " LIMIT {$limits[0]},{$limits[1]}"; + else $limit = ""; + + $tagtable = DatabaseObject::tablename(Tag::$table); + $query = "SELECT t.*,count(sc.product) AS products FROM $this->_table AS sc LEFT JOIN $tagtable AS t ON sc.tag=t.id WHERE sc.tag != 0 GROUP BY t.id ORDER BY t.name ASC$limit"; + $this->tags = $db->query($query,AS_ARRAY); + return true; + } + + function load_category ($category,$options=array()) { + switch ($category) { + case SearchResults::$_slug: return new SearchResults($options); break; + case TagProducts::$_slug: return new TagProducts($options); break; + case BestsellerProducts::$_slug: return new BestsellerProducts(); break; + case CatalogProducts::$_slug: return new CatalogProducts(); break; + case NewProducts::$_slug: return new NewProducts(); break; + case FeaturedProducts::$_slug: return new FeaturedProducts(); break; + case OnSaleProducts::$_slug: return new OnSaleProducts(); break; + case RandomProducts::$_slug: return new RandomProducts(); break; + default: + $key = "id"; + if (!preg_match("/^\d+$/",$category)) $key = "uri"; + return new Category($category,$key); + } + } + + function tag ($property,$options=array()) { + global $Shopp; + + $pages = $Shopp->Settings->get('pages'); + if (SHOPP_PERMALINKS) $path = $Shopp->shopuri; + else $page = add_query_arg('page_id',$pages['catalog']['id'],$Shopp->shopuri); + + switch ($property) { + case "url": return $Shopp->link('catalog'); break; + case "display": + case "type": return $this->type; break; + case "is-landing": + case "is-catalog": return (is_shopp_page('catalog') && $this->type == "catalog"); break; + case "is-category": return (is_shopp_page('catalog') && $this->type == "category"); break; + case "is-product": return (is_shopp_page('catalog') && $this->type == "product"); break; + case "is-cart": return (is_shopp_page('cart')); break; + case "is-checkout": return (is_shopp_page('checkout')); break; + case "is-account": return (is_shopp_page('account')); break; + case "tagcloud": + if (!empty($options['levels'])) $levels = $options['levels']; + else $levels = 7; + if (empty($this->tags)) $this->load_tags(); + $min = -1; $max = -1; + foreach ($this->tags as $tag) { + if ($min == -1 || $tag->products < $min) $min = $tag->products; + if ($max == -1 || $tag->products > $max) $max = $tag->products; + } + if ($max == 0) $max = 1; + $string = '
    '; + foreach ($this->tags as $tag) { + $level = floor((1-$tag->products/$max)*$levels)+1; + if (SHOPP_PERMALINKS) $link = $path.'tag/'.urlencode($tag->name).'/'; + else $link = add_query_arg('shopp_tag',urlencode($tag->name),$page); + $string .= '
  • '; + } + $string .= '
'; + return $string; + break; + case "has-categories": + if (empty($this->categories)) $this->load_categories(array('where'=>'true'),$options['showsmart']); + if (count($this->categories) > 0) return true; else return false; break; + case "categories": + if (!$this->categoryloop) { + reset($this->categories); + $Shopp->Category = current($this->categories); + $this->categoryloop = true; + } else { + $Shopp->Category = next($this->categories); + } + + if (current($this->categories)) { + $Shopp->Category = current($this->categories); + return true; + } else { + $this->categoryloop = false; + return false; + } + break; + case "category-list": + $defaults = array( + 'title' => '', + 'before' => '', + 'after' => '', + 'class' => '', + 'exclude' => '', + 'orderby' => 'name', + 'order' => 'ASC', + 'depth' => 0, + 'childof' => 0, + 'parent' => false, + 'showall' => false, + 'linkall' => false, + 'linkcount' => false, + 'dropdown' => false, + 'hierarchy' => false, + 'products' => false, + 'wraplist' => true, + 'showsmart' => false + ); + + $options = array_merge($defaults,$options); + extract($options, EXTR_SKIP); + + $this->load_categories(array("where"=>"(pd.published='on' OR pd.id IS NULL)","orderby"=>$orderby,"order"=>$order),$showsmart); + + $string = ""; + $depthlimit = $depth; + $depth = 0; + $exclude = explode(",",$exclude); + $classes = ' class="shopp_categories'.(empty($class)?'':' '.$class).'"'; + $wraplist = value_is_true($wraplist); + + if (value_is_true($dropdown)) { + if (!isset($default)) $default = __('Select category…','Shopp'); + $string .= $title; + $string .= '
'; + $string .= ''; + + } else { + $string .= $title; + if ($wraplist) $string .= ''; + foreach ($this->categories as &$category) { + if (!isset($category->total)) $category->total = 0; + if (!isset($category->depth)) $category->depth = 0; + if (!empty($category->id) && in_array($category->id,$exclude)) continue; // Skip excluded categories + if ($depthlimit && $category->depth >= $depthlimit) continue; + if (value_is_true($hierarchy) && $category->depth > $depth) { + $parent = &$previous; + if (!isset($parent->path)) $parent->path = $parent->slug; + $string = substr($string,0,-5); // Remove the previous + $active = ''; + + if (isset($Shopp->Category) && !empty($parent->slug) + && preg_match('/(^|\/)'.$parent->path.'(\/|$)/',$Shopp->Category->uri)) { + $active = ' active'; + } + + $subcategories = '
    '; + $string .= $subcategories; + } + + if (value_is_true($hierarchy) && $category->depth < $depth) { + for ($i = $depth; $i > $category->depth; $i--) { + if (substr($string,strlen($subcategories)*-1) == $subcategories) { + // If the child menu is empty, remove the
      to avoid breaking standards + $string = substr($string,0,strlen($subcategories)*-1).''; + } else $string .= '
    '; + } + } + + if (SHOPP_PERMALINKS) $link = $Shopp->shopuri.'category/'.$category->uri; + else $link = add_query_arg('shopp_category',(!empty($category->id)?$category->id:$category->uri),$Shopp->shopuri); + + $total = ''; + if (value_is_true($products) && $category->total > 0) $total = ' ('.$category->total.')'; + + $current = ''; + if (isset($Shopp->Category) && $Shopp->Category->slug == $category->slug) + $current = ' class="current"'; + + $listing = ''; + if ($category->total > 0 || isset($category->smart) || $linkall) + $listing = ''.$category->name.($linkcount?$total:'').''.(!$linkcount?$total:''); + else $listing = $category->name; + + if (value_is_true($showall) || + $category->total > 0 || + isset($category->smart) || + $category->children) + $string .= ''.$listing.''; + + $previous = &$category; + $depth = $category->depth; + } + if (value_is_true($hierarchy) && $depth > 0) + for ($i = $depth; $i > 0; $i--) { + if (substr($string,strlen($subcategories)*-1) == $subcategories) { + // If the child menu is empty, remove the
      to avoid breaking standards + $string = substr($string,0,strlen($subcategories)*-1).''; + } else $string .= '
    '; + } + if ($wraplist) $string .= '
'; + } + return $string; + break; + case "views": + if (isset($Shopp->Category->controls)) return false; + $string = ""; + $string .= '
    '; + if (isset($options['label'])) $string .= '
  • '.$options['label'].'
  • '; + $string .= '
  • '; + $string .= '
  • '; + $string .= '
'; + return $string; + case "orderby-list": + if (isset($Shopp->Category->controls)) return false; + if (isset($Shopp->Category->smart)) return false; + $menuoptions = Category::sortoptions(); + $title = ""; + $string = ""; + $default = $Shopp->Settings->get('default_product_order'); + if (empty($default)) $default = "title"; + + if (isset($options['default'])) $default = $options['default']; + if (isset($options['title'])) $title = $options['title']; + + if (value_is_true($options['dropdown'])) { + if (isset($Shopp->Cart->data->Category['orderby'])) + $default = $Shopp->Cart->data->Category['orderby']; + $string .= $title; + $string .= '
'; + if (!SHOPP_PERMALINKS) { + foreach ($_GET as $key => $value) + if ($key != 'shopp_orderby') $string .= ''; + } + $string .= ''; + $string .= '
'; + $string .= ''; + } else { + if (strpos($_SERVER['REQUEST_URI'],"?") !== false) + list($link,$query) = explode("\?",$_SERVER['REQUEST_URI']); + $query = $_GET; + unset($query['shopp_orderby']); + $query = http_build_query($query); + if (!empty($query)) $query .= '&'; + + foreach($menuoptions as $value => $option) { + $label = $option; + $href = esc_url($link.'?'.$query.'shopp_orderby='.$value); + $string .= '
  • '.$label.'
  • '; + } + + } + return $string; + break; + case "breadcrumb": + if (isset($Shopp->Category->controls)) return false; + if (empty($this->categories)) $this->load_categories(); + $separator = " » "; + if (isset($options['separator'])) $separator = $options['separator']; + + $category = false; + if (isset($Shopp->Cart->data->breadcrumb)) + $category = $Shopp->Cart->data->breadcrumb; + + $trail = false; + $search = array(); + if (isset($Shopp->Cart->data->Search)) $search = array('search'=>$Shopp->Cart->data->Search); + $path = explode("/",$category); + if ($path[0] == "tag") { + $category = "tag"; + $search = array('tag'=>urldecode($path[1])); + } + $Category = Catalog::load_category($category,$search); + + if (!empty($Category->uri)) { + $type = "category"; + if (isset($Category->tag)) $type = "tag"; + + if (SHOPP_PERMALINKS) + $link = esc_url(add_query_arg($_GET,$Shopp->shopuri.$type.'/'.$Category->uri)); + else { + if (isset($Category->smart)) + $link = esc_url(add_query_arg(array_merge($_GET, + array('shopp_category'=>$Category->slug,'shopp_pid'=>null)), + $Shopp->shopuri)); + else + $link = esc_url(add_query_arg(array_merge($_GET, + array('shopp_category'=>$Category->id,'shopp_pid'=>null)), + $Shopp->shopuri)); + } + + $filters = false; + if (!empty($Shopp->Cart->data->Category[$Category->slug])) + $filters = ' ('.__('Clear Filters','Shopp').')'; + + if (!empty($Shopp->Product)) + $trail .= '
  • '.$Category->name.(!$trail?'':$separator).'
  • '; + elseif (!empty($Category->name)) + $trail .= '
  • '.$Category->name.$filters.(!$trail?'':$separator).'
  • '; + + + // Build category names path by going from the target category up the parent chain + $parentkey = (!empty($Category->id))?$this->categories[$Category->id]->parent:0; + while ($parentkey != 0) { + $tree_category = $this->categories[$parentkey]; + + if (SHOPP_PERMALINKS) $link = $Shopp->shopuri.'category/'.$tree_category->uri; + else $link = esc_url(add_query_arg(array_merge($_GET, + array('shopp_category'=>$tree_category->id,'shopp_pid'=>null)), + $Shopp->shopuri)); + + $trail = '
  • '.$tree_category->name.''. + (empty($trail)?'':$separator).'
  • '.$trail; + + $parentkey = $tree_category->parent; + } + } + + $trail = '
  • '.$pages['catalog']['title'].''.(empty($trail)?'':$separator).'
  • '.$trail; + return ''; + break; + case "search": + global $wp; + $type = "hidden"; + if (isset($options['type'])) $type = $options['type']; + if ($type == "radio") { + $option = "shopp"; + if (isset($options['option'])) $option = $options['option']; + $default = false; + if (isset($options['default'])) $default = value_is_true($options['default']); + $selected = ''; + if ($default) $selected = ' checked="checked"'; + if (!empty($wp->query_vars['st'])) { + $selected = ''; + if ($wp->query_vars['st'] == $option) $selected = ' checked="checked"'; + } + if ($option == "blog") return ''; + else return ''; + } elseif ($type == "menu") { + if (empty($options['store'])) $options['store'] = __('Search the store','Shopp'); + if (empty($options['blog'])) $options['blog'] = __('Search the blog','Shopp'); + if (isset($wp->query_vars['st'])) $selected = $wp->query_vars['st']; + $menu = ''; + return $menu; + } else return ''; + break; + case "catalog-products": + if ($property == "catalog-products") $Shopp->Category = new CatalogProducts($options); + case "new-products": + if ($property == "new-products") $Shopp->Category = new NewProducts($options); + case "featured-products": + if ($property == "featured-products") $Shopp->Category = new FeaturedProducts($options); + case "onsale-products": + if ($property == "onsale-products") $Shopp->Category = new OnSaleProducts($options); + case "bestseller-products": + if ($property == "bestseller-products") $Shopp->Category = new BestsellerProducts($options); + case "random-products": + if ($property == "random-products") $Shopp->Category = new RandomProducts($options); + case "tag-products": + if ($property == "tag-products") $Shopp->Category = new TagProducts($options); + case "related-products": + if ($property == "related-products") $Shopp->Category = new RelatedProducts($options); + case "search-products": + if ($property == "search-products") $Shopp->Category = new SearchResults($options); + case "category": + if ($property == "category") { + if (isset($options['name'])) $Shopp->Category = new Category($options['name'],'name'); + else if (isset($options['slug'])) $Shopp->Category = new Category($options['slug'],'slug'); + else if (isset($options['id'])) $Shopp->Category = new Category($options['id']); + } + if (isset($options['reset'])) return ($Shopp->Category = false); + if (isset($options['title'])) $Shopp->Category->name = $options['title']; + if (isset($options['show'])) $Shopp->Category->loading['limit'] = $options['show']; + if (isset($options['pagination'])) $Shopp->Category->loading['pagination'] = $options['pagination']; + if (isset($options['order'])) $Shopp->Category->loading['order'] = $options['order']; + + if (isset($options['load'])) return true; + if (isset($options['controls']) && !value_is_true($options['controls'])) + $Shopp->Category->controls = false; + if (isset($options['view'])) { + if ($options['view'] == "grid") $Shopp->Category->view = "grid"; + else $Shopp->Category->view = "list"; + } + ob_start(); + if (isset($Shopp->Category->smart) && + file_exists(SHOPP_TEMPLATES."/category-{$Shopp->Category->slug}.php")) + include(SHOPP_TEMPLATES."/category-{$Shopp->Category->slug}.php"); + elseif (isset($Shopp->Category->id) && + file_exists(SHOPP_TEMPLATES."/category-{$Shopp->Category->id}.php")) + include(SHOPP_TEMPLATES."/category-{$Shopp->Category->id}.php"); + else include(SHOPP_TEMPLATES."/category.php"); + $content = ob_get_contents(); + ob_end_clean(); + $Shopp->Category = false; // Reset the current category + return $content; + break; + case "product": + if (isset($options['name'])) $Shopp->Product = new Product($options['name'],'name'); + else if (isset($options['slug'])) $Shopp->Product = new Product($options['slug'],'slug'); + else if (isset($options['id'])) $Shopp->Product = new Product($options['id']); + if (isset($options['load'])) return true; + ob_start(); + if (file_exists(SHOPP_TEMPLATES."/product-{$Shopp->Product->id}.php")) + include(SHOPP_TEMPLATES."/product-{$Shopp->Product->id}.php"); + else include(SHOPP_TEMPLATES."/product.php"); + $content = ob_get_contents(); + ob_end_clean(); + return $content; + break; + case "sideproduct": + $content = false; + $source = $options['source']; + if ($source == "product" && isset($options['product'])) { + // Save original requested product + if ($Shopp->Product) $Requested = $Shopp->Product; + $products = explode(",",$options['product']); + if (!is_array($products)) $products = array($products); + foreach ($products as $product) { + $product = trim($product); + if (empty($product)) continue; + if (preg_match('/^[\d+]$/',$product)) + $Shopp->Product = new Product($product); + else $Shopp->Product = new Product($product,'slug'); + + if (empty($Shopp->Product->id)) continue; + if (isset($options['load'])) return true; + ob_start(); + if (file_exists(SHOPP_TEMPLATES."/sideproduct-{$Shopp->Product->id}.php")) + include(SHOPP_TEMPLATES."/sideproduct-{$Shopp->Product->id}.php"); + else include(SHOPP_TEMPLATES."/sideproduct.php"); + $content .= ob_get_contents(); + ob_end_clean(); + } + // Restore original requested category + if (!empty($Requested)) $Shopp->Product = $Requested; + else $Shopp->Product = false; + } + + if ($source == "category" && isset($options['category'])) { + // Save original requested category + if ($Shopp->Category) $Requested = $Shopp->Category; + if (empty($options['category'])) return false; + $Shopp->Category = Catalog::load_category($options['category']); + $Shopp->Category->load_products($options); + if (isset($options['load'])) return true; + foreach ($Shopp->Category->products as $product) { + $Shopp->Product = $product; + ob_start(); + if (file_exists(SHOPP_TEMPLATES."/sideproduct-{$Shopp->Product->id}.php")) + include(SHOPP_TEMPLATES."/sideproduct-{$Shopp->Product->id}.php"); + else include(SHOPP_TEMPLATES."/sideproduct.php"); + $content .= ob_get_contents(); + ob_end_clean(); + } + // Restore original requested category + if (!empty($Requested)) $Shopp->Category = $Requested; + else $Shopp->Category = false; + } + + return $content; + break; + } + } + +} // end Catalog class + +?> \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/model/Category.php b/blog/wp-content/plugins/shopp/core/model/Category.php new file mode 100644 index 0000000..e86d951 --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/model/Category.php @@ -0,0 +1,1367 @@ +init(self::$table); + + if (!$id) return; + if ($this->load($id,$key)) return true; + return false; + } + + function Smart ($slug) { + $categories = array("new"); + if (in_array($slug,$categories)) return true; + } + + /** + * Load a single record by a slug name */ + function loadby_slug ($slug) { + $db = DB::get(); + + $r = $db->query("SELECT * FROM $this->_table WHERE slug='$slug'"); + $this->populate($r); + + if (!empty($this->id)) return true; + return false; + } + + function load_children($loading=array()) { + if (isset($this->smart) + || empty($this->id) + || empty($this->uri)) return false; + $db = DB::get(); + + if (empty($loading['orderby'])) $loading['orderby'] = "name"; + switch(strtolower($loading['orderby'])) { + case "id": $orderby = "cat.id"; break; + case "slug": $orderby = "cat.slug"; break; + case "count": $orderby = "total"; break; + default: $orderby = "cat.name"; + } + + if (empty($loading['order'])) $loading['order'] = "ASC"; + switch(strtoupper($loading['order'])) { + case "DESC": $order = "DESC"; break; + default: $order = "ASC"; + } + + $catalog_table = DatabaseObject::tablename(Catalog::$table); + $children = $db->query("SELECT cat.*,count(sc.product) AS total FROM $this->_table AS cat LEFT JOIN $catalog_table AS sc ON sc.category=cat.id WHERE cat.uri like '%$this->uri%' AND cat.id <> $this->id GROUP BY cat.id ORDER BY cat.parent DESC,$orderby $order,name ASC",AS_ARRAY); + $children = sort_tree($children,$this->id); + foreach ($children as &$child) { + $this->children[$child->id] = new Category(); + $this->children[$child->id]->populate($child); + $this->children[$child->id]->depth = $child->depth; + $this->children[$child->id]->total = $child->total; + } + + if (!empty($this->children)) return true; + return false; + } + + function load_images () { + global $Shopp; + $db = DB::get(); + + $ordering = $Shopp->Settings->get('product_image_order'); + $orderby = $Shopp->Settings->get('product_image_orderby'); + + if ($ordering == "RAND()") $orderby = $ordering; + else $orderby .= ' '.$ordering; + $table = DatabaseObject::tablename(Asset::$table); + if (empty($this->id)) return false; + $images = $db->query("SELECT id,name,properties,datatype,src FROM $table WHERE parent=$this->id AND context='category' AND (datatype='image' OR datatype='small' OR datatype='thumbnail') ORDER BY $orderby",AS_ARRAY); + + $this->images = array(); + // Organize images into groupings by type + foreach ($images as $key => &$image) { + if (empty($this->images[$image->datatype])) $this->images[$image->datatype] = array(); + $image->properties = unserialize($image->properties); + $image->uri = $Shopp->imguri.$image->id; + $this->images[$image->datatype][] = $image; + } + $this->thumbnail = $this->images['thumbnail'][0]; + return true; + } + + /** + * save_imageorder() + * Updates the sortorder of image assets (source, featured and thumbnails) + * based on the provided array of image ids */ + function save_imageorder ($ordering) { + $db = DB::get(); + $table = DatabaseObject::tablename(Asset::$table); + foreach ($ordering as $i => $id) + $db->query("UPDATE LOW_PRIORITY $table SET sortorder='$i' WHERE id='$id' OR src='$id'"); + return true; + } + + /** + * link_images() + * Updates the product id of the images to link to the product + * when the product being saved is new (has no previous id assigned) */ + function link_images ($images) { + $db = DB::get(); + $table = DatabaseObject::tablename(Asset::$table); + + $query = "UPDATE $table SET parent='$this->id',context='category' WHERE "; + foreach ($images as $i => $id) { + if ($i > 0) $query .= " OR "; + $query .= "id=$id OR src=$id"; + } + $db->query($query); + return true; + } + + + /** + * delete_images() + * Delete provided array of image ids, removing the source image and + * all related images (featured and thumbnails) */ + function delete_images ($images) { + $db = DB::get(); + $table = DatabaseObject::tablename(Asset::$table); + + $query = "DELETE LOW_PRIORITY FROM $table WHERE "; + foreach ($images as $i => $id) { + if ($i > 0) $query .= " OR "; + $query .= "id=$id OR src=$id"; + } + $db->query($query); + return true; + } + + function load_products ($loading=false) { + global $Shopp,$wp; + $db = DB::get(); + + $catalogtable = DatabaseObject::tablename(Catalog::$table); + $producttable = DatabaseObject::tablename(Product::$table); + $pricetable = DatabaseObject::tablename(Price::$table); + $discounttable = DatabaseObject::tablename(Discount::$table); + $promotable = DatabaseObject::tablename(Promotion::$table); + $assettable = DatabaseObject::tablename(Asset::$table); + + $this->paged = false; + $this->pagination = $Shopp->Settings->get('catalog_pagination'); + $this->page = (isset($wp->query_vars['paged']))?$wp->query_vars['paged']:1; + + if (empty($this->page)) $this->page = 1; + + $limit = 1000; // Hard product limit per category to keep resources "reasonable" + + if (!$loading) $loading = $this->loading; + else $loading = array_merge($this->loading,$loading); + + if (!empty($loading['columns'])) $loading['columns'] = ", ".$loading['columns']; + else $loading['columns'] = ''; + + $where = array(); + + if (!empty($loading['where'])) $where[] = "({$loading['where']})"; + + // Handle default WHERE clause matching this category id + if (empty($loading['where']) && !empty($this->id)) + $where[] = "p.id in (SELECT product FROM $catalogtable WHERE category=$this->id)"; + + if (!isset($loading['nostock']) && ($Shopp->Settings->get('outofstock_catalog') == "off")) + $where[] = "p.id in (SELECT product FROM $pricetable WHERE type != 'N/A' AND inventory='off' OR (inventory='on' AND stock > 0))"; + else $where[] = "p.id in (SELECT product FROM $pricetable WHERE type != 'N/A')"; + + if (!isset($loading['joins'])) $loading['joins'] = ''; + if (!empty($Shopp->Cart->data->Category[$this->slug])) { + $spectable = DatabaseObject::tablename(Spec::$table); + + $f = 1; + $filters = ""; + foreach ($Shopp->Cart->data->Category[$this->slug] as $facet => $value) { + if (empty($value)) continue; + $specalias = "spec".($f++); + + // Handle Number Range filtering + $match = ""; + if (!is_array($value) && + preg_match('/^.*?(\d+[\.\,\d]*).*?\-.*?(\d+[\.\,\d]*).*$/',$value,$matches)) { + if ($facet == "Price") { // Prices require complex matching on price line entries + $min = floatvalue($matches[1]); + $max = floatvalue($matches[2]); + if ($matches[1] > 0) $match .= " ((onsale=0 AND (minprice >= $min OR maxprice >= $min)) OR (onsale=1 AND (minsaleprice >= $min OR maxsaleprice >= $min)))"; + if ($matches[2] > 0) $match .= (empty($match)?"":" AND ")." ((onsale=0 AND (minprice <= $max OR maxprice <= $max)) OR (onsale=1 AND (minsaleprice <= $max OR maxsaleprice <= $max)))"; + } else { // Spec-based numbers are somewhat more straightforward + if ($matches[1] > 0) $match .= "$specalias.numeral >= {$matches[1]}"; + if ($matches[2] > 0) $match .= (empty($match)?"":" AND ")."$specalias.numeral <= {$matches[2]}"; + } + } else $match = "$specalias.content='$value'"; // No range, direct value match + + // Use HAVING clause for filtering by pricing information + // because of data aggregation + if ($facet == "Price") { + $loading['having'] .= (empty($loading['having'])?'HAVING ':' AND ').$match; + continue; + } + + $loading['joins'] .= " LEFT JOIN $spectable AS $specalias ON $specalias.product=p.id AND $specalias.name='$facet'"; + $filters .= (empty($filters))?$match:" AND ".$match; + } + if (!empty($filters)) $where[] = $filters; + + } + $where[] = "p.published='on'"; + $loading['where'] = join(" AND ",$where); + + $defaultOrder = $Shopp->Settings->get('default_product_order'); + if (empty($defaultOrder)) $defaultOrder = "title"; + $ordering = isset($Shopp->Cart->data->Category['orderby'])? + $Shopp->Cart->data->Category['orderby']:$defaultOrder; + if (!empty($loading['order'])) $ordering = $loading['order']; + switch ($ordering) { + case "bestselling": + $purchasedtable = DatabaseObject::tablename(Purchased::$table); + $loading['columns'] .= ',count(DISTINCT pur.id) AS sold'; + $loading['joins'] .= " LEFT JOIN $purchasedtable AS pur ON p.id=pur.product"; + $loading['order'] = "sold DESC"; + break; + case "highprice": $loading['order'] = "pd.price DESC"; break; + case "lowprice": $loading['order'] = "pd.price ASC"; break; + case "newest": $loading['order'] = "pd.created DESC"; break; + case "oldest": $loading['order'] = "pd.created ASC"; break; + case "random": $loading['order'] = "RAND()"; break; + case "": + case "title": + default: $loading['order'] = "p.name ASC"; break; + } + if (!empty($loading['orderby'])) $loading['order'] = $loading['orderby']; + + if (empty($loading['limit'])) { + if ($this->pagination > 0 && is_numeric($this->page)) { + if( !$this->pagination || $this->pagination < 0 ) + $this->pagination = $limit; + $start = ($this->pagination * ($this->page-1)); + + $loading['limit'] = "$start,$this->pagination"; + } else $loading['limit'] = $limit; + } else $limit = (int)$loading['limit']; + + $columns = "p.*, + img.id AS thumbnail,img.properties AS thumbnail_properties,MAX(pr.status) as promos, + SUM(DISTINCT IF(pr.type='Percentage Off',pr.discount,0))AS percentoff, + SUM(DISTINCT IF(pr.type='Amount Off',pr.discount,0)) AS amountoff, + if (pr.type='Free Shipping',1,0) AS freeshipping, + if (pr.type='Buy X Get Y Free',pr.buyqty,0) AS buyqty, + if (pr.type='Buy X Get Y Free',pr.getqty,0) AS getqty, + MAX(pd.price) AS maxprice,MIN(pd.price) AS minprice, + IF(pd.sale='on',1,IF (pr.discount > 0,1,0)) AS onsale, + MAX(pd.saleprice) as maxsaleprice,MIN(pd.saleprice) AS minsaleprice, + IF(pd.inventory='on',1,0) AS inventory, + SUM(pd.stock) as stock"; + + // Query without promotions for MySQL servers prior to 5 + if (version_compare($db->version,'5.0','<')) { + $columns = "p.*, + img.id AS thumbnail,img.properties AS thumbnail_properties, + MAX(pd.price) AS maxprice,MIN(pd.price) AS minprice, + IF(pd.sale='on',1,0) AS onsale, + MAX(pd.saleprice) as maxsaleprice,MIN(pd.saleprice) AS minsaleprice, + IF(pd.inventory='on',1,0) AS inventory, + SUM(pd.stock) as stock"; + } + + // Handle alphabetic page requests + if ((!isset($Shopp->Category->controls) || + (isset($Shopp->Category->controls) && $Shopp->Category->controls !== false)) && + ((isset($loading['pagination']) && $loading['pagination'] == "alpha") || + !is_numeric($this->page))) { + + $alphanav = range('A','Z'); + + $ac = "SELECT count(DISTINCT p.id) AS total,IF(LEFT(p.name,1) REGEXP '[0-9]',LEFT(p.name,1),LEFT(SOUNDEX(p.name),1)) AS letter,AVG(IF(pd.sale='on',pd.saleprice,pd.price)) as avgprice + FROM $producttable AS p + LEFT JOIN $pricetable AS pd ON pd.product=p.id AND pd.type != 'N/A' + LEFT JOIN $discounttable AS dc ON dc.product=p.id AND dc.price=pd.id + LEFT JOIN $promotable AS pr ON pr.id=dc.promo + LEFT JOIN $assettable AS img ON img.parent=p.id AND img.context='product' AND img.datatype='thumbnail' AND img.sortorder=0 + {$loading['joins']} + WHERE {$loading['where']} + GROUP BY letter"; + $alpha = $db->query($ac); + + $existing = current($alpha); + if (!isset($this->alpha['0-9'])) { + $this->alpha['0-9'] = new stdClass(); + $this->alpha['0-9']->letter = '0-9'; + $this->alpha['0-9']->total = 0; + $this->alpha['0-9']->avg = 0; + } + while (is_numeric($existing->letter)) { + $this->alpha['0-9']->total += $existing->total; + $this->alpha['0-9']->avg = ($this->alpha['0-9']->avg+$existing->avg)/2; + $this->alpha['0-9']->letter = '0-9'; + $existing = next($alpha); + } + + foreach ($alphanav as $letter) { + if ($existing->letter == $letter) { + $this->alpha[$letter] = $existing; + $existing = next($alpha); + } else { + $entry = new stdClass(); + $entry->letter = $letter; + $entry->total = 0; + $entry->avg = 0; + $this->alpha[$letter] = $entry; + } + } + $this->paged = true; + if (!is_numeric($this->page)) { + $alphafilter = $this->page == "0-9"? + "(LEFT(p.name,1) REGEXP '[0-9]') = 1": + "IF(LEFT(p.name,1) REGEXP '[0-9]',LEFT(p.name,1),LEFT(SOUNDEX(p.name),1))='$this->page'"; + $loading['where'] .= (empty($loading['where'])?"":" AND ").$alphafilter; + } + + } + + $query = "SELECT SQL_CALC_FOUND_ROWS $columns{$loading['columns']} + FROM $producttable AS p + LEFT JOIN $pricetable AS pd ON pd.product=p.id AND pd.type != 'N/A' + LEFT JOIN $discounttable AS dc ON dc.product=p.id AND dc.price=pd.id + LEFT JOIN $promotable AS pr ON pr.id=dc.promo + LEFT JOIN $assettable AS img ON img.parent=p.id AND img.context='product' AND img.datatype='thumbnail' AND img.sortorder=0 + {$loading['joins']} + WHERE {$loading['where']} + GROUP BY p.id {$loading['having']} + ORDER BY {$loading['order']} + LIMIT {$loading['limit']}"; + + // Execute the main category products query + $products = $db->query($query,AS_ARRAY); + + if ($this->pagination > 0 && $limit > $this->pagination) { + $total = $db->query("SELECT FOUND_ROWS() as count"); + $this->total = $total->count; + $this->pages = ceil($this->total / $this->pagination); + if ($this->pages > 1) $this->paged = true; + } + + if ($this->pagination == 0 || $limit < $this->pagination) + $this->total = count($this->products); + + $this->pricing['min'] = 0; + $this->pricing['max'] = 0; + + $prices = array(); + foreach ($products as &$product) { + if ($product->maxsaleprice == 0) $product->maxsaleprice = $product->maxprice; + if ($product->minsaleprice == 0) $product->minsaleprice = $product->minprice; + + $prices[] = $product->onsale?$product->minsaleprice:$product->minprice; + + if (!empty($product->percentoff)) { + $product->maxsaleprice = $product->maxsaleprice - ($product->maxsaleprice * ($product->percentoff/100)); + $product->minsaleprice = $product->minsaleprice - ($product->minsaleprice * ($product->percentoff/100)); + } + + if (!empty($product->amountoff)) { + $product->maxsaleprice = $product->maxsaleprice - $product->amountoff; + $product->minsaleprice = $product->minsaleprice - $product->amountoff; + } + + if ($this->pricing['max'] == 0 || $product->maxsaleprice > $this->pricing['max']) + $this->pricing['max'] = $product->maxsaleprice; + + if ($this->pricing['min'] == 0 || $product->minsaleprice < $this->pricing['min']) + $this->pricing['min'] = $product->minsaleprice; + + $this->products[$product->id] = new Product(); + $this->products[$product->id]->populate($product); + + // Special property for Bestseller category + if (isset($product->sold) && $product->sold) + $this->products[$product->id]->sold = $product->sold; + + // Special property Promotions + if (isset($product->promos)) + $this->products[$product->id]->promos = $product->promos; + + if (!empty($product->thumbnail)) { + $image = new stdClass(); + $image->properties = unserialize($product->thumbnail_properties); + if (SHOPP_PERMALINKS) $image->uri = $Shopp->imguri.$product->thumbnail; + else $image->uri = add_query_arg('shopp_image',$product->thumbnail,$Shopp->imguri); + $this->products[$product->id]->imagesets['thumbnail'] = array(); + $this->products[$product->id]->imagesets['thumbnail'][] = $image; + $this->products[$product->id]->thumbnail =& $this->products[$product->id]->imagesets['thumbnail'][0]; + } + + } + $this->pricing['average'] = 0; + if (count($prices) > 0) $this->pricing['average'] = array_sum($prices)/count($prices); + + if (!isset($loading['load'])) $loading['load'] = array('prices'); + + if (count($this->products) > 0) { + $Processing = new Product(); + $Processing->load_data($loading['load'],$this->products); + } + + $this->loaded = true; + + } + + function rss () { + global $Shopp; + $db = DB::get(); + + do_action_ref_array('shopp_category_rss',array(&$this)); + + if (!$this->products) $this->load_products(array('limit'=>500)); + + if (SHOPP_PERMALINKS) $rssurl = $Shopp->shopuri.'feed/'; + else $rssurl = add_query_arg('shopp_lookup','products-rss',$Shopp->shopuri); + + $rss = array('title' => get_bloginfo('name')." ".$this->name, + 'link' => $rssurl, + 'description' => $this->description, + 'sitename' => get_bloginfo('name').' ('.get_bloginfo('url').')'); + $rss = apply_filters('shopp_rss_meta',$rss); + + $items = array(); + foreach ($this->products as $product) { + if (isset($product->thumbnail_properties)) + $product->thumbnail_properties = unserialize($product->thumbnail_properties); + $item = array(); + $item['guid'] = array($product->id,'isPermaLink'=>'false'); + $item['title'] = attribute_escape($product->name); + if (SHOPP_PERMALINKS) $item['link'] = $Shopp->shopuri.$product->id; + else $item['link'] = urlencode(add_query_arg('shopp_pid',$product->id,$Shopp->shopuri)); + $item['description'] = "thumbnail)) { + $item['description'] .= ''; + $item['description'] .= ''.$product->name.''; + $item['description'] .= ''; + $item['g:image_link'] = $product->thumbnail->uri; + } + + $item['g:condition'] = "new"; + $pricing = ""; + if ($product->onsale) { + if ($product->pricerange['min']['saleprice'] != $product->pricerange['max']['saleprice']) $pricing .= "from "; + $pricing .= money($product->pricerange['min']['saleprice']); + } else { + if ($product->pricerange['min']['price'] != $product->pricerange['max']['price']) $pricing .= "from "; + $pricing .= money($product->pricerange['min']['price']); + } + $item['g:price'] = number_format(($product->onsale)? + $product->pricerange['min']['saleprice']:$product->pricerange['min']['price'],2); + $item['g:price_type'] = "starting"; + + $item['description'] .= "

    $pricing

    "; + $item['description'] .= wpautop(attribute_escape($product->description)); + $item['description'] .= "]]>"; + + $item = apply_filters('shopp_rss_item',$item,$product); + //$item['g:quantity'] = $product->stock; + + $items[] = $item; + } + $rss['items'] = $items; + + return $rss; + } + + function sortoptions () { + return array( + "title" => __('Title','Shopp'), + "bestselling" => __('Bestselling','Shopp'), + "highprice" => __('Price High to Low','Shopp'), + "lowprice" => __('Price Low to High','Shopp'), + "newest" => __('Newest to Oldest','Shopp'), + "oldest" => __('Oldest to Newest','Shopp'), + "random" => __('Random','Shopp') + ); + } + + function tag ($property,$options=array()) { + global $Shopp; + $db = DB::get(); + + $page = $Shopp->link('catalog'); + if (SHOPP_PERMALINKS) $imageuri = trailingslashit($page)."images/"; + else $imageuri = add_query_arg('shopp_image','=',$page); + + if (SHOPP_PERMALINKS) { + $pages = $Shopp->Settings->get('pages'); + if ($page == trailingslashit(get_bloginfo('url'))) + $page .= $pages['catalog']['name']."/"; + } + + switch ($property) { + case "link": + case "url": + return (SHOPP_PERMALINKS)? + $Shopp->shopuri."category/".urldecode($this->uri): + add_query_arg('shopp_category',$this->id,$Shopp->shopuri); + break; + case "id": return $this->id; break; + case "name": return $this->name; break; + case "slug": return urldecode($this->slug); break; + case "description": return wpautop($this->description); break; + case "total": return $this->total; break; + case "has-products": + case "hasproducts": + if (empty($this->id) && empty($this->slug)) return false; + if (isset($options['load'])) { + $dataset = explode(",",$options['load']); + $options['load'] = array(); + foreach ($dataset as $name) $options['load'][] = trim($name); + } else { + $options['load'] = array('prices'); + } + if (!$this->loaded) $this->load_products($options); + if (count($this->products) > 0) return true; else return false; break; + case "products": + if (!$this->productloop) { + reset($this->products); + $Shopp->Product = current($this->products); + $this->productsidx = 0; + $this->productloop = true; + } else { + $Shopp->Product = next($this->products); + $this->productsidx++; + } + + if (current($this->products)) { + $Shopp->Product = current($this->products); + return true; + } + else { + $this->productloop = false; + return false; + } + break; + case "row": + if (empty($options['products'])) $options['products'] = $Shopp->Settings->get('row_products'); + if ($this->productsidx > 0 && $this->productsidx % $options['products'] == 0) return true; + else return false; + break; + case "has-categories": + case "hascategories": + if (empty($this->children)) $this->load_children(); + return (!empty($this->children)); + break; + case "is-subcategory": + case "issubcategory": + return ($this->parent != 0); + break; + case "subcategories": + if (!$this->childloop) { + reset($this->children); + $this->child = current($this->children); + $this->childidx = 0; + $this->childloop = true; + } else { + $this->child = next($this->children); + $this->childidx++; + } + + if (current($this->children)) { + $this->child = current($this->children); + return true; + } else { + $this->childloop = false; + return false; + } + break; + case "subcategory-list": + if (isset($Shopp->Category->controls)) return false; + + $defaults = array( + 'title' => '', + 'before' => '', + 'after' => '', + 'class' => '', + 'depth' => 0, + 'orderby' => 'name', + 'order' => 'ASC', + 'parent' => false, + 'showall' => false, + 'dropdown' => false, + 'hierarchy' => false, + 'products' => false + ); + + $options = array_merge($defaults,$options); + extract($options, EXTR_SKIP); + + if (!$this->children) $this->load_children(array('orderby'=>$orderby,'order'=>$order)); + if (empty($this->children)) return false; + + $string = ""; + $depthlimit = $depth; + $depth = 0; + $count = 0; + + if (value_is_true($dropdown)) { + $string .= $title; + $string .= ''; + $string .= ''; + + } else { + if (!empty($class)) $classes = ' class="'.$class.'"'; + $string .= $title.''; + foreach ($this->children as &$category) { + if (!empty($show) && $count+1 > $show) break; + if (value_is_true($hierarchy) && $depthlimit && + $category->depth >= $depthlimit) continue; + if (value_is_true($hierarchy) && $category->depth > $depth) { + $parent = &$previous; + if (!isset($parent->path)) $parent->path = $parent->slug; + $string .= '
      '; + } + if (value_is_true($hierarchy) && $category->depth < $depth) $string .= '
    '; + + if (SHOPP_PERMALINKS) $link = $Shopp->shopuri.'category/'.$category->uri; + else $link = add_query_arg('shopp_category',$category->id,$Shopp->shopuri); + + $total = ''; + if (value_is_true($products)) $total = ' ('.$category->products.')'; + + if (value_is_true($showall) || $category->products > 0 || $category->smart) // Only show categories with products + $string .= '
  • '.$category->name.''.$total.'
  • '; + + $previous = &$category; + $depth = $category->depth; + $count++; + } + if (value_is_true($hierarchy)) + for ($i = 0; $i < $depth; $i++) $string .= ""; + $string .= ''; + } + return $string; + break; + case "section-list": + if (empty($this->id)) return false; + if (isset($Shopp->Category->controls)) return false; + if (empty($Shopp->Catalog->categories)) $Shopp->Catalog->load_categories(array("where"=>"(pd.published='on' OR pd.id IS NULL)")); + if (empty($Shopp->Catalog->categories)) return false; + if (!$this->children) $this->load_children(); + + $defaults = array( + 'title' => '', + 'before' => '', + 'after' => '', + 'class' => '', + 'classes' => '', + 'exclude' => '', + 'total' => '', + 'current' => '', + 'listing' => '', + 'depth' => 0, + 'parent' => false, + 'showall' => false, + 'linkall' => false, + 'dropdown' => false, + 'hierarchy' => false, + 'products' => false, + 'wraplist' => true + ); + + $options = array_merge($defaults,$options); + extract($options, EXTR_SKIP); + + $string = ""; + $depthlimit = $depth; + $depth = 0; + $wraplist = value_is_true($wraplist); + $exclude = explode(",",$exclude); + $section = array(); + + // Identify root parent + if (empty($this->id)) return false; + $parent = $this->id; + while($parent != 0) { + if ($Shopp->Catalog->categories[$parent]->parent == 0 + || $Shopp->Catalog->categories[$parent]->parent == $parent) break; + $parent = $Shopp->Catalog->categories[$parent]->parent; + } + $root = $Shopp->Catalog->categories[$parent]; + if ($this->id == $parent && empty($this->children)) return false; + + // Build the section + $section[] = $root; + $in = false; + foreach ($Shopp->Catalog->categories as &$c) { + if ($in && $c->depth == $root->depth) break; // Done + if ($in) $section[] = $c; + if (!$in && isset($c->id) && $c->id == $root->id) $in = true; + } + + if (value_is_true($dropdown)) { + $string .= $title; + $string .= ''; + $string .= ''; + + } else { + if (!empty($class)) $classes = ' class="'.$class.'"'; + $string .= $title; + if ($wraplist) $string .= ''; + foreach ($section as &$category) { + if (in_array($category->id,$exclude)) continue; // Skip excluded categories + if (value_is_true($hierarchy) && $depthlimit && + $category->depth >= $depthlimit) continue; + if (value_is_true($hierarchy) && $category->depth > $depth) { + $parent = &$previous; + if (!isset($parent->path) && isset($parent->slug)) $parent->path = $parent->slug; + $string = substr($string,0,-5); + $string .= '
      '; + } + if (value_is_true($hierarchy) && $category->depth < $depth) $string .= '
    '; + + if (SHOPP_PERMALINKS) $link = $Shopp->shopuri.'category/'.$category->uri; + else $link = add_query_arg('shopp_category',$category->id,$Shopp->shopuri); + + if (value_is_true($products)) $total = ' ('.$category->total.')'; + + if ($category->total > 0 || isset($category->smart) || $linkall) $listing = ''.$category->name.$total.''; + else $listing = $category->name; + + if (value_is_true($showall) || + $category->total > 0 || + $category->children) + $string .= '
  • '.$listing.'
  • '; + + $previous = &$category; + $depth = $category->depth; + } + if (value_is_true($hierarchy) && $depth > 0) + for ($i = $depth; $i > 0; $i--) $string .= ''; + + if ($wraplist) $string .= ''; + } + return $string; + break; + case "pagination": + if (!$this->paged) return ""; + + global $wp; + // Set options + if (!isset($options['label'])) $options['label'] = __("Pages:","Shopp"); + if (!isset($options['next'])) $options['next'] = __("next","Shopp"); + if (!isset($options['previous'])) $options['previous'] = __("previous","Shopp"); + + $navlimit = 1000; + if (!empty($options['show'])) $navlimit = $options['show']; + + $before = "
    ".$options['label']; // Set the label + if (!empty($options['before'])) $before = $options['before']; + + $after = "
    "; + if (!empty($options['after'])) $after = $options['after']; + + $type = "category"; + if (isset($wp->query_vars['shopp_tag'])) $type = "tag"; + + $string = ""; + if (isset($this->alpha) && $this->paged) { + + $string .= '
      '; + foreach ($this->alpha as $alpha) { + $link = (SHOPP_PERMALINKS)? + "$page"."$type/$this->uri/page/$alpha->letter/": + "$page&shopp_$type=$this->uri&paged=$alpha->letter"; + if ($alpha->total > 0) + $string .= '
    • '.$alpha->letter.'
    • '; + else $string .= '
    • '.$alpha->letter.'
    • '; + } + $string .= '
    '; + return $string; + } + + if ($this->pages > 1) { + + if ( $this->pages > $navlimit ) $visible_pages = $navlimit + 1; + else $visible_pages = $this->pages + 1; + $jumps = ceil($visible_pages/2); + $string .= $before; + + $string .= '
      '; + if ( $this->page <= floor(($navlimit) / 2) ) { + $i = 1; + } else { + $i = $this->page - floor(($navlimit) / 2); + $visible_pages = $this->page + floor(($navlimit) / 2) + 1; + if ($visible_pages > $this->pages) $visible_pages = $this->pages + 1; + if ($i > 1) { + $link = (SHOPP_PERMALINKS)? + "$page"."$type/$this->uri/page/$i/": + "$page&shopp_$type=$this->uri&paged=$i"; + $string .= '
    • 1
    • '; + + $pagenum = ($this->page - $jumps); + if ($pagenum < 1) $pagenum = 1; + $link = (SHOPP_PERMALINKS)? + "$page"."$type/$this->uri/page/$pagenum/": + "$page&shopp_$type=$this->uri&paged=$pagenum"; + + $string .= '
    • «
    • '; + } + } + + // Add previous button + if (!value_is_true($options['previous']) && $this->page > 1) { + $prev = $this->page-1; + $link = (SHOPP_PERMALINKS)? + "$page"."$type/$this->uri/page/$prev/": + "$page&shopp_$type=$this->uri&paged=$prev"; + $string .= ''; + } else $string .= ''; + // end previous button + + while ($i < $visible_pages) { + $link = (SHOPP_PERMALINKS)? + "$page"."$type/$this->uri/page/$i/": + "$page&shopp_$type=$this->uri&paged=$i"; + if ( $i == $this->page ) $string .= '
    • '.$i.'
    • '; + else $string .= '
    • '.$i.'
    • '; + $i++; + } + if ($this->pages > $visible_pages) { + $pagenum = ($this->page + $jumps); + if ($pagenum > $this->pages) $pagenum = $this->pages; + $link = (SHOPP_PERMALINKS)? + "$page"."$type/$this->uri/page/$pagenum/": + "$page&shopp_$type=$this->uri&paged=$pagenum"; + $string .= '
    • »
    • '; + + $link = (SHOPP_PERMALINKS)? + "$page"."$type/$this->uri/page/$this->pages/": + "$page&shopp_$type=$this->uri&paged=$this->pages"; + $string .= '
    • '.$this->pages.'
    • '; + } + + // Add next button + if (!value_is_true($options['next']) && $this->page < $this->pages) { + $next = $this->page+1; + $link = (SHOPP_PERMALINKS)? + "$page"."$type/$this->uri/page/$next/": + "$page&shopp_$type=$this->uri&paged=$next"; + $string .= ''; + } else $string .= ''; + + $string .= '
    '; + $string .= $after; + } + return $string; + break; + case "has-faceted-menu": return ($this->facetedmenus == "on"); break; + case "faceted-menu": + if ($this->facetedmenus == "off") return; + $output = ""; + $CategoryFilters =& $Shopp->Cart->data->Category[$this->slug]; + $link = $_SERVER['REQUEST_URI']; + if (!isset($options['cancel'])) $options['cancel'] = "X"; + if (strpos($_SERVER['REQUEST_URI'],"?") !== false) + list($link,$query) = explode("?",$_SERVER['REQUEST_URI']); + $query = $_GET; + unset($query['shopp_catfilters']); + $query = http_build_query($query); + if (!empty($query)) $query .= '&'; + + $list = ""; + if (is_array($CategoryFilters)) { + foreach($CategoryFilters AS $facet => $filter) { + $href = add_query_arg('shopp_catfilters['.urlencode($facet).']','',esc_url($link)); + if (preg_match('/^(.*?(\d+[\.\,\d]*).*?)\-(.*?(\d+[\.\,\d]*).*)$/',$filter,$matches)) { + $label = $matches[1].' — '.$matches[3]; + if ($matches[2] == 0) $label = __('Under ','Shopp').$matches[3]; + if ($matches[4] == 0) $label = $matches[1].' '.__('and up','Shopp'); + } else $label = $filter; + if (!empty($filter)) $list .= '
  • '.$facet.': '.stripslashes($label).' '.$options['cancel'].'
  • '; + } + $output .= '
      '.$list.'
    '; + } + + if ($this->pricerange == "auto" && empty($CategoryFilters['Price'])) { + if (!$this->loaded) $this->load_products(); + $list = ""; + $this->priceranges = auto_ranges($this->pricing['average'],$this->pricing['max'],$this->pricing['min']); + foreach ($this->priceranges as $range) { + $href = add_query_arg('shopp_catfilters[Price]',urlencode(money($range['min']).'-'.money($range['max'])),esc_url($link)); + $label = money($range['min']).' — '.money($range['max']-0.01); + if ($range['min'] == 0) $label = __('Under ','Shopp').money($range['max']); + elseif ($range['max'] == 0) $label = money($range['min']).' '.__('and up','Shopp'); + $list .= '
  • '.$label.'
  • '; + } + if (!empty($this->priceranges)) $output .= '

    '.__('Price Range','Shopp').'

    '; + $output .= '
      '.$list.'
    '; + } + + $catalogtable = DatabaseObject::tablename(Catalog::$table); + $producttable = DatabaseObject::tablename(Product::$table); + $spectable = DatabaseObject::tablename(Spec::$table); + + $results = $db->query("SELECT spec.name,spec.content, + IF(spec.numeral > 0,spec.name,spec.content) AS merge, + count(*) AS total,avg(numeral) AS avg,max(numeral) AS max,min(numeral) AS min + FROM $catalogtable AS cat + LEFT JOIN $producttable AS p ON cat.product=p.id + LEFT JOIN $spectable AS spec ON spec.product=p.id + WHERE cat.category=$this->id GROUP BY merge ORDER BY spec.name,merge",AS_ARRAY); + + $specdata = array(); + foreach ($results as $data) { + if (isset($specdata[$data->name])) { + if (!is_array($specdata[$data->name])) + $specdata[$data->name] = array($specdata[$data->name]); + $specdata[$data->name][] = $data; + } else $specdata[$data->name] = $data; + } + + if (is_array($this->specs)) { + foreach ($this->specs as $spec) { + $list = ""; + if (!empty($CategoryFilters[$spec['name']])) continue; + + // For custom menu presets + if ($spec['facetedmenu'] == "custom" && !empty($spec['options'])) { + foreach ($spec['options'] as $option) { + $href = add_query_arg('shopp_catfilters['.$spec['name'].']',urlencode($option['name']),esc_url($_SERVER['REQUEST_URI'])); + $list .= '
  • '.$option['name'].'
  • '; + } + $output .= '

    '.$spec['name'].'

      '.$list.'
    '; + + // For preset ranges + } elseif ($spec['facetedmenu'] == "ranges" && !empty($spec['options'])) { + foreach ($spec['options'] as $i => $option) { + $matches = array(); + $format = '%s'; + $next = 0; + if (isset($spec['options'][$i+1])) { + if (preg_match('/(\d+[\.\,\d]*)/',$spec['options'][$i+1]['name'],$matches)) + $next = $matches[0]; + } + $matches = array(); + $range = array("min" => 0,"max" => 0); + if (preg_match('/^(.*?)(\d+[\.\,\d]*)(.*)$/',$option['name'],$matches)) { + $base = $matches[2]; + $format = $matches[1].'%s'.$matches[3]; + if (!isset($spec['options'][$i+1])) $range['min'] = $base; + else $range = array("min" => $base, "max" => ($next-1)); + } + if ($i == 1) { + $href = esc_url($link.'?'.$query).'shopp_catfilters['.$spec['name'].']='.urlencode(sprintf($format,'0').'-'.sprintf($format,$range['min'])); + $label = __('Under ','Shopp').sprintf($format,$range['min']); + $list .= '
  • '.$label.'
  • '; + } + + $href = esc_url($link.'?'.$query).'shopp_catfilters['.$spec['name'].']='.urlencode(sprintf($format,$range['min']).'-'.sprintf($format,$range['max'])); + $label = sprintf($format,$range['min']).' — '.sprintf($format,$range['max']); + if ($range['max'] == 0) $label = sprintf($format,$range['min']).' '.__('and up','Shopp'); + $list .= '
  • '.$label.'
  • '; + } + $output .= '

    '.$spec['name'].'

      '.$list.'
    '; + + // For automatically building the menu options + } elseif ($spec['facetedmenu'] == "auto" && isset($specdata[$spec['name']])) { + + if (is_array($specdata[$spec['name']])) { // Generate from text values + foreach ($specdata[$spec['name']] as $option) { + $href = esc_url($link.'?'.$query).'shopp_catfilters['.$spec['name'].']='.urlencode($option->content); + $list .= '
  • '.$option->content.'
  • '; + } + $output .= '

    '.$spec['name'].'

      '.$list.'
    '; + } else { // Generate number ranges + $format = '%s'; + if (preg_match('/^(.*?)(\d+[\.\,\d]*)(.*)$/',$specdata[$spec['name']]->content,$matches)) + $format = $matches[1].'%s'.$matches[3]; + + $ranges = auto_ranges($specdata[$spec['name']]->avg,$specdata[$spec['name']]->max,$specdata[$spec['name']]->min); + foreach ($ranges as $range) { + $href = esc_url($link.'?'.$query.'shopp_catfilters['.$spec['name'].']='.urlencode($range['min'].'-'.$range['max'])); + $label = sprintf($format,$range['min']).' — '.sprintf($format,$range['max']); + if ($range['min'] == 0) $label = __('Under ','Shopp').sprintf($format,$range['max']); + elseif ($range['max'] == 0) $label = sprintf($format,$range['min']).' '.__('and up','Shopp'); + $list .= '
  • '.$label.'
  • '; + } + if (!empty($list)) $output .= '

    '.$spec['name'].'

    '; + $output .= '
      '.$list.'
    '; + + } + } + } + } + + + return $output; + break; + + case "thumbnail": + if (empty($this->images)) $this->load_images(); + if (!empty($options['class'])) $options['class'] = ' class="'.$options['class'].'"'; + if (isset($this->thumbnail)) { + $img = $this->thumbnail; + return ''.$this->name.' '.$img->datatype.''; break; + } + break; + case "has-images": + if (empty($options['type'])) $options['type'] = "thumbnail"; + if (empty($this->images)) $this->load_images(); + return (count($this->images[$options['type']]) > 0); break; + case "images": + if (empty($options['type'])) $options['type'] = "thumbnail"; + if (!$this->imageloop) { + reset($this->images[$options['type']]); + $this->imageloop = true; + } else next($this->images[$options['type']]); + + if (current($this->images[$options['type']])) return true; + else { + $this->imageloop = false; + return false; + } + break; + case "image": + if (empty($options['type'])) $options['type'] = "thumbnail"; + $img = current($this->images[$options['type']]); + if (!empty($options['class'])) $options['class'] = ' class="'.$options['class'].'"'; + $string = ""; + if (!empty($options['zoom'])) $string .= ''; + $string .= ''.$this->name.' '.$img->datatype.''; + if (!empty($options['zoom'])) $string .= ""; + return $string; + break; + + } + } + +} // end Category class + +class CatalogProducts extends Category { + static $_slug = "catalog"; + + function CatalogProducts ($options=array()) { + $this->name = __("Catalog Products","Shopp"); + $this->slug = self::$_slug; + $this->uri = $this->slug; + $this->smart = true; + $this->loading = array('where'=>"true"); + if (isset($options['order'])) $this->loading['order'] = $options['order']; + if (isset($options['show'])) $this->loading['limit'] = $options['show']; + if (isset($options['pagination'])) $this->loading['pagination'] = $options['pagination']; + } + +} + +class NewProducts extends Category { + static $_slug = "new"; + + function NewProducts ($options=array()) { + $this->name = __("New Products","Shopp"); + $this->slug = self::$_slug; + $this->uri = $this->slug; + $this->smart = true; + $this->loading = array('where'=>"p.id IS NOT NULL",'order'=>'newest'); + if (isset($options['columns'])) $this->loading['columns'] = $options['columns']; + if (isset($options['show'])) $this->loading['limit'] = $options['show']; + if (isset($options['pagination'])) $this->loading['pagination'] = $options['pagination']; + } + +} + +class FeaturedProducts extends Category { + static $_slug = "featured"; + + function FeaturedProducts ($options=array()) { + $this->name = __("Featured Products","Shopp"); + $this->slug = self::$_slug; + $this->uri = $this->slug; + $this->smart = true; + $this->loading = array('where'=>"p.featured='on'",'order'=>'p.modified DESC'); + if (isset($options['show'])) $this->loading['limit'] = $options['show']; + if (isset($options['pagination'])) $this->loading['pagination'] = $options['pagination']; + } + +} + +class OnSaleProducts extends Category { + static $_slug = "onsale"; + + function OnSaleProducts ($options=array()) { + $this->name = __("On Sale","Shopp"); + $this->slug = self::$_slug; + $this->uri = $this->slug; + $this->smart = true; + $this->loading = array('where'=>"pd.sale='on' OR (pr.status='enabled' AND pr.discount > 0 AND ((UNIX_TIMESTAMP(starts)=1 AND UNIX_TIMESTAMP(ends)=1) OR (UNIX_TIMESTAMP(now()) > UNIX_TIMESTAMP(starts) AND UNIX_TIMESTAMP(now()) < UNIX_TIMESTAMP(ends)) ))",'order'=>'p.modified DESC'); + if (isset($options['show'])) $this->loading['limit'] = $options['show']; + if (isset($options['pagination'])) $this->loading['pagination'] = $options['pagination']; + } + +} + +class BestsellerProducts extends Category { + static $_slug = "bestsellers"; + + function BestsellerProducts ($options=array()) { + $this->name = __("Bestsellers","Shopp"); + $this->slug = self::$_slug; + $this->uri = $this->slug; + $this->smart = true; + $purchasedtable = DatabaseObject::tablename(Purchased::$table); + + $this->loading = array( + 'where' => 'TRUE', + 'order'=>'bestselling'); + if (isset($options['where'])) $this->loading['where'] = $options['where']; + if (isset($options['show'])) $this->loading['limit'] = $options['show']; + if (isset($options['pagination'])) $this->loading['pagination'] = $options['pagination']; + } + +} + +class SearchResults extends Category { + static $_slug = "search-results"; + + function SearchResults ($options=array()) { + if (empty($options['search'])) $options['search'] = "(no search terms)"; + $this->name = __("Search Results for","Shopp")." "".stripslashes($options['search'])."""; + $this->slug = self::$_slug; + $this->uri = $this->slug; + $this->smart = true; + + $keywords = $options['search']; + + // Strip accents for search + $accents = array('á','à','â','ã','ª','Á','À', + 'Â','Ã', 'é','è','ê','É','È','Ê','í','ì','î','Í', + 'Ì','Î','ò','ó','ô', 'õ','º','Ó','Ò','Ô','Õ','ú', + 'ù','û','Ú','Ù','Û','ç','Ç','Ñ','ñ'); + $alt = array('a','a','a','a','a','A','A', + 'A','A','e','e','e','E','E','E','i','i','i','I','I', + 'I','o','o','o','o','o','O','O','O','O','u','u','u', + 'U','U','U','c','C','N','n'); + $keywords = trim(str_replace($accents, $alt, $keywords)); + + if (!defined('SHOPP_SEARCH_LOGIC')) define('SHOPP_SEARCH_LOGIC','OR'); + $logic = (strtoupper(SHOPP_SEARCH_LOGIC) == "AND")?"+":""; + + // Strip non alpha-numerics + $keywords = preg_replace('[^A-Za-z0-9\_\.\-]', '', $keywords); + $keywords = preg_replace('/(\s?)(\w+)\b(\s?)/','\1'.$logic.'\2*\3',$keywords); + + if (strlen($options['search']) > 0 && empty($keywords)) $keywords = $options['search']; + + $this->loading = array( + 'columns'=> "MATCH(p.name,p.summary,p.description) AGAINST ('$keywords' IN BOOLEAN MODE) AS score", + 'where'=>"MATCH(p.name,p.summary,p.description) AGAINST ('$keywords' IN BOOLEAN MODE)", + 'orderby'=>'score DESC'); + if (isset($options['show'])) $this->loading['limit'] = $options['show']; + } + +} + +class TagProducts extends Category { + static $_slug = "tag"; + + function TagProducts ($options=array()) { + $tagtable = DatabaseObject::tablename(Tag::$table); + $catalogtable = DatabaseObject::tablename(Catalog::$table); + + $this->tag = $options['tag']; + $tagquery = ""; + if (strpos($options['tag'],',') !== false) { + $tags = explode(",",$options['tag']); + foreach ($tags as $tag) + $tagquery .= empty($tagquery)?"tag.name='$tag'":" OR tag.name='$tag'"; + } else $tagquery = "tag.name='{$options['tag']}'"; + + $this->name = __("Products tagged","Shopp")." "".stripslashes($options['tag'])."""; + $this->slug = self::$_slug; + $this->uri = urlencode($options['tag']); + $this->smart = true; + $this->loading = array('where'=>"p.id in (SELECT product FROM $catalogtable AS catalog LEFT JOIN $tagtable AS tag ON catalog.tag=tag.id WHERE $tagquery)"); + if (isset($options['show'])) $this->loading['limit'] = $options['show']; + if (isset($options['pagination'])) $this->loading['pagination'] = $options['pagination']; + } + +} + +class RelatedProducts extends Category { + static $_slug = "related"; + + function RelatedProducts ($options=array()) { + global $Shopp; + $tagtable = DatabaseObject::tablename(Tag::$table); + $catalogtable = DatabaseObject::tablename(Catalog::$table); + + // Use the current product if available + if (!empty($Shopp->Product->id)) + $this->product = $Shopp->Product; + + // Or load a product specified + if (isset($options['product'])) { + if ($options['product'] == "recent-cartitem") // Use most recently added item in the cart + $this->product = new Product($Shopp->Cart->contents[$Shopp->Cart->data->added]->product); + elseif (preg_match('/^[\d+]$/',$options['product'])) // Load by specified id + $this->product = new Product($options['product']); + else $this->product = new Product($options['product'],'slug'); // Load by specified slug + } + + if (empty($this->product->id)) return false; + + // Load the product's tags if they are not available + if (empty($this->product->tags)) + $this->product->load_data(array('tags')); + + if (empty($this->product->tags)) return false; + + $tagscope = ""; + if (isset($options['tagged'])) { + $tagged = new Tag($options['tagged'],'name'); + + if (!empty($tagged->id)) { + $tagscope .= (empty($tagscope)?"":" OR ")."catalog.tag=$tagged->id"; + } + + } + + foreach ($this->product->tags as $tag) + if (!empty($tag->id)) + $tagscope .= (empty($tagscope)?"":" OR ")."catalog.tag=$tag->id"; + + $this->tag = "product-".$this->product->id; + $this->name = __("Products related to","Shopp")." "".stripslashes($this->product->name)."""; + $this->slug = self::$_slug; + $this->uri = urlencode($this->tag); + $this->smart = true; + $this->controls = false; + + $exclude = ""; + if (!empty($this->product->id)) $exclude = " AND p.id != {$this->product->id}"; + + $this->loading = array( + 'columns'=>'count(DISTINCT catalog.id)+SUM(IF('.$tagscope.',100,0)) AS score', + 'joins'=>"LEFT JOIN $catalogtable AS catalog ON catalog.product=p.id LEFT JOIN $tagtable AS t ON t.id=catalog.tag AND catalog.product=p.id", + 'where'=>"($tagscope) $exclude", + 'orderby'=>'score DESC' + ); + if (isset($options['show'])) $this->loading['limit'] = $options['show']; + if (isset($options['pagination'])) $this->loading['pagination'] = $options['pagination']; + if (isset($options['order'])) $this->loading['order'] = $options['order']; + if (isset($options['controls']) && value_is_true($options['controls'])) + unset($this->controls); + } + +} + +class RandomProducts extends Category { + static $_slug = "random"; + + function RandomProducts ($options=array()) { + $this->name = __("Random Products","Shopp"); + $this->slug = self::$_slug; + $this->uri = $this->slug; + $this->smart = true; + $this->loading = array('where'=>'true','order'=>'random'); + if (isset($options['exclude'])) { + $where = array(); + $excludes = explode(",",$options['exclude']); + if (in_array('featured',$excludes)) $where[] = "(p.featured='off')"; + if (in_array('onsale',$excludes)) $where[] = "(pd.sale='off' OR pr.discount=0)"; + $this->loading['where'] = join(" AND ",$where); + } + if (isset($options['columns'])) $this->loading['columns'] = $options['columns']; + if (isset($options['show'])) $this->loading['limit'] = $options['show']; + if (isset($options['pagination'])) $this->loading['pagination'] = $options['pagination']; + } + +} + + +?> \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/model/Customer.php b/blog/wp-content/plugins/shopp/core/model/Customer.php new file mode 100644 index 0000000..4c504b9 --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/model/Customer.php @@ -0,0 +1,725 @@ + "account", + "downloads" => "downloads", + "history" => "history", + "status" => "status", + "logout" => "logout", + ); + + function Customer ($id=false,$key=false) { + $this->init(self::$table); + if ($this->load($id,$key)) return true; + else return false; + } + + function management () { + global $Shopp; + + if (isset($_GET['acct'])) { + switch ($_GET['acct']) { + case "receipt": break; + case "history": $this->load_orders(); break; + case "downloads": $this->load_downloads(); break; + // case "logout": $Shopp->Cart->logout(); break; + } + } + + if (!empty($_POST['vieworder']) && !empty($_POST['purchaseid'])) { + + $Purchase = new Purchase($_POST['purchaseid']); + if ($Purchase->email == $_POST['email']) { + $Shopp->Cart->data->Purchase = $Purchase; + $Purchase->load_purchased(); + ob_start(); + include(SHOPP_TEMPLATES."/receipt.php"); + $content = ob_get_contents(); + ob_end_clean(); + return '
    '.$content.'
    '; + } + } + + if (!empty($_GET['acct']) && !empty($_GET['id'])) { + $Purchase = new Purchase($_GET['id']); + if ($Purchase->customer != $this->id) { + new ShoppError(sprintf(__('Order number %s could not be found in your order history.','Shopp'),$Purchase->id),'customer_order_history',SHOPP_AUTH_ERR); + unset($_GET['acct']); + return false; + } else { + $Shopp->Cart->data->Purchase = $Purchase; + $Purchase->load_purchased(); + ob_start(); + include(SHOPP_TEMPLATES."/receipt.php"); + $content = ob_get_contents(); + ob_end_clean(); + } + $management = apply_filters('shopp_account_management_url', + '

    « Return to Account Management

    '); + + echo '
    '.$management.$content.$management.'
    '; + return false; + } + + + if (!empty($_POST['customer'])) { + $this->updates($_POST); + if ($_POST['password'] == $_POST['confirm-password']) + $this->password = wp_hash_password($_POST['password']); + $this->save(); + } + + } + + function recovery () { + global $Shopp; + $authentication = $Shopp->Settings->get('account_system'); + $errors = array(); + + // Check email or login supplied + if (empty($_POST['account-login'])) { + if ($authentication == "wordpress") $errors[] = new ShoppError(__('Enter an email address or login name','Shopp')); + else $errors[] = new ShoppError(__('Enter an email address','Shopp')); + } else { + // Check that the account exists + if (strpos($_POST['account-login'],'@') !== false) { + $RecoveryCustomer = new Customer($_POST['account-login'],'email'); + if (!$RecoveryCustomer->id) + $errors[] = new ShoppError(__('There is no user registered with that email address.','Shopp'),'password_recover_noaccount',SHOPP_AUTH_ERR); + } else { + $user_data = get_userdatabylogin($_POST['account-login']); + $RecoveryCustomer = new Customer($user_data->ID,'wpuser'); + if (empty($RecoveryCustomer->id)) + $errors[] = new ShoppError(__('There is no user registered with that login name.','Shopp'),'password_recover_noaccount',SHOPP_AUTH_ERR); + } + } + + // return errors + if (!empty($errors)) return; + + // Generate new key + $RecoveryCustomer->activation = wp_generate_password(20, false); + do_action_ref_array('shopp_generate_password_key', array(&$RecoveryCustomer)); + $RecoveryCustomer->save(); + + $subject = apply_filters('shopp_recover_password_subject', sprintf(__('[%s] Password Recovery Request','Shopp'),get_option('blogname'))); + + $_ = array(); + $_[] = 'From: "'.get_option('blogname').'" <'.$Shopp->Settings->get('merchant_email').'>'; + $_[] = 'To: '.$RecoveryCustomer->email; + $_[] = 'Subject: '.$subject; + $_[] = ''; + $_[] = __('A request has beem made to reset the password for the following site and account:','Shopp'); + $_[] = get_option('siteurl'); + $_[] = ''; + if (isset($_POST['email-login'])) + $_[] = sprintf(__('Email: %s','Shopp'), $RecoveryCustomer->email); + if (isset($_POST['loginname-login'])) + $_[] = sprintf(__('Login name: %s','Shopp'), $user_data->user_login); + $_[] = ''; + $_[] = __('To reset your password visit the following address, otherwise just ignore this email and nothing will happen.'); + $_[] = ''; + $_[] = add_query_arg(array('acct'=>'rp','key'=>$RecoveryCustomer->activation),$Shopp->link('account')); + $message = apply_filters('shopp_recover_password_message',join("\r\n",$_)); + + if (!shopp_email($message)) { + new ShoppError(__('The e-mail could not be sent.'),'password_recovery_email',SHOPP_ERR); + shopp_redirect(add_query_arg('acct','recover',$Shopp->link('account'))); + } else { + new ShoppError(__('Check your email address for instructions on resetting the password for your account.','Shopp'),'password_recovery_email',SHOPP_ERR); + } + + } + + function reset_password ($activation) { + global $Shopp; + $authentication = $Shopp->Settings->get('account_system'); + + $user_data = false; + $activation = preg_replace('/[^a-z0-9]/i', '', $activation); + + $errors = array(); + if (empty($activation) || !is_string($activation)) + $errors[] = new ShoppError(__('Invalid key')); + + $RecoveryCustomer = new Customer($activation,'activation'); + if (empty($RecoveryCustomer->id)) + $errors[] = new ShoppError(__('Invalid key')); + + if (!empty($errors)) return false; + + // Generate a new random password + $password = wp_generate_password(); + + do_action_ref_array('password_reset', array(&$RecoveryCustomer,$password)); + + $RecoveryCustomer->password = wp_hash_password($password); + if ($authentication == "wordpress") { + $user_data = get_userdata($RecoveryCustomer->wpuser); + wp_set_password($password, $user_data->ID); + } + + $RecoveryCustomer->activation = ''; + $RecoveryCustomer->save(); + + $subject = apply_filters('shopp_reset_password_subject', sprintf(__('[%s] New Password','Shopp'),get_option('blogname'))); + + $_ = array(); + $_[] = 'From: "'.get_option('blogname').'" <'.$Shopp->Settings->get('merchant_email').'>'; + $_[] = 'To: '.$RecoveryCustomer->email; + $_[] = 'Subject: '.$subject; + $_[] = ''; + $_[] = sprintf(__('Your new password for %s:','Shopp'),get_option('siteurl')); + $_[] = ''; + if ($user_data) + $_[] = sprintf(__('Login name: %s','Shopp'), $user_data->user_login); + $_[] = sprintf(__('Password: %s'), $password) . "\r\n"; + $_[] = ''; + $_[] = __('Click here to login:').' '.$Shopp->link('account'); + $message = apply_filters('shopp_reset_password_message',join("\r\n",$_)); + + if (!shopp_email($message)) { + new ShoppError(__('The e-mail could not be sent.'),'password_reset_email',SHOPP_ERR); + shopp_redirect(add_query_arg('acct','recover',$Shopp->link('account'))); + } else { + new ShoppError(__('Check your email address for your new password.','Shopp'),'password_reset_email',SHOPP_ERR); + } + unset($_GET['acct']); + } + + function load_downloads () { + if (empty($this->id)) return false; + $db =& DB::get(); + $orders = DatabaseObject::tablename(Purchase::$table); + $purchases = DatabaseObject::tablename(Purchased::$table); + $pricing = DatabaseObject::tablename(Price::$table); + $asset = DatabaseObject::tablename(Asset::$table); + $query = "SELECT p.*,f.name as filename,f.size,f.properties FROM $purchases AS p LEFT JOIN $orders AS o ON o.id=p.purchase LEFT JOIN $asset AS f ON f.parent=p.price WHERE o.customer=$this->id AND f.size > 0"; + $this->downloads = $db->query($query,AS_ARRAY); + + } + + function load_orders ($filters=array()) { + if (empty($this->id)) return false; + global $Shopp; + $db =& DB::get(); + + $where = ''; + if (isset($filters['where'])) $where = " AND {$filters['where']}"; + $orders = DatabaseObject::tablename(Purchase::$table); + $purchases = DatabaseObject::tablename(Purchased::$table); + $query = "SELECT o.* FROM $orders AS o LEFT JOIN $purchases AS p ON p.purchase=o.id WHERE o.customer=$this->id $where ORDER BY created DESC"; + $Shopp->purchases = $db->query($query,AS_ARRAY); + foreach($Shopp->purchases as &$p) { + $Purchase = new Purchase(); + $Purchase->updates($p); + $p = $Purchase; + } + } + + function new_wpuser () { + global $Shopp; + require_once(ABSPATH."/wp-includes/registration.php"); + if (empty($this->login)) return false; + if (username_exists($this->login)){ + new ShoppError(__('The login name you provided is already in use. Please choose another login name.','Shopp'),'login_exists',SHOPP_ERR); + return false; + } + if (empty($this->password)) $this->password = wp_generate_password(12,true); + + // Create the WordPress account + $wpuser = wp_insert_user(array( + 'user_login' => $this->login, + 'user_pass' => $this->password, + 'user_email' => $this->email, + 'display_name' => $this->firstname.' '.$this->Customer->lastname, + 'nickname' => $handle, + 'first_name' => $this->firstname, + 'last_name' => $this->lastname + )); + if (!$wpuser) return false; + + // Link the WP user ID to this customer record + $this->wpuser = $wpuser; + + // Send email notification of the new account + wp_new_user_notification( $wpuser, $this->password ); + $this->password = ""; + if (SHOPP_DEBUG) new ShoppError('Successfully created the WordPress user for the Shopp account.',false,SHOPP_DEBUG_ERR); + return true; + } + + function exportcolumns () { + $prefix = "c."; + return array( + $prefix.'firstname' => __('Customer\'s First Name','Shopp'), + $prefix.'lastname' => __('Customer\'s Last Name','Shopp'), + $prefix.'email' => __('Customer\'s Email Address','Shopp'), + $prefix.'phone' => __('Customer\'s Phone Number','Shopp'), + $prefix.'company' => __('Customer\'s Company','Shopp'), + $prefix.'info' => __('Customer\'s Custom Information','Shopp'), + $prefix.'created' => __('Customer Created Date','Shopp'), + $prefix.'modified' => __('Customer Last Updated Date','Shopp'), + ); + } + + function tag ($property,$options=array()) { + global $Shopp; + + $menus = array( + "account" => __("My Account","Shopp"), + "downloads" => __("Downloads","Shopp"), + "history" => __("Order History","Shopp"), + "status" => __("Order Status","Shopp"), + "logout" => __("Logout","Shopp") + ); + + // Return strings with no options + switch ($property) { + case "url": return $Shopp->link('account'); + case "recover-url": return add_query_arg('acct','recover',$Shopp->link('account')); + case "process": + if (isset($_GET['acct'])) return $_GET['acct']; + return false; + + case "loggedin": return $Shopp->Cart->data->login; break; + case "notloggedin": return (!$Shopp->Cart->data->login && $Shopp->Settings->get('account_system') != "none"); break; + case "login-label": + $accounts = $Shopp->Settings->get('account_system'); + $label = __('Email Address','Shopp'); + if ($accounts == "wordpress") $label = __('Login Name','Shopp'); + if (isset($options['label'])) $label = $options['label']; + return $label; + break; + case "email-login": + case "loginname-login": + case "account-login": + if (!empty($_POST['account-login'])) + $options['value'] = $_POST['account-login']; + return ''; + break; + case "password-login": + if (!empty($_POST['password-login'])) + $options['value'] = $_POST['password-login']; + return ''; + break; + case "recover-button": + if (!isset($options['value'])) $options['value'] = __('Get New Password','Shopp'); + return ''; + break; + case "submit-login": // Deprecating + case "login-button": + if (!isset($options['value'])) $options['value'] = __('Login','Shopp'); + if (is_shopp_page('account')) + $string = ''; + else $string = ''; + $string .= ''; + return $string; + break; + case "errors-exist": + $Errors =& ShoppErrors(); + return ($Errors->exist(SHOPP_AUTH_ERR)); + break; + case "login-errors": + $Errors =& ShoppErrors(); + $result = ""; + if (!$Errors->exist(SHOPP_AUTH_ERR)) return false; + $errors = $Errors->get(SHOPP_AUTH_ERR); + foreach ((array)$errors as $error) + if (!empty($error)) $result .= '

    '.$error->message().'

    '; + $Errors->reset(); + return $result; + break; + + case "menu": + if (!$this->looping) { + reset($this->management); + $this->looping = true; + } else next($this->management); + + if (current($this->management)) return true; + else { + $this->looping = false; + reset($this->management); + return false; + } + break; + case "management": + if (array_key_exists('url',$options)) return add_query_arg('acct',key($this->management),$Shopp->link('account')); + if (array_key_exists('action',$options)) return key($this->management); + return $menus[key($this->management)]; + case "accounts": return $Shopp->Settings->get('account_system'); break; + case "order-lookup": + $auth = $Shopp->Settings->get('account_system'); + if ($auth != "none") return true; + + if (!empty($_POST['vieworder']) && !empty($_POST['purchaseid'])) { + require_once("Purchase.php"); + $Purchase = new Purchase($_POST['purchaseid']); + if ($Purchase->email == $_POST['email']) { + $Shopp->Cart->data->Purchase = $Purchase; + $Purchase->load_purchased(); + ob_start(); + include(SHOPP_TEMPLATES."/receipt.php"); + $content = ob_get_contents(); + ob_end_clean(); + return '
    '.$content.'
    '; + } + } + + ob_start(); + include(SHOPP_ADMINPATH."/orders/account.php"); + $content = ob_get_contents(); + ob_end_clean(); + return '
    '.$content.'
    '; + break; + + case "firstname": + if ($options['mode'] == "value") return $this->firstname; + if (!empty($this->firstname)) + $options['value'] = $this->firstname; + return ''; + break; + case "lastname": + if ($options['mode'] == "value") return $this->lastname; + if (!empty($this->lastname)) + $options['value'] = $this->lastname; + return ''; + break; + case "company": + if ($options['mode'] == "value") return $this->company; + if (!empty($this->company)) + $options['value'] = $this->company; + return ''; + break; + case "email": + if ($options['mode'] == "value") return $this->email; + if (!empty($this->email)) + $options['value'] = $this->email; + return ''; + break; + case "loginname": + if ($options['mode'] == "value") return $this->loginname; + if (!empty($this->login)) + $options['value'] = $this->login; + return ''; + break; + case "password": + if ($options['mode'] == "value") + return strlen($this->password) == 34?str_pad('•',8):$this->password; + if (!empty($this->password)) + $options['value'] = $this->password; + return ''; + break; + case "confirm-password": + if (!empty($this->confirm_password)) + $options['value'] = $this->confirm_password; + return ''; + break; + case "phone": + if ($options['mode'] == "value") return $this->phone; + if (!empty($this->phone)) + $options['value'] = $this->phone; + return ''; + break; + case "hasinfo": + case "has-info": + if (empty($this->info)) return false; + if (!$this->looping) { + reset($this->info); + $this->looping = true; + } else next($this->info); + + if (current($this->info)) return true; + else { + $this->looping = false; + reset($this->info); + return false; + } + break; + case "info": + $info = current($this->info); + $name = key($this->info); + $allowed_types = array("text","password","hidden","checkbox","radio"); + if (empty($options['type'])) $options['type'] = "hidden"; + if (in_array($options['type'],$allowed_types)) { + if ($options['mode'] == "name") return $name; + if ($options['mode'] == "value") return $info; + $options['value'] = $info; + return ''; + } + break; + case "save-button": + if (!isset($options['label'])) $options['label'] = __('Save','Shopp'); + $result = ''; + $result .= ''; + return $result; + break; + + + // Downloads UI tags + case "hasdownloads": + case "has-downloads": return (!empty($this->downloads)); break; + case "downloads": + if (empty($this->downloads)) return false; + if (!$this->looping) { + reset($this->downloads); + $this->looping = true; + } else next($this->downloads); + + if (current($this->downloads)) return true; + else { + $this->looping = false; + reset($this->downloads); + return false; + } + break; + case "download": + $download = current($this->downloads); + $df = get_option('date_format'); + $properties = unserialize($download->properties); + $string = ''; + if (array_key_exists('id',$options)) $string .= $download->download; + if (array_key_exists('purchase',$options)) $string .= $download->purchase; + if (array_key_exists('name',$options)) $string .= $download->name; + if (array_key_exists('variation',$options)) $string .= $download->optionlabel; + if (array_key_exists('downloads',$options)) $string .= $download->downloads; + if (array_key_exists('key',$options)) $string .= $download->dkey; + if (array_key_exists('created',$options)) $string .= $download->created; + if (array_key_exists('total',$options)) $string .= money($download->total); + if (array_key_exists('filetype',$options)) $string .= $properties['mimetype']; + if (array_key_exists('size',$options)) $string .= readableFileSize($download->size); + if (array_key_exists('date',$options)) $string .= _d($df,mktimestamp($download->created)); + if (array_key_exists('url',$options)) $string .= (SHOPP_PERMALINKS) ? + $Shopp->shopuri."download/".$download->dkey : + add_query_arg('shopp_download',$download->dkey,$Shopp->link('account')); + + return $string; + break; + + // Downloads UI tags + case "haspurchases": + case "has-purchases": + $filters = array(); + if (isset($options['daysago'])) + $filters['where'] = "UNIX_TIMESTAMP(o.created) > UNIX_TIMESTAMP()-".($options['daysago']*86400); + if (empty($Shopp->purchases)) $this->load_orders($filters); + return (!empty($Shopp->purchases)); + break; + case "purchases": + if (!$this->looping) { + reset($Shopp->purchases); + $Shopp->Cart->data->Purchase = current($Shopp->purchases); + $this->looping = true; + } else { + $Shopp->Cart->data->Purchase = next($Shopp->purchases); + } + + if (current($Shopp->purchases)) { + $Shopp->Cart->data->Purchase = current($Shopp->purchases); + return true; + } + else { + $this->looping = false; + return false; + } + break; + case "receipt": + return add_query_arg( + array( + 'acct'=>'receipt', + 'id'=>$Shopp->Cart->data->Purchase->id), + $Shopp->link('account')); + + } + } + +} // end Customer class + +class CustomersExport { + var $sitename = ""; + var $headings = false; + var $data = false; + var $defined = array(); + var $customer_cols = array(); + var $billing_cols = array(); + var $shipping_cols = array(); + var $selected = array(); + var $recordstart = true; + var $content_type = "text/plain"; + var $extension = "txt"; + + function CustomersExport () { + global $Shopp; + + $this->customer_cols = Customer::exportcolumns(); + $this->billing_cols = Billing::exportcolumns(); + $this->shipping_cols = Shipping::exportcolumns(); + $this->defined = array_merge($this->customer_cols,$this->billing_cols,$this->shipping_cols); + + $this->sitename = get_bloginfo('name'); + $this->headings = ($Shopp->Settings->get('customerexport_headers') == "on"); + $this->selected = $Shopp->Settings->get('customerexport_columns'); + $Shopp->Settings->save('customerexport_lastexport',mktime()); + } + + function query ($request=array()) { + $db =& DB::get(); + if (empty($request)) $request = $_GET; + + if (!empty($request['start'])) { + list($month,$day,$year) = explode("/",$request['start']); + $starts = mktime(0,0,0,$month,$day,$year); + } + + if (!empty($request['end'])) { + list($month,$day,$year) = explode("/",$request['end']); + $ends = mktime(0,0,0,$month,$day,$year); + } + + $where = "WHERE c.id IS NOT NULL "; + if (isset($request['s']) && !empty($request['s'])) $where .= " AND (id='{$request['s']}' OR firstname LIKE '%{$request['s']}%' OR lastname LIKE '%{$request['s']}%' OR CONCAT(firstname,' ',lastname) LIKE '%{$request['s']}%' OR transactionid LIKE '%{$request['s']}%')"; + if (!empty($request['start']) && !empty($request['end'])) $where .= " AND (UNIX_TIMESTAMP(c.created) >= $starts AND UNIX_TIMESTAMP(c.created) <= $ends)"; + + $customer_table = DatabaseObject::tablename(Customer::$table); + $billing_table = DatabaseObject::tablename(Billing::$table); + $shipping_table = DatabaseObject::tablename(Shipping::$table); + + $c = 0; $columns = array(); + foreach ($this->selected as $column) $columns[] = "$column AS col".$c++; + $query = "SELECT ".join(",",$columns)." FROM $customer_table AS c LEFT JOIN $billing_table AS b ON c.id=b.customer LEFT JOIN $shipping_table AS s ON c.id=s.customer $where ORDER BY c.created ASC"; + $this->data = $db->query($query,AS_ARRAY); + } + + // Implement for exporting all the data + function output () { + if (!$this->data) $this->query(); + if (!$this->data) return false; + header("Content-type: $this->content_type; charset=UTF-8"); + header("Content-Disposition: attachment; filename=\"$this->sitename Customer Export.$this->extension\""); + header("Content-Description: Delivered by WordPress/Shopp ".SHOPP_VERSION); + header("Cache-Control: maxage=1"); + header("Pragma: public"); + + $this->begin(); + if ($this->headings) $this->heading(); + $this->records(); + $this->end(); + } + + function begin() {} + + function heading () { + foreach ($this->selected as $name) + $this->export($this->defined[$name]); + $this->record(); + } + + function records () { + foreach ($this->data as $key => $record) { + foreach(get_object_vars($record) as $column) + $this->export($this->parse($column)); + $this->record(); + } + } + + function parse ($column) { + if (preg_match("/^[sibNaO](?:\:.+?\{.*\}$|\:.+;$|;$)/",$column)) { + $list = unserialize($column); + $column = ""; + foreach ($list as $name => $value) + $column .= (empty($column)?"":";")."$name:$value"; + } + return $column; + } + + function end() {} + + // Implement for exporting a single value + function export ($value) { + echo ($this->recordstart?"":"\t").$value; + $this->recordstart = false; + } + + function record () { + echo "\n"; + $this->recordstart = true; + } + +} + +class CustomersTabExport extends CustomersExport { + function CustomersTabExport () { + parent::CustomersExport(); + $this->output(); + } +} + +class CustomersCSVExport extends CustomersExport { + function CustomersCSVExport () { + parent::CustomersExport(); + $this->content_type = "text/csv"; + $this->extension = "csv"; + $this->output(); + } + + function export ($value) { + $value = str_replace('"','""',$value); + if (preg_match('/^\s|[,"\n\r]|\s$/',$value)) $value = '"'.$value.'"'; + echo ($this->recordstart?"":",").$value; + $this->recordstart = false; + } + +} + +class CustomersXLSExport extends CustomersExport { + function CustomersXLSExport () { + parent::CustomersExport(); + $this->content_type = "application/vnd.ms-excel"; + $this->extension = "xls"; + $this->c = 0; $this->r = 0; + $this->output(); + } + + function begin () { + echo pack("ssssss", 0x809, 0x8, 0x0, 0x10, 0x0, 0x0); + } + + function end () { + echo pack("ss", 0x0A, 0x00); + } + + function export ($value) { + if (preg_match('/^[\d\.]+$/',$value)) { + echo pack("sssss", 0x203, 14, $this->r, $this->c, 0x0); + echo pack("d", $value); + } else { + $l = strlen($value); + echo pack("ssssss", 0x204, 8+$l, $this->r, $this->c, 0x0, $l); + echo $value; + } + $this->c++; + } + + function record () { + $this->c = 0; + $this->r++; + } +} + +?> \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/model/Error.php b/blog/wp-content/plugins/shopp/core/model/Error.php new file mode 100644 index 0000000..0c9b811 --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/model/Error.php @@ -0,0 +1,273 @@ +notifications = new CallbackSubscription(); + + $types = E_ALL ^ E_NOTICE; + if (defined('WP_DEBUG') && WP_DEBUG) $types = E_ALL; + // Handle PHP errors + if (SHOPP_ERROR_REPORTING >= SHOPP_PHP_ERR) + set_error_handler(array($this,'phperror'),$types); + } + + function add ($ShoppError) { + $this->errors[$ShoppError->source] = $ShoppError; + $this->notifications->send($ShoppError); + } + + function get ($level=SHOPP_DEBUG_ERR) { + $errors = array(); + foreach ($this->errors as &$error) + if ($error->level <= $level) $errors[] = &$error; + return $errors; + } + + function exist ($level=SHOPP_DEBUG_ERR) { + $errors = array(); + foreach ($this->errors as &$error) + if ($error->level <= $level) $errors[] = &$error; + return (count($errors) > 0); + } + + function reset () { + $this->errors = array(); + } + + function phperror ($number, $message, $file, $line) { + if (strpos($file,SHOPP_PATH) !== false) + new ShoppError($message,'php_error',SHOPP_PHP_ERR, + array('file'=>$file,'line'=>$line,'phperror'=>$number)); + } + + /* Provides functionality for shopp('error') tags */ + function tag ($property,$options=array()) { + global $Shopp; + if (empty($options)) return false; + switch ($property) { + case "trxn": new ShoppError(key($options),'template_error',SHOPP_TRXN_ERR); break; + case "auth": new ShoppError(key($options),'template_error',SHOPP_AUTH_ERR); break; + case "addon": new ShoppError(key($options),'template_error',SHOPP_ADDON_ERR); break; + case "comm": new ShoppError(key($options),'template_error',SHOPP_COMM_ERR); break; + case "stock": new ShoppError(key($options),'template_error',SHOPP_STOCK_ERR); break; + case "admin": new ShoppError(key($options),'template_error',SHOPP_ADMIN_ERR); break; + case "db": new ShoppError(key($options),'template_error',SHOPP_DB_ERR); break; + case "debug": new ShoppError(key($options),'template_error',SHOPP_DEBUG_ERR); break; + default: new ShoppError(key($options),'template_error',SHOPP_ERR); break; + } + } + +} + +class ShoppError { + + var $code; + var $source; + var $messages; + var $level; + var $data = array(); + var $php = array( + E_ERROR => 'ERROR', + E_WARNING => 'WARNING', + E_PARSE => 'PARSE ERROR', + E_NOTICE => 'NOTICE', + E_CORE_ERROR => 'CORE ERROR', + E_CORE_WARNING => 'CORE WARNING', + E_COMPILE_ERROR => 'COMPILE ERROR', + E_COMPILE_WARNING => 'COMPILE WARNING', + E_USER_ERROR => 'USER ERROR', + E_USER_WARNING => 'USER WARNING', + E_USER_NOTICE => 'USER NOTICE', + E_STRICT => 'STRICT NOTICE', + E_RECOVERABLE_ERROR => 'RECOVERABLE ERROR' + ); + + function ShoppError($message='',$code='',$level=SHOPP_ERR,$data='') { + if ($level > SHOPP_ERROR_REPORTING) return; + if (empty($message)) return; + + $debug = debug_backtrace(); + + $this->code = $code; + $this->messages[] = $message; + $this->level = $level; + $this->data = $data; + $this->debug = $debug[1]; + + // Handle template errors + if (isset($this->debug['class']) && $this->debug['class'] == "ShoppErrors") + $this->debug = $debug[2]; + + if (isset($data['file'])) $this->debug['file'] = $data['file']; + if (isset($data['line'])) $this->debug['line'] = $data['line']; + unset($this->debug['object'],$this->debug['args']); + + $this->source = "Shopp"; + if (isset($this->debug['class'])) $this->source = $this->debug['class']; + if (isset($this->data['phperror'])) $this->source = "PHP ".$this->php[$this->data['phperror']]; + + $Errors = &ShoppErrors(); + if (!empty($Errors)) $Errors->add($this); + } + + function message ($delimiter="\n") { + $string = ""; + // Show source if debug is on, or not a general error message + if (((defined('WP_DEBUG') && WP_DEBUG) || $this->level > SHOPP_ERR) && + !empty($this->source)) $string .= "$this->source: "; + $string .= join($delimiter,$this->messages); + return $string; + } + +} + +class ShoppErrorLogging { + var $dir; + var $file = "shopp_errors.log"; + var $logfile; + var $log; + var $loglevel = 0; + + function ShoppErrorLogging ($loglevel=0) { + $this->loglevel = $loglevel; + $this->dir = defined('SHOPP_TEMP_PATH') ? SHOPP_TEMP_PATH : sys_get_temp_dir(); + $this->dir = str_replace('\\', '/', $this->dir); //Windows path sanitiation + $sitename = sanitize_title_with_dashes(get_bloginfo('sitename')); + $this->logfile = trailingslashit($this->dir).$sitename."-".$this->file; + + $Errors = &ShoppErrors(); + $Errors->notifications->subscribe($this,'log'); + } + + function log (&$error) { + if ($error->level > $this->loglevel) return; + $debug = ""; + if (isset($error->debug['file'])) $debug = " [".basename($error->debug['file']).", line ".$error->debug['line']."]"; + $message = date("Y-m-d H:i:s",mktime())." - ".$error->message().$debug."\n"; + if ($this->log = @fopen($this->logfile,'at')) { + fwrite($this->log,$message); + fclose($this->log); + } + } + + function reset () { + $this->log = fopen($this->logfile,'w'); + fwrite($this->log,''); + fclose($this->log); + } + + function tail($lines=100) { + if (!file_exists($this->logfile)) return; + $f = fopen($this->logfile, "r"); + $c = $lines; + $pos = -2; + $beginning = false; + $text = array(); + while ($c > 0) { + $t = ""; + while ($t != "\n") { + if(fseek($f, $pos, SEEK_END) == -1) { $beginning = true; break; } + $t = fgetc($f); + $pos--; + } + $c--; + if($beginning) rewind($f); + $text[$lines-$c-1] = fgets($f); + if($beginning) break; + } + fclose($f); + return array_reverse($text); + } + +} + +class ShoppErrorNotification { + + var $recipients; + var $types=0; + + function ShoppErrorNotification ($recipients='',$types=array()) { + if (empty($recipients)) return; + $this->recipients = $recipients; + foreach ((array)$types as $type) $this->types += $type; + $Errors = &ShoppErrors(); + $Errors->notifications->subscribe($this,'notify'); + } + + function notify (&$error) { + if (!($error->level & $this->types)) return; + $url = parse_url(get_bloginfo('url')); + $_ = array(); + $_[] = 'From: "'.get_bloginfo('sitename').'" '; + $_[] = 'To: '.$this->recipients; + $_[] = 'Subject: '.__('Shopp Notification','Shopp'); + $_[] = ''; + $_[] = __('This is an automated message notification generated when the Shopp installation at '.get_bloginfo('url').' encountered the following:','Shopp'); + $_[] = ''; + $_[] = $error->message(); + $_[] = ''; + if (isset($error->debug['file']) && defined('WP_DEBUG')) + $_[] = 'DEBUG: '.basename($error->debug['file']).', line '.$error->debug['line'].''; + + shopp_email(join("\r\n",$_)); + } + +} + +class CallbackSubscription { + + var $subscribers = array(); + + function subscribe ($target,$method) { + if (!isset($this->subscribers[get_class($target)])) + $this->subscribers[get_class($target)] = array(&$target,$method); + } + + function send () { + $args = func_get_args(); + foreach ($this->subscribers as $callback) { + if (version_compare(PHP_VERSION, '5.3.0') === -1) call_user_func_array($callback,$args); + else call_user_func_array($callback,&$args); + } + } + +} + +function &ShoppErrors () { + global $Shopp; + return $Shopp->Cart->data->Errors; +} + +function is_shopperror ($e) { + return (get_class($e) == "ShoppError"); +} + +?> \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/model/Gateway.php b/blog/wp-content/plugins/shopp/core/model/Gateway.php new file mode 100644 index 0000000..aa1542a --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/model/Gateway.php @@ -0,0 +1,132 @@ +classname = get_class($this); + $this->uid = strtolower($this->classname)."-settings"; + $this->settings = $Shopp->Settings->get($this->classname); + $this->settings['merchant_email'] = $Shopp->Settings->get('merchant_email'); + if (!isset($this->settings['cards'])) $this->settings['cards'] = $this->cards; + + // $this->file = __FILE__; + // if (!empty($Order)) $this->build($Order); + // return true; + } + + function build ($Order) { + $_ = array(); + $this->transaction = join("",$_); + } + + function process () { + $this->Response = $this->send(); + $status = $this->Response; // Get the response status + + if ($status == "APPROVED") return true; + else return false; + } + + function transactionid () { + $transaction = $this->Response->transaction; + if (!empty($transaction)) return $transaction; + return false; + } + + function error () { + if (empty($this->Response)) return false; + $message = __('Gateway response error.','Shopp'); + if (class_exists('ShoppError')) { + if (empty($message)) return new ShoppError(__("An unknown error occurred while processing this transaction. Please contact the site administrator.","Shopp"),'gateway_trxn_error',SHOPP_TRXN_ERR); + return new ShoppError($message,'gateway_trxn_error',SHOPP_TRXN_ERR); + } else { + $Error = new stdClass(); + $Error->code = 'gateway_trxn_error'; + $Error->message = $message; + return $Error; + } + } + + function send () { + $connection = curl_init(); + curl_setopt($connection,CURLOPT_URL,$this->url); + curl_setopt($connection, CURLOPT_SSL_VERIFYPEER, 0); + curl_setopt($connection, CURLOPT_SSL_VERIFYHOST, 0); + curl_setopt($connection, CURLOPT_NOPROGRESS, 1); + curl_setopt($connection, CURLOPT_VERBOSE, 1); + curl_setopt($connection, CURLOPT_FOLLOWLOCATION,0); + curl_setopt($connection, CURLOPT_POST, 1); + curl_setopt($connection, CURLOPT_POSTFIELDS, $this->transaction); + curl_setopt($connection, CURLOPT_TIMEOUT, 30); + curl_setopt($connection, CURLOPT_USERAGENT, SHOPP_GATEWAY_USERAGENT); + curl_setopt($connection, CURLOPT_REFERER, "https://".$_SERVER['SERVER_NAME']); + curl_setopt($connection, CURLOPT_RETURNTRANSFER, 1); + $buffer = curl_exec($connection); + if ($error = curl_error($connection)) { + if (class_exists('ShoppError')) new ShoppError($error,'gateway_connection',SHOPP_COMM_ERR); + } + curl_close($connection); + + $this->Response = $this->response($buffer); + return $this->Response; + } + + function response ($string) { + return $string; + } + + + function settings () { + global $Shopp; + ?> + + Gateway Name + +

    +

    +
    settings['testmode'] == "on")?' checked="checked"':''; ?> />
    +
    Accept these cards: +
      cards as $id => $card): + $checked = ""; + if (in_array($card,$this->settings['cards'])) $checked = ' checked="checked"'; + ?> +
    • />
    • +
    + + + + + + gatewayHandlers.register('file); ?>', + 'uid; ?>-settings'); \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/model/Image.php b/blog/wp-content/plugins/shopp/core/model/Image.php new file mode 100644 index 0000000..801fc1a --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/model/Image.php @@ -0,0 +1,260 @@ +src = new StdClass(); + $this->src->width = $width; + $this->src->height = $height; + $this->src->image = imagecreatefromstring($data); + } + + /** + * scaleToWidth() + * Determine the scale percentage by width of the image, + * height is variable to maintain image proportions + **/ + function scaleToWidth($width) { + $scale = $width / $this->src->width; + + $this->Processed = new StdClass(); + $this->Processed->width = $width; + $this->Processed->height = ceil($this->src->height * $scale); + + $this->Processed->image = ImageCreateTrueColor($this->Processed->width,$this->Processed->height); + ImageCopyResampled($this->Processed->image, $this->src->image, + 0, 0, 0, 0, + $this->Processed->width, $this->Processed->height, $this->src->width, $this->src->height); + + } + + /** + * scaleToHeight() + * Determine the scale percentage by height of the image, + * width is variable to maintain image proportions + */ + function scaleToHeight($height) { + $scale = $height / $this->src->height; + + $this->Processed = new StdClass(); + $this->Processed->height = $height; + $this->Processed->width = ceil($this->src->width * $scale); + + $this->Processed->image = ImageCreateTrueColor($this->Processed->width,$this->Processed->height); + ImageCopyResampled($this->Processed->image, $this->src->image, + 0, 0, 0, 0, + $this->Processed->width, $this->Processed->height, $this->src->width, $this->src->height); + + } + + /** + * scaleToFit() + * Resize the image directly to the provided dimensions, + * do not maintain image proportions + */ + function scaleToFit($width,$height) { + + $this->Processed = new StdClass(); + + if ($this->src->width > $this->src->height) { // Scale to width + $scale = $width / $this->src->width; + $this->Processed->width = $width; + $this->Processed->height = ceil($this->src->height * $scale); + } else { // Scale to height + $scale = $height / $this->src->height; + $this->Processed->height = $height; + $this->Processed->width = ceil($this->src->width * $scale); + } + + $this->Processed->image = ImageCreateTrueColor($this->Processed->width,$this->Processed->height); + ImageCopyResampled($this->Processed->image, $this->src->image, + 0, 0, 0, 0, + $this->Processed->width, $this->Processed->height, $this->src->width, $this->src->height); + + } + + /** + * scaleCrop() + * Scale based on the smallest dimension, + * cropping the extra on the other dimension to + * maintain image proportion and fit the provided + * dimensions exactly + */ + function scaleCrop($width,$height) { + $this->Processed = new StdClass(); + $this->Processed->width = $width; + $this->Processed->height = $height; + + $widthScale = $width / $this->src->width; + $heightScale = $height / $this->src->height; + + $this->Processed->image = ImageCreateTrueColor($this->Processed->width,$this->Processed->height); + if ($heightScale > $widthScale) { + $scale = $height / $this->src->height; // Scale by height + $width = ceil($this->src->width * $scale); // Determine proportional width + $x = ($width - $this->Processed->width)*-0.5; // Center scaled image on the canvas + ImageCopyResampled($this->Processed->image, $this->src->image, + $x, 0, 0, 0, + $width, $this->Processed->height, $this->src->width, $this->src->height); + } else { + $scale = $width / $this->src->width; // Scale by width + $height = ceil($this->src->height * $scale); // Determine proportional height + $y = ($height - $this->Processed->height)*-0.5; // Center scaled image on the canvas + ImageCopyResampled($this->Processed->image, $this->src->image, + 0, $y, 0, 0, + $this->Processed->width, $height, $this->src->width, $this->src->height); + } + + } + + /** + * Return the processed image + */ + function imagefile ($quality=80) { + if (!isset($this->Processed->image)) return false; + imageinterlace($this->Processed->image, true); // For progressive loading + ob_start(); // Start capturing output buffer stream + imagejpeg($this->Processed->image,NULL,$quality); // Output the image to the stream + $buffer = ob_get_contents(); // Get the bugger + ob_end_clean(); // Clear the buffer + return $buffer; // Send it back + } + + /** + * UnsharpMask () + * version 2.1.1 + * Unsharp mask algorithm by Torstein Hansi , July 2003 + **/ + function UnsharpMask ($amount=50, $radius=0.5, $threshold=3) { + if (!isset($this->Processed->image)) return false; + $image = $this->Processed->image; + + // Attempt to calibrate the parameters to Photoshop + if ($amount > 500) $amount = 500; + $amount = $amount * 0.016; + if ($radius > 50) $radius = 50; + $radius = $radius * 2; + if ($threshold > 255) $threshold = 255; + + $radius = abs(round($radius)); + if ($radius == 0) return $image; + $w = imagesx($image); $h = imagesy($image); + $canvas = imagecreatetruecolor($w, $h); + $blur = imagecreatetruecolor($w, $h); + + /** + * Gaussian blur matrix: + * 1 2 1 + * 2 4 2 + * 1 2 1 + **/ + + if (function_exists('imageconvolution')) { // PHP >= 5.1 + $matrix = array( + array( 1, 2, 1 ), + array( 2, 4, 2 ), + array( 1, 2, 1 ) + ); + imagecopy ($blur, $image, 0, 0, 0, 0, $w, $h); + imageconvolution($blur, $matrix, 16, 0); + } else { + + // Move copies of the image around one pixel at the time and merge them with weight + // according to the matrix. The same matrix is simply repeated for higher radii. + for ($i = 0; $i < $radius; $i++) { + imagecopy ($blur, $image, 0, 0, 1, 0, $w - 1, $h); // left + imagecopymerge ($blur, $image, 1, 0, 0, 0, $w, $h, 50); // right + imagecopymerge ($blur, $image, 0, 0, 0, 0, $w, $h, 50); // center + imagecopy ($canvas, $blur, 0, 0, 0, 0, $w, $h); + + imagecopymerge ($blur, $canvas, 0, 0, 0, 1, $w, $h - 1, 33.33333 ); // up + imagecopymerge ($blur, $canvas, 0, 1, 0, 0, $w, $h, 25); // down + } + } + + if ($threshold > 0){ + // Calculate the difference between the blurred pixels and the original + // and set the pixels + for ($x = 0; $x < $w-1; $x++) { // each row + for ($y = 0; $y < $h; $y++) { // each pixel + + $rgbOrig = ImageColorAt($image, $x, $y); + $rOrig = (($rgbOrig >> 16) & 0xFF); + $gOrig = (($rgbOrig >> 8) & 0xFF); + $bOrig = ($rgbOrig & 0xFF); + + $rgbBlur = ImageColorAt($blur, $x, $y); + + $rBlur = (($rgbBlur >> 16) & 0xFF); + $gBlur = (($rgbBlur >> 8) & 0xFF); + $bBlur = ($rgbBlur & 0xFF); + + // When the masked pixels differ less from the original + // than the threshold specifies, they are set to their original value. + $rNew = (abs($rOrig - $rBlur) >= $threshold) + ? max(0, min(255, ($amount * ($rOrig - $rBlur)) + $rOrig)) + : $rOrig; + $gNew = (abs($gOrig - $gBlur) >= $threshold) + ? max(0, min(255, ($amount * ($gOrig - $gBlur)) + $gOrig)) + : $gOrig; + $bNew = (abs($bOrig - $bBlur) >= $threshold) + ? max(0, min(255, ($amount * ($bOrig - $bBlur)) + $bOrig)) + : $bOrig; + + + + if (($rOrig != $rNew) || ($gOrig != $gNew) || ($bOrig != $bNew)) { + $pixCol = ImageColorAllocate($image, $rNew, $gNew, $bNew); + ImageSetPixel($image, $x, $y, $pixCol); + } + } + } + } else { + for ($x = 0; $x < $w; $x++) { // each row + for ($y = 0; $y < $h; $y++) { // each pixel + $rgbOrig = ImageColorAt($image, $x, $y); + $rOrig = (($rgbOrig >> 16) & 0xFF); + $gOrig = (($rgbOrig >> 8) & 0xFF); + $bOrig = ($rgbOrig & 0xFF); + + $rgbBlur = ImageColorAt($blur, $x, $y); + + $rBlur = (($rgbBlur >> 16) & 0xFF); + $gBlur = (($rgbBlur >> 8) & 0xFF); + $bBlur = ($rgbBlur & 0xFF); + + $rNew = ($amount * ($rOrig - $rBlur)) + $rOrig; + if ($rNew > 255) $rNew=255; + elseif($rNew < 0) $rNew=0; + $gNew = ($amount * ($gOrig - $gBlur)) + $gOrig; + if ($gNew > 255) $gNew=255; + elseif($gNew < 0) $gNew=0; + $bNew = ($amount * ($bOrig - $bBlur)) + $bOrig; + if($bNew > 255) $bNew=255; + elseif($bNew < 0) $bNew=0; + $rgbNew = ($rNew << 16) + ($gNew <<8) + $bNew; + ImageSetPixel($image, $x, $y, $rgbNew); + } + } + } + imagedestroy($canvas); + imagedestroy($blur); + + $this->Processed->image = $image; + + } + +} // end Image class + +?> \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/model/Item.php b/blog/wp-content/plugins/shopp/core/model/Item.php new file mode 100644 index 0000000..50aea57 --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/model/Item.php @@ -0,0 +1,351 @@ +load_data(array('prices','images')); + // If product variations are enabled, disregard the first priceline + if ($Product->variations == "on") array_shift($Product->prices); + + // If option ids are passed, lookup by option key, otherwise by id + if (is_array($pricing)) { + $Price = $Product->pricekey[$Product->optionkey($pricing)]; + if (empty($Price)) $Price = $Product->pricekey[$Product->optionkey($pricing,true)]; + } elseif ($pricing) $Price = $Product->priceid[$pricing]; + else { + foreach ($Product->prices as &$Price) + if ($Price->type != "N/A" && + (!$Price->stocked || + ($Price->stocked && $Price->stock > 0))) break; + + } + if (isset($Product->id)) $this->product = $Product->id; + if (isset($Price->id)) $this->price = $Price->id; + $this->category = $category; + $this->option = $Price; + $this->name = $Product->name; + $this->slug = $Product->slug; + $this->description = $Product->summary; + if (isset($Product->thumbnail)) $this->thumbnail = $Product->thumbnail; + $this->menus = $Product->options; + if ($Product->variations == "on") $this->options = $Product->prices; + $this->sku = $Price->sku; + $this->type = $Price->type; + $this->sale = $Price->onsale; + $this->freeshipping = $Price->freeshipping; + $this->saved = ($Price->price - $Price->promoprice); + $this->savings = ($Price->price > 0)?percentage($this->saved/$Price->price)*100:0; + $this->unitprice = (($Price->onsale)?$Price->promoprice:$Price->price); + $this->optionlabel = (count($Product->prices) > 1)?$Price->label:''; + $this->donation = $Price->donation; + $this->data = stripslashes_deep(attribute_escape_deep($data)); + + // Map out the selected menu name and option + if ($Product->variations == "on") { + $selected = explode(",",$this->option->options); $s = 0; + foreach ($this->menus as $i => $menu) { + foreach($menu['options'] as $option) { + if ($option['id'] == $selected[$s]) { + $this->variation[$menu['name']] = $option['name']; break; + } + } + $s++; + } + } + + if (!empty($Price->download)) $this->download = $Price->download; + if ($Price->type == "Shipped") { + $this->shipping = true; + if ($Price->shipping == "on") { + $this->weight = $Price->weight; + $this->shipfee = $Price->shipfee; + } else $this->freeshipping = true; + } + + $this->inventory = ($Price->inventory == "on")?true:false; + $this->taxable = ($Price->tax == "on" && $Shopp->Settings->get('taxes') == "on")?true:false; + } + + function valid () { + if (!$this->product || !$this->price) { + new ShoppError(__('The product could not be added to the cart because it could not be found.','cart_item_invalid',SHOPP_ERR)); + return false; + } + if ($this->inventory && $this->option->stock == 0) { + new ShoppError(__('The product could not be added to the cart because it is not in stock.','cart_item_invalid',SHOPP_ERR)); + return false; + } + return true; + } + + function quantity ($qty) { + + if ($this->type == "Donation" && $this->donation['var'] == "on") { + if ($this->donation['min'] == "on" && floatnum($qty) < $this->unitprice) + $this->unitprice = $this->unitprice; + else $this->unitprice = floatnum($qty); + $this->quantity = 1; + $qty = 1; + } + + $qty = preg_replace('/[^\d+]/','',$qty); + if ($this->inventory) { + if ($qty > $this->option->stock) { + new ShoppError(__('Not enough of the product is available in stock to fulfill your request.','Shopp'),'item_low_stock'); + $this->quantity = $this->option->stock; + } + else $this->quantity = $qty; + } else $this->quantity = $qty; + + $this->total = $this->quantity * $this->unitprice; + } + + function add ($qty) { + if ($this->type == "Donation" && $this->donation['var'] == "on") { + $qty = floatnum($qty); + $this->quantity = $this->unitprice; + } + $this->quantity($this->quantity+$qty); + } + + function options ($selection = "",$taxrate=0) { + if (empty($this->options)) return ""; + + $string = ""; + foreach($this->options as $option) { + if ($option->type == "N/A") continue; + $currently = ($option->onsale)?$option->promoprice:$option->price; + + $difference = (float)($currently+($currently*$taxrate))-($this->unitprice+($this->unitprice*$taxrate)); + // $difference = $currently-$this->unitprice; + + $price = ''; + if ($difference > 0) $price = ' (+'.money($difference).')'; + if ($difference < 0) $price = ' (-'.money(abs($difference)).')'; + + $selected = ""; + if ($selection == $option->id) $selected = ' selected="Selected"'; + $disabled = ""; + if ($option->inventory == "on" && $option->stock < $this->quantity) + $disabled = ' disabled="disabled"'; + + $string .= ''; + } + return $string; + } + + function unstock () { + if (!$this->inventory) return; + global $Shopp; + $db = DB::get(); + + // Update stock in the database + $table = DatabaseObject::tablename(Price::$table); + $db->query("UPDATE $table SET stock=stock-{$this->quantity} WHERE id='{$this->price}' AND stock > 0"); + + // Update stock in the model + $this->option->stock -= $this->quantity; + + // Handle notifications + $product = $this->name.' ('.$this->option->label.')'; + if ($this->option->stock == 0) + return new ShoppError(sprintf(__('%s is now out-of-stock!','Shopp'),$product),'outofstock_warning',SHOPP_STOCK_ERR); + + if ($this->option->stock <= $Shopp->Settings->get('lowstock_level')) + return new ShoppError(sprintf(__('%s has low stock levels and should be re-ordered soon.','Shopp'),$product),'lowstock_warning',SHOPP_STOCK_ERR); + + } + + function shipping (&$Shipping) { + } + + function tag ($id,$property,$options=array()) { + global $Shopp; + + // Return strings with no options + switch ($property) { + case "id": return $id; + case "name": return $this->name; + case "link": + case "url": + return (SHOPP_PERMALINKS)? + $Shopp->shopuri.$this->slug: + add_query_arg('shopp_pid',$this->product,$Shopp->shopuri); + case "sku": return $this->sku; + } + + $taxes = false; + if (isset($options['taxes'])) $taxes = $options['taxes']; + if ($property == "unitprice" || $property == "total" || $property == "options") + $taxrate = shopp_taxrate($taxes,$this->taxable); + + // Handle currency values + $result = ""; + switch ($property) { + case "unitprice": $result = (float)$this->unitprice+($this->unitprice*$taxrate); break; + case "total": $result = (float)$this->total+($this->total*$taxrate); break; + case "tax": $result = (float)$this->tax; break; + } + if (is_float($result)) { + if (isset($options['currency']) && !value_is_true($options['currency'])) return $result; + else return money($result); + } + + // Handle values with complex options + switch ($property) { + case "quantity": + $result = $this->quantity; + if ($this->type == "Donation" && $this->donation['var'] == "on") return $result; + if (isset($options['input']) && $options['input'] == "menu") { + if (!isset($options['value'])) $options['value'] = $this->quantity; + if (!isset($options['options'])) + $values = "1-15,20,25,30,35,40,45,50,60,70,80,90,100"; + else $values = $options['options']; + + if (strpos($values,",") !== false) $values = explode(",",$values); + else $values = array($values); + $qtys = array(); + foreach ($values as $value) { + if (strpos($value,"-") !== false) { + $value = explode("-",$value); + if ($value[0] >= $value[1]) $qtys[] = $value[0]; + else for ($i = $value[0]; $i < $value[1]+1; $i++) $qtys[] = $i; + } else $qtys[] = $value; + } + $result = ''; + } elseif (isset($options['input']) && valid_input($options['input'])) { + if (!isset($options['size'])) $options['size'] = 5; + if (!isset($options['value'])) $options['value'] = $this->quantity; + $result = ''; + } else $result = $this->quantity; + break; + case "remove": + $label = __("Remove"); + if (isset($options['label'])) $label = $options['label']; + if (isset($options['class'])) $class = ' class="'.$options['class'].'"'; + else $class = ' class="remove"'; + if (isset($options['input'])) { + switch ($options['input']) { + case "button": + $result = ''; break; + case "checkbox": + $result = ''; break; + } + } else { + $result = ''.$label.''; + } + break; + case "optionlabel": $result = $this->optionlabel; break; + case "options": + $class = ""; + if (isset($options['show']) && + strtolower($options['show']) == "selected") + return (!empty($this->optionlabel))? + $options['before'].$this->optionlabel.$options['after']:''; + + if (isset($options['class'])) $class = ' class="'.$options['class'].'" '; + if (count($this->options) > 1) { + $result .= $options['before']; + $result .= ''; + $result .= ' '; + $result .= $options['after']; + } + break; + case "hasinputs": + case "has-inputs": return (count($this->data) > 0); break; + case "inputs": + if (!$this->dataloop) { + reset($this->data); + $this->dataloop = true; + } else next($this->data); + + if (current($this->data)) return true; + else { + $this->dataloop = false; + return false; + } + break; + case "input": + $data = current($this->data); + $name = key($this->data); + if (isset($options['name'])) return $name; + return $data; + break; + case "inputs-list": + case "inputslist": + if (empty($this->data)) return false; + $before = ""; $after = ""; $classes = ""; $excludes = array(); + if (!empty($options['class'])) $classes = ' class="'.$options['class'].'"'; + if (!empty($options['exclude'])) $excludes = explode(",",$options['exclude']); + if (!empty($options['before'])) $before = $options['before']; + if (!empty($options['after'])) $after = $options['after']; + + $result .= $before.''; + foreach ($this->data as $name => $data) { + if (in_array($name,$excludes)) continue; + $result .= '
  • '.$name.': '.$data.'
  • '; + } + $result .= ''.$after; + return $result; + break; + case "thumbnail": + if (!empty($options['class'])) $options['class'] = ' class="'.$options['class'].'"'; + if (isset($this->thumbnail)) { + $img = $this->thumbnail; + $width = (isset($options['width']))?$options['width']:$img->properties['height']; + $height = (isset($options['height']))?$options['height']:$img->properties['height']; + + return ''.$this->name.' '.$img->datatype.''; break; + } + + } + if (!empty($result)) return $result; + + + return false; + } + +} // end Item class + +?> \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/model/Price.php b/blog/wp-content/plugins/shopp/core/model/Price.php new file mode 100644 index 0000000..1736fbe --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/model/Price.php @@ -0,0 +1,64 @@ +init(self::$table); + if ($this->load($id,$key)) { + $this->load_download(); + return true; + } + else return false; + } + + /** + * Load a single record by a slug name */ + function loadby_optionkey ($product,$key) { + $db = DB::get(); + + $r = $db->query("SELECT * FROM $this->_table WHERE product='$product' AND optionkey='$key'"); + $this->populate($r); + + if (!empty($this->id)) return true; + return false; + } + + function load_download () { + if ($this->type != "Download") return false; + $db = DB::get(); + + $table = DatabaseObject::tablename(Asset::$table); + $this->download = $db->query("SELECT id,name,properties,size FROM $table WHERE parent='$this->id' AND context='price' AND datatype='download' LIMIT 1"); + + if (empty($this->download)) return false; + + $this->download->properties = unserialize($this->download->properties); + return true; + } + + function attach_download ($id) { + if (!$id) return false; + $db = DB::get(); + + $table = DatabaseObject::tablename(Asset::$table); + $db->query("DELETE FROM $table WHERE parent='$this->id' AND context='price' AND datatype='download'"); + $db->query("UPDATE $table SET parent='$this->id',context='price',datatype='download' WHERE id='$id'"); + + do_action('attach_product_download',$id,$this->id); + + return true; + } + +} // end Price class + +?> \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/model/Product.php b/blog/wp-content/plugins/shopp/core/model/Product.php new file mode 100644 index 0000000..d040da5 --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/model/Product.php @@ -0,0 +1,1327 @@ +array(),'min'=>array()); + var $onsale = false; + var $categories = array(); + var $tags = array(); + var $images = array(); + var $imagesets = array(); + var $imageset = false; + var $specs = array(); + var $ranges = array('max'=>array(),'min'=>array()); + var $freeshipping = false; + var $priceloop = false; + var $specloop = false; + var $categoryloop = false; + var $imageloop = false; + var $tagloop = false; + var $outofstock = false; + var $stock = 0; + var $options = 0; + + function Product ($id=false,$key=false) { + $this->init(self::$table); + if ($this->load($id,$key)) { + add_filter('shopp_product_description', 'wptexturize'); + add_filter('shopp_product_description', 'convert_chars'); + add_filter('shopp_product_description', 'wpautop'); + add_filter('shopp_product_description', 'do_shortcode', 11); // AFTER wpautop() + + add_filter('shopp_product_spec', 'wptexturize'); + add_filter('shopp_product_spec', 'convert_chars'); + add_filter('shopp_product_spec', 'do_shortcode', 11); // AFTER wpautop() + + return true; + } + return false; + } + + function load_data ($options=false,&$products=false) { + global $Shopp; + $db =& DB::get(); + + // Load object schemas on request + + $Dataset = array(); + if (in_array('prices',$options)) { + $promotable = DatabaseObject::tablename(Promotion::$table); + $discounttable = DatabaseObject::tablename(Discount::$table); + $assettable = DatabaseObject::tablename(Asset::$table); + + $Dataset['prices'] = new Price(); + $Dataset['prices']->_datatypes['promos'] = "MAX(promo.status)"; + $Dataset['prices']->_datatypes['promotions'] = "group_concat(promo.name)"; + $Dataset['prices']->_datatypes['percentoff'] = "SUM(IF (promo.type='Percentage Off',promo.discount,0))"; + $Dataset['prices']->_datatypes['amountoff'] = "SUM(IF (promo.type='Amount Off',promo.discount,0))"; + $Dataset['prices']->_datatypes['freeshipping'] = "SUM(IF (promo.type='Free Shipping',1,0))"; + $Dataset['prices']->_datatypes['buyqty'] = "IF (promo.type='Buy X Get Y Free',promo.buyqty,0)"; + $Dataset['prices']->_datatypes['getqty'] = "IF (promo.type='Buy X Get Y Free',promo.getqty,0)"; + $Dataset['prices']->_datatypes['download'] = "download.id"; + $Dataset['prices']->_datatypes['filename'] = "download.name"; + $Dataset['prices']->_datatypes['filedata'] = "download.properties"; + $Dataset['prices']->_datatypes['filesize'] = "download.size"; + } + + if (in_array('images',$options)) { + $Dataset['images'] = new Asset(); + unset($Dataset['images']->_datatypes['data']); + } + + if (in_array('categories',$options)) { + $Dataset['categories'] = new Category(); + unset($Dataset['categories']->_datatypes['priceranges']); + unset($Dataset['categories']->_datatypes['specs']); + unset($Dataset['categories']->_datatypes['options']); + unset($Dataset['categories']->_datatypes['prices']); + } + + if (in_array('specs',$options)) $Dataset['specs'] = new Spec(); + if (in_array('tags',$options)) $Dataset['tags'] = new Tag(); + + // Determine the maximum columns to allocate + $maxcols = 0; + foreach ($Dataset as $set) { + $cols = count($set->_datatypes); + if ($cols > $maxcols) $maxcols = $cols; + } + + // Prepare product list depending on single product or entire list + $ids = array(); + if (isset($products) && is_array($products)) { + foreach ($products as $product) $ids[] = $product->id; + } else $ids[0] = $this->id; + + // Skip if there are no product ids + if (empty($ids) || empty($ids[0])) return false; + + // Build the mega-query + foreach ($Dataset as $rtype => $set) { + + // Allocate generic columns for record data + $columns = array(); $i = 0; + foreach ($set->_datatypes as $key => $datatype) + $columns[] = ((strpos($datatype,'.')!==false)?"$datatype":"{$set->_table}.$key")." AS c".($i++); + for ($i = $i; $i < $maxcols; $i++) + $columns[] = "'' AS c$i"; + + $cols = join(',',$columns); + + // Build object-specific selects and UNION them + $where = ""; + if (isset($query)) $query .= " UNION "; + else $query = ""; + switch($rtype) { + case "prices": + foreach ($ids as $id) $where .= ((!empty($where))?" OR ":"")."$set->_table.product=$id"; + $query .= "(SELECT '$set->_table' as dataset,$set->_table.product AS product,'$rtype' AS rtype,'' AS alphaorder,$set->_table.sortorder AS sortorder,$cols FROM $set->_table + LEFT JOIN $assettable AS download ON $set->_table.id=download.parent AND download.context='price' AND download.datatype='download' + LEFT JOIN $discounttable AS discount ON discount.product=$set->_table.product AND discount.price=$set->_table.id + LEFT JOIN $promotable AS promo ON promo.id=discount.promo AND (promo.status='enabled' AND ((UNIX_TIMESTAMP(starts)=1 AND UNIX_TIMESTAMP(ends)=1) OR (UNIX_TIMESTAMP(now()) > UNIX_TIMESTAMP(starts) AND UNIX_TIMESTAMP(now()) < UNIX_TIMESTAMP(ends)) )) + WHERE $where GROUP BY $set->_table.id)"; + break; + case "images": + $ordering = $Shopp->Settings->get('product_image_order'); + if (empty($ordering)) $ordering = "ASC"; + $orderby = $Shopp->Settings->get('product_image_orderby'); + + $sortorder = "0"; + if ($orderby == "sortorder" || $orderby == "created") { + if ($orderby == "created") $orderby = "UNIX_TIMESTAMP(created)"; + switch ($ordering) { + case "DESC": $sortorder = "$orderby*-1"; break; + case "RAND": $sortorder = "RAND()"; break; + default: $sortorder = "$orderby"; + } + } + + $alphaorder = "''"; + if ($orderby == "name") { + switch ($ordering) { + case "DESC": $alphaorder = "$orderby"; break; + case "RAND": $alphaorder = "RAND()"; break; + default: $alphaorder = "$orderby"; + } + } + + foreach ($ids as $id) $where .= ((!empty($where))?" OR ":"")."parent=$id"; + $where = "($where) AND context='product'"; + $query .= "(SELECT '$set->_table' as dataset,parent AS product,'$rtype' AS rtype,$alphaorder AS alphaorder,$sortorder AS sortorder,$cols FROM $set->_table WHERE $where ORDER BY $orderby)"; + break; + case "specs": + foreach ($ids as $id) $where .= ((!empty($where))?" OR ":"")."product=$id"; + $query .= "(SELECT '$set->_table' as dataset,product,'$rtype' AS rtype,'' AS alphaorder,sortorder AS sortorder,$cols FROM $set->_table WHERE $where)"; + break; + case "categories": + foreach ($ids as $id) $where .= ((!empty($where))?" OR ":"")."catalog.product=$id"; + $where = "($where) AND catalog.category > 0"; + $query .= "(SELECT '$set->_table' as dataset,catalog.product AS product,'$rtype' AS rtype,$set->_table.name AS alphaorder,0 AS sortorder,$cols FROM {$Shopp->Catalog->_table} AS catalog LEFT JOIN $set->_table ON catalog.category=$set->_table.id WHERE $where)"; + break; + case "tags": + foreach ($ids as $id) $where .= ((!empty($where))?" OR ":"")."catalog.product=$id"; + $where = "($where) AND catalog.tag > 0"; + $query .= "(SELECT '$set->_table' as dataset,catalog.product AS product,'$rtype' AS rtype,$set->_table.name AS alphaorder,0 AS sortorder,$cols FROM {$Shopp->Catalog->_table} AS catalog LEFT JOIN $set->_table ON catalog.tag=$set->_table.id WHERE $where)"; + break; + } + } + + // Add order by columns + $query .= " ORDER BY sortorder"; + // echo $query; + // Execute the query + $data = $db->query($query,AS_ARRAY); + + // Process the results into specific product object data in a product set + + foreach ($data as $row) { + if (is_array($products) && isset($products[$row->product])) + $target = $products[$row->product]; + else $target = $this; + + $record = new stdClass(); $i = 0; $name = ""; + foreach ($Dataset[$row->rtype]->_datatypes AS $key => $datatype) { + $column = 'c'.$i++; + $record->{$key} = ''; + if ($key == "name") $name = $row->{$column}; + if (!empty($row->{$column})) { + if (preg_match("/^[sibNaO](?:\:.+?\{.*\}$|\:.+;$|;$)/",$row->{$column})) + $row->{$column} = unserialize($row->{$column}); + $record->{$key} = $row->{$column}; + } + } + $target->{$row->rtype}[] = $record; + if (!empty($name)) { + if (isset($target->{$row->rtype.'key'}[$name])) + $target->{$row->rtype.'key'}[$name] = array($target->{$row->rtype.'key'}[$name],$record); + else $target->{$row->rtype.'key'}[$name] = $record; + } + } + + if (is_array($products)) { + foreach ($products as $product) if (!empty($product->prices)) $product->pricing(); + foreach ($products as $product) if (count($product->images) >= 3 && count($product->imagesets) <= 1) + $product->imageset(); + } else { + if (!empty($this->prices)) $this->pricing($options); + if (count($this->images) >= 3 && count($this->imagesets) <= 1) $this->imageset(); + } + + } // end load_data() + + function pricing ($options = false) { + global $Shopp; + + $variations = ($this->variations == "on"); + $freeshipping = true; + $this->inventory = false; + foreach ($this->prices as $i => &$price) { + // Build secondary lookup table using the combined optionkey + $this->pricekey[$price->optionkey] = $price; + + // Build third lookup table using the price id as the key + $this->priceid[$price->id] = $price; + if ($price->type == "N/A" || ($i > 0 && !$variations)) continue; + + // Boolean flag for custom product sales + $price->onsale = false; + if ($price->sale == "on" && $price->type != "N/A") + $this->onsale = $price->onsale = true; + + $price->stocked = false; + if ($price->inventory == "on" && $price->type != "N/A") { + $this->stock += $price->stock; + $this->inventory = $price->stocked = true; + } + + if ($price->freeshipping == 0) $freeshipping = false; + + if ($price->onsale) $price->promoprice = (float)$price->saleprice; + else $price->promoprice = (float)$price->price; + + if ((isset($price->promos) && $price->promos == 'enabled')) { + if ($price->percentoff > 0) { + $price->promoprice = $price->promoprice - ($price->promoprice * ($price->percentoff/100)); + $this->onsale = $price->onsale = true; + } + if ($price->amountoff > 0) { + $price->promoprice = $price->promoprice - $price->amountoff; + $this->onsale = $price->onsale = true;; + } + } + + // Grab price and saleprice ranges (minimum - maximum) + if ($price->type != "N/A") { + if (!$price->price) $price->price = 0; + if (!isset($this->pricerange['min']['price'])) + $this->pricerange['min']['price'] = $this->pricerange['max']['price'] = $price->price; + if ($this->pricerange['min']['price'] > $price->price) + $this->pricerange['min']['price'] = $price->price; + if ($this->pricerange['max']['price'] < $price->price) + $this->pricerange['max']['price'] = $price->price; + + if (!isset($this->pricerange['min']['saleprice'])) + $this->pricerange['min']['saleprice'] = $this->pricerange['max']['saleprice'] = $price->promoprice; + if ($this->pricerange['min']['saleprice'] > $price->promoprice) + $this->pricerange['min']['saleprice'] = $price->promoprice; + if ($this->pricerange['max']['saleprice'] < $price->promoprice) + $this->pricerange['max']['saleprice'] = $price->promoprice; + + if ($price->stocked) { + if (!isset($this->pricerange['min']['stock'])) + $this->pricerange['min']['stock'] = $this->pricerange['max']['stock'] = $price->stock; + if ($this->pricerange['min']['stock'] > $price->stock) + $this->pricerange['min']['stock'] = $price->stock; + if ($this->pricerange['max']['stock'] < $price->stock) + $this->pricerange['max']['stock'] = $price->stock; + } + + } + + // Determine savings ranges + if ($price->onsale + && isset($this->pricerange['min']['price']) + && isset($this->pricerange['min']['saleprice'])) { + + if (!isset($this->pricerange['min']['saved'])) { + $this->pricerange['min']['saved'] = $price->price; + $this->pricerange['min']['savings'] = 100; + $this->pricerange['max']['saved'] = 0; + $this->pricerange['max']['savings'] = 0; + } + + if ($price->price - $price->promoprice < $this->pricerange['min']['saved']) + $this->pricerange['min']['saved'] = + $price->price - $price->promoprice; + + if ($price->price - $price->promoprice > $this->pricerange['max']['saved']) + $this->pricerange['max']['saved'] = + $price->price - $price->promoprice; + + // Find lowest savings percentage + if ($price->onsale) { + if ($this->pricerange['min']['saved']/$price->price < $this->pricerange['min']['savings']) + $this->pricerange['min']['savings'] = ($this->pricerange['min']['saved']/$price->price)*100; + if ($this->pricerange['max']['saved']/$price->price < $this->pricerange['min']['savings']) + $this->pricerange['min']['savings'] = ($this->pricerange['max']['saved']/$price->price)*100; + + // Find highest savings percentage + if ($this->pricerange['min']['saved']/$price->price > $this->pricerange['max']['savings']) + $this->pricerange['max']['savings'] = ($this->pricerange['min']['saved']/$price->price)*100; + if ($this->pricerange['max']['saved']/$price->price > $this->pricerange['max']['savings']) + $this->pricerange['max']['savings'] = ($this->pricerange['max']['saved']/$price->price)*100; + } + } + + // Determine weight ranges + if(!isset($this->weightrange['min'])) $this->weightrange = array('min'=>0,'max'=>0); + if($price->weight && $price->weight > 0){ + if(!$this->weightrange['min'] || $price->weight < $this->weightrange['min']) + $this->weightrange['min'] = $price->weight; + if(!$this->weightrange['max'] || $price->weight > $this->weightrange['max']) + $this->weightrange['max'] = $price->weight; + } + + if (defined('WP_ADMIN') && !isset($options['taxes'])) $options['taxes'] = true; + if (defined('WP_ADMIN') && value_is_true($options['taxes']) && $price->tax == "on") { + $base = $Shopp->Settings->get('base_operations'); + if ($base['vat']) { + $taxrate = $Shopp->Cart->taxrate(); + $price->price += $price->price*$taxrate; + $price->saleprice += $price->saleprice*$taxrate; + } + } + + } // end foreach($price) + if ($this->inventory && $this->stock <= 0) $this->outofstock = true; + if ($freeshipping) $this->freeshipping = true; + } + + function imageset () { + global $Shopp; + // Organize images into groupings by type + $this->imagesets = array(); + foreach ($this->images as $key => &$image) { + if (empty($this->imagesets[$image->datatype])) $this->imagesets[$image->datatype] = array(); + if ($image->id) { + if (SHOPP_PERMALINKS) $image->uri = $Shopp->imguri.$image->id; + else $image->uri = add_query_arg('shopp_image',$image->id,$Shopp->imguri); + } + $this->imagesets[$image->datatype][] = $image; + } + $this->thumbnail = $this->imagesets['thumbnail'][0]; + return true; + } + + function merge_specs () { + $merged = array(); + foreach ($this->specs as $key => $spec) { + if (!isset($merged[$spec->name])) $merged[$spec->name] = $spec; + else { + if (!is_array($merged[$spec->name]->content)) + $merged[$spec->name]->content = array($merged[$spec->name]->content); + $merged[$spec->name]->content[] = $spec->content; + } + } + $this->specs = $merged; + } + + function save_categories ($updates) { + $db = DB::get(); + + if (empty($updates)) $updates = array(); + + $current = array(); + foreach ($this->categories as $category) $current[] = $category->id; + + $added = array_diff($updates,$current); + $removed = array_diff($current,$updates); + + $table = DatabaseObject::tablename(Catalog::$table); + + if (!empty($added)) { + foreach ($added as $id) { + if (empty($id)) continue; + $db->query("INSERT $table SET category='$id',product='$this->id',created=now(),modified=now()"); + } + } + + if (!empty($removed)) { + foreach ($removed as $id) { + if (empty($id)) continue; + $db->query("DELETE LOW_PRIORITY FROM $table WHERE category='$id' AND product='$this->id'"); + } + + } + + } + + function save_tags ($updates) { + $db = DB::get(); + + if (empty($updates)) $updates = array(); + $updates = stripslashes_deep($updates); + + $current = array(); + foreach ($this->tags as $tag) $current[] = $tag->name; + + $added = array_diff($updates,$current); + $removed = array_diff($current,$updates); + + if (!empty($added)) { + $catalog = DatabaseObject::tablename(Catalog::$table); + $tagtable = DatabaseObject::tablename(Tag::$table); + $where = ""; + foreach ($added as $tag) $where .= ($where == ""?"":" OR ")."name='".$db->escape($tag)."'"; + $results = $db->query("SELECT id,name FROM $tagtable WHERE $where",AS_ARRAY); + $exists = array(); + foreach ($results as $tag) $exists[$tag->id] = $tag->name; + + foreach ($added as $tag) { + if (empty($tag)) continue; // No empty tags + $tagid = array_search($tag,$exists); + + if (!$tagid) { + $Tag = new Tag(); + $Tag->name = $tag; + $Tag->save(); + $tagid = $Tag->id; + } + + if (!empty($tagid)) + $db->query("INSERT $catalog SET tag='$tagid',product='$this->id',created=now(),modified=now()"); + + } + } + + if (!empty($removed)) { + $catalog = DatabaseObject::tablename(Catalog::$table); + foreach ($removed as $tag) { + $Tag = new Tag($tag,'name'); + if (!empty($Tag->id)) + $db->query("DELETE LOW_PRIORITY FROM $catalog WHERE tag='$Tag->id' AND product='$this->id'"); + } + } + + } + + /** + * optionkey + * There is no Zul only XOR! */ + function optionkey ($ids=array(),$deprecated=false) { + if ($deprecated) $factor = 101; + else $factor = 7001; + if (empty($ids)) return 0; + foreach ($ids as $set => $id) + $key = $key ^ ($id*$factor); + return $key; + } + + /** + * save_imageorder() + * Updates the sortorder of image assets (source, featured and thumbnails) + * based on the provided array of image ids */ + function save_imageorder ($ordering) { + $db = DB::get(); + $table = DatabaseObject::tablename(Asset::$table); + foreach ($ordering as $i => $id) + $db->query("UPDATE LOW_PRIORITY $table SET sortorder='$i' WHERE id='$id' OR src='$id'"); + return true; + } + + /** + * link_images() + * Updates the product id of the images to link to the product + * when the product being saved is new (has no previous id assigned) */ + function link_images ($images) { + $db = DB::get(); + $table = DatabaseObject::tablename(Asset::$table); + + $query = ""; + foreach ($images as $i => $id) { + if (empty($id)) continue; + if ($i > 0) $query .= " OR "; + $query .= "id=$id OR src=$id"; + } + + if (empty($query)) return false; + else $query = "UPDATE $table SET parent='$this->id',context='product' WHERE ".$query; + + $db->query($query); + + return true; + } + + /** + * update_images() + * Updates the image details for an entire image set (thumbnail, small, image) */ + function update_images ($images) { + if (!is_array($images)) return false; + + $db = DB::get(); + $table = DatabaseObject::tablename(Asset::$table); + + foreach($images as $i => $img) { + $query = "SELECT imgs.id FROM $table AS thumb LEFT JOIN $table AS imgs ON thumb.src=imgs.src OR thumb.src=imgs.id WHERE thumb.id={$img['id']}"; + $imageset = $db->query($query); + foreach ($imageset as $is) { + $Image = new Asset(); + unset($Image->_datatypes['data'],$Image->data); + $Image->load($is->id); + $Image->properties['title'] = $img['title']; + $Image->properties['alt'] = $img['alt']; + $Image->save(); + } + } + + return true; + } + + + /** + * delete_images() + * Delete provided array of image ids, removing the source image and + * all related images (small and thumbnails) */ + function delete_images ($images) { + $Images = new Asset(); + $Images->deleteset($images,'image'); + return true; + } + + /** + * Deletes the record associated with this object */ + function delete () { + $db = DB::get(); + $id = $this->{$this->_key}; + if (empty($id)) return false; + + // Delete from categories + $table = DatabaseObject::tablename(Catalog::$table); + $db->query("DELETE LOW_PRIORITY FROM $table WHERE product='$id'"); + + // Delete prices + $table = DatabaseObject::tablename(Price::$table); + $db->query("DELETE LOW_PRIORITY FROM $table WHERE product='$id'"); + + // Delete specs + $table = DatabaseObject::tablename(Spec::$table); + $db->query("DELETE LOW_PRIORITY FROM $table WHERE product='$id'"); + + // Delete images/files + $table = DatabaseObject::tablename(Asset::$table); + + // Delete images + $images = array(); + $src = $db->query("SELECT id FROM $table WHERE parent='$id' AND context='product' AND datatype='image'",AS_ARRAY); + foreach ($src as $img) $images[] = $img->id; + $this->delete_images($images); + + // Delete product downloads (but keep the file if on file system) + $db->query("DELETE LOW_PRIORITY FROM $table WHERE parent='$id' AND context='product'"); + + // Delete record + $db->query("DELETE FROM $this->_table WHERE $this->_key='$id'"); + + } + + function duplicate () { + $db =& DB::get(); + + $this->load_data(array('prices','specs','categories','tags','images','taxes'=>'false')); + $this->id = ''; + $this->name = $this->name.' '.__('copy','Shopp'); + $this->slug = sanitize_title_with_dashes($this->name); + + // Check for an existing product slug + $existing = $db->query("SELECT slug FROM $this->_table WHERE slug='$this->slug' LIMIT 1"); + if ($existing) { + $suffix = 2; + while($existing) { + $altslug = substr($this->slug, 0, 200-(strlen($suffix)+1)). "-$suffix"; + $existing = $db->query("SELECT slug FROM $this->_table WHERE slug='$altslug' LIMIT 1"); + $suffix++; + } + $this->slug = $altslug; + } + $this->created = ''; + $this->modified = ''; + + $this->save(); + + // Copy prices + foreach ($this->prices as $price) { + $Price = new Price(); + $Price->updates($price,array('id','product','created','modified')); + $Price->product = $this->id; + $Price->save(); + } + + // Copy sepcs + foreach ($this->specs as $spec) { + $Spec = new Spec(); + $Spec->updates($spec,array('id','product','created','modified')); + $Spec->product = $this->id; + $Spec->save(); + } + + // Copy categories + $categories = array(); + foreach ($this->categories as $category) $categories[] = $category->id; + $this->categories = array(); + $this->save_categories($categories); + + // Copy tags + $taglist = array(); + foreach ($this->tags as $tag) $taglist[] = $tag->name; + $this->tags = array(); + $this->save_tags($taglist); + + // // Copy product images + $template = new Asset(); + $columns = array(); $values = array(); + foreach ($template->_datatypes as $name => $type) { + $colname = $name; + $columns[$colname] = $name; + if ($name == "id") $name = "''"; + if ($name == "parent") $name = "'$this->id'"; + if ($name == "created" || $name == "modified") $name = "now()"; + $values[$colname] = $name; + } + $sets = array('image','small','thumbnail'); + $images = array(); + foreach ($sets as $set) { + foreach ($this->imagesets[$set] as $image) { + if (isset($images[$image->src])) $values['src'] = $images[$image->src]; + $id = $db->query("INSERT $template->_table (".join(',',$columns).") SELECT ".join(",",$values)." FROM $template->_table WHERE id=$image->id"); + if ($set == "image") { + $images[$image->id] = $id; + $db->query("UPDATE $template->_table SET src=$id WHERE id=$id LIMIT 1"); + } + } + } + + } + + function tag ($property,$options=array()) { + global $Shopp; + + switch ($property) { + case "link": + case "url": + if (SHOPP_PERMALINKS) $url = esc_url(add_query_arg($_GET,$Shopp->shopuri.urldecode($this->slug)."/")); + else $url = add_query_arg('shopp_pid',$this->id,$Shopp->shopuri); + return $url; + break; + case "found": + if (empty($this->id)) return false; + $load = array('prices','images','specs'); + if (isset($options['load'])) $load = explode(",",$options['load']); + $this->load_data($load); + return true; + break; + case "id": return $this->id; break; + case "name": return $this->name; break; + case "slug": return $this->slug; break; + case "summary": return $this->summary; break; + case "description": + return apply_filters('shopp_product_description',$this->description); + case "isfeatured": + case "is-featured": + return ($this->featured == "on"); break; + case "price": + if (empty($this->prices)) $this->load_data(array('prices')); + if (!isset($options['taxes'])) $options['taxes'] = null; + + // $taxrate = 0; + // $taxes = false; + // $base = $Shopp->Settings->get('base_operations'); + // if ($base['vat']) $taxes = true; + // if (isset($options['taxes'])) $taxes = (value_is_true($options['taxes'])); + // if ($taxes) $taxrate = $Shopp->Cart->taxrate(); + + if (count($this->options) > 0) { + $taxrate = shopp_taxrate($options['taxes']); + if ($this->pricerange['min']['price'] == $this->pricerange['max']['price']) + return money($this->pricerange['min']['price'] + ($this->pricerange['min']['price']*$taxrate)); + else { + if (!empty($options['starting'])) return $options['starting']." ".money($this->pricerange['min']['price']+($this->pricerange['min']['price']*$taxrate)); + return money($this->pricerange['min']['price']+($this->pricerange['min']['price']*$taxrate))." — ".money($this->pricerange['max']['price'] + ($this->pricerange['max']['price']*$taxrate)); + } + } else { + $taxrate = shopp_taxrate($options['taxes'],$this->prices[0]->tax); + return money($this->prices[0]->price + ($this->prices[0]->price*$taxrate)); + } + break; + case "weight": + if(empty($this->prices)) $this->load_data(array('prices')); + $unit = (isset($options['units']) && !value_is_true($options['units'])? + false : $Shopp->Settings->get('weight_unit')); + if(!$this->weightrange['min']) return false; + + $string = ($this->weightrange['min'] == $this->weightrange['max']) ? + round($this->weightrange['min'],3) : + round($this->weightrange['min'],3) . " - " . round($this->weightrange['max'],3); + $string .= ($unit) ? " $unit" : ""; + return $string; + break; + case "onsale": + if (empty($this->prices)) $this->load_data(array('prices')); + if (empty($this->prices)) return false; + return $this->onsale; + + // if (empty($this->prices)) $this->load_prices(); + $sale = false; + if (count($this->prices) > 1) { + foreach($this->prices as $pricetag) + if (isset($pricetag->onsale) && $pricetag->onsale == "on") $sale = true; + return $sale; + } else return ($this->prices[0]->onsale == "on")?true:false; + break; + case "saleprice": + if (empty($this->prices)) $this->load_data(array('prices')); + if (!isset($options['taxes'])) $options['taxes'] = null; + $pricetag = 'price'; + + if ($this->onsale) $pricetag = 'saleprice'; + if (count($this->options) > 0) { + $taxrate = shopp_taxrate($options['taxes']); + if ($this->pricerange['min'][$pricetag] == $this->pricerange['max'][$pricetag]) + return money($this->pricerange['min'][$pricetag]+($this->pricerange['min'][$pricetag]*$taxrate)); // No price range + else { + if (!empty($options['starting'])) return $options['starting']." ".money($this->pricerange['min'][$pricetag]+($this->pricerange['min'][$pricetag]*$taxrate)); + return money($this->pricerange['min'][$pricetag]+($this->pricerange['min'][$pricetag]*$taxrate))." — ".money($this->pricerange['max'][$pricetag]+($this->pricerange['max'][$pricetag]*$taxrate)); + } + } else { + $taxrate = shopp_taxrate($options['taxes'],$this->prices[0]->tax); + return money($this->prices[0]->promoprice+($this->prices[0]->promoprice*$taxrate)); + } + break; + case "has-savings": return ($this->onsale && $this->pricerange['min']['saved'] > 0)?true:false; break; + case "savings": + if (empty($this->prices)) $this->load_data(array('prices')); + if (!isset($options['taxes'])) $options['taxes'] = null; + + $taxrate = shopp_taxrate($options['taxes']); + + if (!isset($options['show'])) $options['show'] = ''; + if ($options['show'] == "%" || $options['show'] == "percent") { + if ($this->options > 1) { + if (round($this->pricerange['min']['savings']) == round($this->pricerange['max']['savings'])) + return percentage($this->pricerange['min']['savings']); // No price range + else return percentage($this->pricerange['min']['savings'])." — ".percentage($this->pricerange['max']['savings']); + } else return percentage($this->pricerange['max']['savings']); + } else { + if ($this->options > 1) { + if ($this->pricerange['min']['saved'] == $this->pricerange['max']['saved']) + return money($this->pricerange['min']['saved']+($this->pricerange['min']['saved']*$taxrate)); // No price range + else return money($this->pricerange['min']['saved']+($this->pricerange['min']['saved']*$taxrate))." — ".money($this->pricerange['max']['saved']+($this->pricerange['max']['saved']*$taxrate)); + } else return money($this->pricerange['max']['saved']+($this->pricerange['max']['saved']*$taxrate)); + } + break; + case "freeshipping": + if (empty($this->prices)) $this->load_data(array('prices')); + // if (empty($this->prices)) $this->load_prices(); + return $this->freeshipping; + case "thumbnail": + if (empty($this->imagesets)) $this->load_data(array('images')); + if (empty($options['class'])) $options['class'] = ''; + else $options['class'] = ' class="'.$options['class'].'"'; + if (isset($this->thumbnail)) { + $img = $this->thumbnail; + $title = !empty($img->properties['title'])?' title="'.attribute_escape($img->properties['title']).'"':''; + + $width = (isset($options['width']))?$options['width']:$img->properties['width']; + $height = (isset($options['height']))?$options['height']:$img->properties['height']; + + if (isset($options['width']) && !isset($options['height'])) { + $scale = $width/$img->properties['width']; + $height = round($img->properties['height']*$scale); + } + if (isset($options['height']) && !isset($options['width'])) { + $scale = $height/$img->properties['height']; + $width = round($img->properties['width']*$scale); + } + + if (!empty($options['title'])) $title = ' title="'.attribute_escape($options['title']).'"'; + $alt = attribute_escape(!empty($img->properties['alt'])?$img->properties['alt']:$this->name); + return ''.$alt.''; break; + } + break; + case "hasimages": + case "has-images": + if (empty($options['type'])) $options['type'] = "thumbnail"; + if (empty($this->images)) $this->load_data(array('images')); + if (!empty($this->imagesets[$options['type']])) { + $this->imageset = &$this->imagesets[$options['type']]; + return true; + } else return false; + break; + case "images": + if (!$this->imageset) return false; + if (!$this->imageloop) { + reset($this->imageset); + $this->imageloop = true; + } else next($this->imageset); + + if (current($this->imageset)) return true; + else { + $this->imageloop = false; + $this->imageset = false; + return false; + } + break; + case "image": + $img = current($this->imageset); + if (isset($options['property'])) { + switch (strtolower($options['property'])) { + case "url": return $img->uri; + case "width": return $img->properties['width']; + case "height": return $img->properties['height']; + case "title": return attribute_escape($img->properties['title']); + case "alt": return attribute_escape($img->properties['alt']); + default: return $img->id; + } + } + if (!isset($options['class'])) $options['class'] = false; + if (!empty($options['class'])) $options['class'] = ' class="'.$options['class'].'"'; + + $title = !empty($img->properties['title'])?' title="'.attribute_escape($img->properties['title']).'"':''; + + $width = (isset($options['width']))?$options['width']:$img->properties['width']; + $height = (isset($options['height']))?$options['height']:$img->properties['height']; + + if (isset($options['width']) && !isset($options['height'])) { + $scale = $width/$img->properties['width']; + $height = round($img->properties['height']*$scale); + } + if (isset($options['height']) && !isset($options['width'])) { + $scale = $height/$img->properties['height']; + $width = round($img->properties['width']*$scale); + } + + if (!empty($options['title'])) $title = ' title="'.attribute_escape($options['title']).'"'; + $alt = attribute_escape(!empty($img->properties['alt'])?$img->properties['alt']:$this->name); + + $string = ""; + if (!isset($options['zoomfx'])) $options['zoomfx'] = "shopp-thickbox"; + if (!empty($options['zoom'])) $string .= ''; + $string .= ''.$alt.''; + if (!empty($options['zoom'])) $string .= ""; + return $string; + break; + case "gallery": + if (empty($this->images)) $this->load_data(array('images')); + if (!isset($options['zoomfx'])) $options['zoomfx'] = "shopp-thickbox"; + if (!isset($options['preview'])) $options['preview'] = "click"; + + $previews = '
      '; + $firstPreview = true; + if (!empty($this->imagesets['small'])) { + foreach ($this->imagesets['small'] as $img) { + if ($firstPreview) { + $previews .= '
    • '; + $previews .= ''.$img->datatype.''; + $previews .= '
    • '; + } + $title = !empty($img->properties['title'])?' title="'.attribute_escape($img->properties['title']).'"':''; + $alt = attribute_escape(!empty($img->properties['alt'])?$img->properties['alt']:$img->name); + $rel = (isset($options['rel']) && $options['rel'])?' rel="product_'.$this->id.'_gallery"':''; + + $previews .= '
    • '; + $previews .= ''; + $previews .= ''.$alt.''; + $previews .= ''; + $previews .= '
    • '; + $firstPreview = false; + } + } + $previews .= '
    '; + + $thumbs = ""; + if (isset($this->imagesets['thumbnail']) && count($this->imagesets['thumbnail']) > 1) { + $thumbsize = 32; + if (isset($options['thumbsize'])) $thumbsize = $options['thumbsize']; + $thumbwidth = $thumbsize; + $thumbheight = $thumbsize; + if (isset($options['thumbwidth'])) $thumbwidth = $options['thumbwidth']; + if (isset($options['thumbheight'])) $thumbheight = $options['thumbheight']; + + $firstThumb = true; + $thumbs = '
      '; + foreach ($this->imagesets['thumbnail'] as $img) { + if (isset($options['thumbwidth']) && !isset($options['thumbheight'])) { + $scale = $thumbwidth/$img->properties['width']; + $thumbheight = round($img->properties['height']*$scale); + } + + if (isset($options['thumbheight']) && !isset($options['thumbwidth'])) { + $scale = $thumbheight/$img->properties['height']; + $thumbwidth = round($img->properties['width']*$scale); + } + + $title = !empty($img->properties['title'])?' title="'.attribute_escape($img->properties['title']).'"':''; + $alt = attribute_escape(!empty($img->properties['alt'])?$img->properties['alt']:$img->name); + + $thumbs .= '
    • '; + $thumbs .= ''.$alt.''; + $thumbs .= '
    • '; + $firstThumb = false; + } + $thumbs .= '
    '; + } + + $result = ''; + $result .= ''; + return $result; + break; + case "has-categories": + if (empty($this->categories)) $this->load_data(array('categories')); + if (count($this->categories) > 0) return true; else return false; break; + case "categories": + if (!$this->categoryloop) { + reset($this->categories); + $this->categoryloop = true; + } else next($this->categories); + + if (current($this->categories)) return true; + else { + $this->categoryloop = false; + return false; + } + break; + case "in-category": + if (empty($this->categories)) $this->load_data(array('categories')); + if (isset($options['id'])) $field = "id"; + if (isset($options['name'])) $field = "name"; + if (isset($options['slug'])) $field = "slug"; + foreach ($this->categories as $category) + if ($category->{$field} == $options[$field]) return true; + return false; + case "category": + $category = current($this->categories); + if (isset($options['show'])) { + if ($options['show'] == "id") return $category->id; + if ($options['show'] == "slug") return $category->slug; + } + return $category->name; + break; + case "has-tags": + if (empty($this->tags)) $this->load_data(array('tags')); + if (count($this->tags) > 0) return true; else return false; break; + case "tags": + if (!$this->tagloop) { + reset($this->tags); + $this->tagloop = true; + } else next($this->tags); + + if (current($this->tags)) return true; + else { + $this->tagloop = false; + return false; + } + break; + case "tagged": + if (empty($this->tags)) $this->load_data(array('tags')); + if (isset($options['id'])) $field = "id"; + if (isset($options['name'])) $field = "name"; + foreach ($this->tags as $tag) + if ($tag->{$field} == $options[$field]) return true; + return false; + case "tag": + $tag = current($this->tags); + if (isset($options['show'])) { + if ($options['show'] == "id") return $tag->id; + } + return $tag->name; + break; + case "has-specs": + if (empty($this->specs)) $this->load_data(array('specs')); + if (count($this->specs) > 0) { + $this->merge_specs(); + return true; + } else return false; break; + case "specs": + if (!$this->specloop) { + reset($this->specs); + $this->specloop = true; + } else next($this->specs); + + if (current($this->specs)) return true; + else { + $this->specloop = false; + return false; + } + break; + case "spec": + $string = ""; + $separator = ": "; + $delimiter = ", "; + if (isset($options['separator'])) $separator = $options['separator']; + if (isset($options['delimiter'])) $separator = $options['delimiter']; + + $spec = current($this->specs); + if (is_array($spec->content)) $spec->content = join($delimiter,$spec->content); + + if (isset($options['name']) + && !empty($options['name']) + && isset($this->specskey[$options['name']])) { + $spec = $this->specskey[$options['name']]; + if (is_array($spec)) { + if (isset($options['index'])) { + foreach ($spec as $index => $entry) + if ($index+1 == $options['index']) + $content = $entry->content; + } else { + foreach ($spec as $entry) $contents[] = $entry->content; + $content = join($delimiter,$contents); + } + } else $content = $spec->content; + $string = apply_filters('shopp_product_spec',$content); + return $string; + } + + if (isset($options['name']) && isset($options['content'])) + $string = "{$spec->name}{$separator}".apply_filters('shopp_product_spec',$spec->content); + elseif (isset($options['name'])) $string = $spec->name; + elseif (isset($options['content'])) $string = apply_filters('shopp_product_spec',$spec->content); + else $string = "{$spec->name}{$separator}".apply_filters('shopp_product_spec',$spec->content); + return $string; + break; + case "has-variations": + return ($this->variations == "on" && !empty($this->options)); break; + case "variations": + + $string = ""; + + if (!isset($options['mode'])) { + if (!$this->priceloop) { + reset($this->prices); + $this->priceloop = true; + } else next($this->prices); + $thisprice = current($this->prices); + + if ($thisprice && $thisprice->type == "N/A") + next($this->prices); + + if (current($this->prices)) return true; + else { + $this->priceloop = false; + return false; + } + return true; + } + + if ($this->outofstock) return false; // Completely out of stock, hide menus + if (!isset($options['taxes'])) $options['taxes'] = null; + + $defaults = array( + 'defaults' => '', + 'disabled' => 'show', + 'before_menu' => '', + 'after_menu' => '' + ); + + $options = array_merge($defaults,$options); + + if (!isset($options['label'])) $options['label'] = "on"; + if (!isset($options['required'])) $options['required'] = __('You must select the options for this item before you can add it to your shopping cart.','Shopp'); + if ($options['mode'] == "single") { + if (!empty($options['before_menu'])) $string .= $options['before_menu']."\n"; + if (value_is_true($options['label'])) $string .= ' '."\n"; + + $string .= ''; + if (!empty($options['after_menu'])) $string .= $options['after_menu']."\n"; + + } else { + $taxrate = shopp_taxrate($options['taxes'],true); + ob_start(); + ?> + + options['variations'])) { + foreach ($this->options['variations'] as $id => $menu) { + if (!empty($options['before_menu'])) $string .= $options['before_menu']."\n"; + if (value_is_true($options['label'])) $string .= ' '."\n"; + + $string .= ''; + if (!empty($options['after_menu'])) $string .= $options['after_menu']."\n"; + } + } else { + foreach ($this->options as $id => $menu) { + if (!empty($options['before_menu'])) $string .= $options['before_menu']."\n"; + if (value_is_true($options['label'])) $string .= ' '."\n"; + $category_class = isset($Shopp->Category->slug)?'category-'.$Shopp->Category->slug:''; + $string .= ''; + if (!empty($options['after_menu'])) $string .= $options['after_menu']."\n"; + } + } + + } + + return $string; + break; + case "variation": + $variation = current($this->prices); + if (!isset($options['taxes'])) $options['taxes'] = null; + $taxrate = shopp_taxrate($options['taxes'],$variation->tax); + + $weightunit = (isset($options['units']) && !value_is_true($options['units']) ) ? false : $Shopp->Settings->get('weight_unit'); + + $string = ''; + if (array_key_exists('id',$options)) $string .= $variation->id; + if (array_key_exists('label',$options)) $string .= $variation->label; + if (array_key_exists('type',$options)) $string .= $variation->type; + if (array_key_exists('sku',$options)) $string .= $variation->sku; + if (array_key_exists('price',$options)) $string .= money($variation->price+($variation->price*$taxrate)); + if (array_key_exists('saleprice',$options)) $string .= money($variation->saleprice+($variation->saleprice*$taxrate)); + if (array_key_exists('stock',$options)) $string .= $variation->stock; + if (array_key_exists('weight',$options)) $string .= round($variation->weight, 3) . ($weightunit ? " $weightunit" : false); + if (array_key_exists('shipfee',$options)) $string .= money(floatvalue($variation->shipfee)); + if (array_key_exists('sale',$options)) return ($variation->sale == "on"); + if (array_key_exists('shipping',$options)) return ($variation->shipping == "on"); + if (array_key_exists('tax',$options)) return ($variation->tax == "on"); + if (array_key_exists('inventory',$options)) return ($variation->inventory == "on"); + return $string; + break; + case "has-addons": + if (isset($this->options['addons'])) return true; else return false; break; + break; + case "donation": + case "amount": + case "quantity": + if ($this->outofstock) return false; + if (!isset($options['value'])) $options['value'] = 1; + if (!isset($options['input'])) $options['input'] = "text"; + if (!isset($options['labelpos'])) $options['labelpos'] = "before"; + if (!isset($options['label'])) $label =""; + else $label = ''; + + $result = ""; + if ($options['labelpos'] == "before") $result .= "$label "; + + if (!$this->priceloop) reset($this->prices); + $variation = current($this->prices); + + if (isset($options['input']) && $options['input'] == "menu") { + if (!isset($options['options'])) + $values = "1-15,20,25,30,40,50,75,100"; + else $values = $options['options']; + if ($this->inventory && $this->pricerange['max']['stock'] == 0) return ""; + + if (strpos($values,",") !== false) $values = explode(",",$values); + else $values = array($values); + $qtys = array(); + foreach ($values as $value) { + if (strpos($value,"-") !== false) { + $value = explode("-",$value); + if ($value[0] >= $value[1]) $qtys[] = $value[0]; + else for ($i = $value[0]; $i < $value[1]+1; $i++) $qtys[] = $i; + } else $qtys[] = $value; + } + $result .= ''; + if ($options['labelpos'] == "after") $result .= " $label"; + return $result; + } + if (valid_input($options['input'])) { + if (!isset($options['size'])) $options['size'] = 3; + if ($variation->type == "Donation" && $variation->donation['var'] == "on") { + if ($variation->donation['min']) $options['value'] = $variation->price; + $options['class'] .= " currency"; + } + $result = ''; + } + if ($options['labelpos'] == "after") $result .= " $label"; + return $result; + break; + case "input": + if (!isset($options['type']) || + ($options['type'] != "menu" && $options['type'] != "textarea" && !valid_input($options['type']))) $options['type'] = "text"; + if (!isset($options['name'])) return ""; + if ($options['type'] == "menu") { + $result = ''; + } elseif ($options['type'] == "textarea") { + if (isset($options['cols'])) $cols = ' cols="'.$options['cols'].'"'; + if (isset($options['rows'])) $rows = ' rows="'.$options['rows'].'"'; + $result .= ''; + } else { + $result = ''; + } + + return $result; + break; + case "outofstock": + if ($this->outofstock) { + $label = isset($options['label'])?$options['label']:$Shopp->Settings->get('outofstock_text'); + $string = ''.$label.''; + return $string; + } else return false; + break; + case "buynow": + if (!isset($options['value'])) $options['value'] = __("Buy Now","Shopp"); + case "addtocart": + if (!isset($options['class'])) $options['class'] = "addtocart"; + else $options['class'] .= " addtocart"; + if (!isset($options['value'])) $options['value'] = __("Add to Cart","Shopp"); + $string = ""; + + if ($this->outofstock) { + $string .= ''.$Shopp->Settings->get('outofstock_text').''; + return $string; + } + + $string .= ''; + + if (!empty($this->prices[0]) && $this->prices[0]->type != "N/A") + $string .= ''; + + if (!empty($Shopp->Category)) { + if (SHOPP_PERMALINKS) + $string .= ''; + else + $string .= ''; + } + + $string .= ''; + if (isset($options['ajax'])) { + $options['class'] .= " ajax"; + $string .= ''; + $string .= ''; + } else { + $string .= ''; + } + + return $string; + } + + + } + +} // end Product class + +?> \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/model/Promotion.php b/blog/wp-content/plugins/shopp/core/model/Promotion.php new file mode 100644 index 0000000..732457d --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/model/Promotion.php @@ -0,0 +1,177 @@ + "text", + "Category" => "text", + "Variation" => "text", + "Price" => "price", + "Sale price" => "price", + "Type" => "text", + "In stock" => "text", + "Item name" => "text", + "Item quantity" => "text", + "Item amount" => "price", + "Total quantity" => "text", + "Shipping amount" => "price", + "Subtotal amount" => "price", + "Promo code" => "text" + ); + + function Promotion ($id=false) { + $this->init(self::$table); + if ($this->load($id)) return true; + else return false; + } + + function build_discounts () { + $db = DB::get(); + + $discount_table = DatabaseObject::tablename(Discount::$table); + $product_table = DatabaseObject::tablename(Product::$table); + $price_table = DatabaseObject::tablename(Price::$table); + $catalog_table = DatabaseObject::tablename(Catalog::$table); + $category_table = DatabaseObject::tablename(Category::$table); + + $where = ""; + // Go through each rule to construct an SQL query + // that gets all applicable product & price ids + if (!empty($this->rules) && is_array($this->rules)) { + foreach ($this->rules as $rule) { + + if ($this->values[$rule['property']] == "price") + $value = floatnum($rule['value']); + else $value = "'".$rule['value']."'"; + + switch($rule['logic']) { + case "Is equal to": $match = "=$value"; break; + case "Is not equal to": $match = "!=$value"; break; + case "Contains": $match = " LIKE '%$value%'"; break; + case "Does not contain": $match = " NOT LIKE '%$value%'"; break; + case "Begins with": $match = " LIKE '$value%'"; break; + case "Ends with": $match = " LIKE '%$value'"; break; + case "Is greater than": $match = "> $value"; break; + case "Is greater than or equal to": $match = ">= $value"; break; + case "Is less than": $match = "< $value"; break; + case "Is less than or equal to": $match = "<= $value"; break; + } + + $where .= "AND "; + switch($rule['property']) { + case "Name": $where .= "p.name$match"; break; + case "Category": $where .= "cat.name$match"; break; + case "Variation": $where .= "prc.label$match"; break; + case "Price": $where .= "prc.price$match"; break; + case "Sale price": $where .= "(prc.onsale='on' AND prc.saleprice$match)"; break; + case "Type": $where .= "prc.type$match"; break; + case "In stock": $where .= "(prc.inventory='on' AND prc.stock$match)"; break; + } + + } + + } + + $type = ($this->type == "Item")?'catalog':'cart'; + // Delete previous discount records + $db->query("DELETE FROM $discount_table WHERE promo=$this->id"); + $query = "INSERT INTO $discount_table (promo,product,price) + SELECT '$this->id' as promo,p.id AS product,prc.id AS price + FROM $product_table as p + LEFT JOIN $price_table AS prc ON prc.product=p.id + LEFT JOIN $catalog_table AS clog ON clog.product=p.id + LEFT JOIN $category_table AS cat ON clog.category=cat.id + WHERE TRUE $where + GROUP BY prc.id"; + + $db->query($query); + + } + + /** + * match_rule () + * Determines if the value of a given subject matches the rule based + * on the specified operation */ + function match_rule ($subject,$op,$value,$property=false) { + switch($op) { + // String or Numeric operations + case "Is equal to": + if($property && $this->values[$property] == 'price'){ + return ( floatvalue($subject) != 0 + && floatvalue($value) != 0 + && floatvalue($subject) == floatvalue($value)); + } else { + return ($subject === $value); + } + break; + case "Is not equal to": + return ($subject !== $value + || (floatvalue($subject) != 0 + && floatvalue($value) != 0 + && floatvalue($subject) != floatvalue($value))); + break; + + // String operations + case "Contains": return (stripos($subject,$value) !== false); break; + case "Does not contain": return (stripos($subject,$value) === false); break; + case "Begins with": return (stripos($subject,$value) === 0); break; + case "Ends with": return (stripos($subject,$value) === strlen($subject) - strlen($value)); break; + + // Numeric operations + case "Is greater than": + return (floatvalue($subject,false) > floatvalue($value,false)); + break; + case "Is greater than or equal to": + return (floatvalue($subject,false) >= floatvalue($value,false)); + break; + case "Is less than": + return (floatvalue($subject,false) < floatvalue($value,false)); + break; + case "Is less than or equal to": + return (floatvalue($subject,false) <= floatvalue($value,false)); + break; + } + + return false; + } + +} // end Promotion class + + +// Discount table provides discount index for faster, efficient discount lookups +class Discount extends DatabaseObject { + static $table = "discount"; + + function Promotion ($id=false) { + $this->init(self::$table); + if ($this->load($id)) return true; + else return false; + } + + function delete () { + $db = DB::get(); + // Delete record + $id = $this->{$this->_key}; + + // Delete related discounts + $discount_table = DatabaseObject::tablename(Discount::$table); + if (!empty($id)) $db->query("DELETE LOW_PRIORITY FROM $discount_table WHERE promo='$id'"); + + if (!empty($id)) $db->query("DELETE FROM $this->_table WHERE $this->_key='$id'"); + else return false; + } + +} // end Discount class + + +?> \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/model/Purchase.php b/blog/wp-content/plugins/shopp/core/model/Purchase.php new file mode 100644 index 0000000..481d663 --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/model/Purchase.php @@ -0,0 +1,561 @@ +init(self::$table); + if (!$id) return true; + if ($this->load($id,$key)) return true; + else return false; + } + + function load_purchased () { + $db = DB::get(); + + $table = DatabaseObject::tablename(Purchased::$table); + if (empty($this->id)) return false; + $this->purchased = $db->query("SELECT * FROM $table WHERE purchase=$this->id",AS_ARRAY); + foreach ($this->purchased as &$purchase) $purchase->data = unserialize($purchase->data); + return true; + } + + function notification ($addressee,$address,$subject,$template="order.html",$receipt="receipt.php") { + global $Shopp; + global $is_IIS; + + $template = trailingslashit(SHOPP_TEMPLATES).$template; + if (!file_exists($template)) + return new ShoppError(__('A purchase notification could not be sent because the template for it does not exist.','purchase_notification_template',SHOPP_ADMIN_ERR)); + + // // Send the e-mail receipt + $email = array(); + $email['from'] = '"'.get_bloginfo("name").'"'; + if ($Shopp->Settings->get('merchant_email')) + $email['from'] .= ' <'.$Shopp->Settings->get('merchant_email').'>'; + if($is_IIS) $email['to'] = $address; + else $email['to'] = '"'.html_entity_decode($addressee,ENT_QUOTES).'" <'.$address.'>'; + $email['subject'] = $subject; + $email['receipt'] = $Shopp->Flow->order_receipt($receipt); + $email['url'] = get_bloginfo('siteurl'); + $email['sitename'] = get_bloginfo('name'); + $email['orderid'] = $this->id; + + $email = apply_filters('shopp_email_receipt_data',$email); + + if (shopp_email($template,$email)) { + if (SHOPP_DEBUG) new ShoppError('A purchase notification was sent to "'.$addressee.'" <'.$address.'>',false,SHOPP_DEBUG_ERR); + return true; + } + + if (SHOPP_DEBUG) new ShoppError('A purchase notification FAILED to be sent to "'.$addressee.'" <'.$address.'>',false,SHOPP_DEBUG_ERR); + return false; + } + + function copydata ($Object,$prefix="") { + $ignores = array("_datatypes","_table","_key","_lists","id","created","modified"); + foreach(get_object_vars($Object) as $property => $value) { + $property = $prefix.$property; + if (property_exists($this,$property) && + !in_array($property,$ignores)) + $this->{$property} = $value; + } + } + + function exportcolumns () { + $prefix = "o."; + return array( + $prefix.'id' => __('Order ID','Shopp'), + $prefix.'ip' => __('Customer\'s IP Address','Shopp'), + $prefix.'firstname' => __('Customer\'s First Name','Shopp'), + $prefix.'lastname' => __('Customer\'s Last Name','Shopp'), + $prefix.'email' => __('Customer\'s Email Address','Shopp'), + $prefix.'phone' => __('Customer\'s Phone Number','Shopp'), + $prefix.'company' => __('Customer\'s Company','Shopp'), + $prefix.'card' => __('Credit Card Number','Shopp'), + $prefix.'cardtype' => __('Credit Card Type','Shopp'), + $prefix.'cardexpires' => __('Credit Card Expiration Date','Shopp'), + $prefix.'cardholder' => __('Credit Card Holder\'s Name','Shopp'), + $prefix.'address' => __('Billing Street Address','Shopp'), + $prefix.'xaddress' => __('Billing Street Address 2','Shopp'), + $prefix.'city' => __('Billing City','Shopp'), + $prefix.'state' => __('Billing State/Province','Shopp'), + $prefix.'country' => __('Billing Country','Shopp'), + $prefix.'postcode' => __('Billing Postal Code','Shopp'), + $prefix.'shipaddress' => __('Shipping Street Address','Shopp'), + $prefix.'shipxaddress' => __('Shipping Street Address 2','Shopp'), + $prefix.'shipcity' => __('Shipping City','Shopp'), + $prefix.'shipstate' => __('Shipping State/Province','Shopp'), + $prefix.'shipcountry' => __('Shipping Country','Shopp'), + $prefix.'shippostcode' => __('Shipping Postal Code','Shopp'), + $prefix.'shipmethod' => __('Shipping Method','Shopp'), + $prefix.'promos' => __('Promotions Applied','Shopp'), + $prefix.'subtotal' => __('Order Subtotal','Shopp'), + $prefix.'discount' => __('Order Discount','Shopp'), + $prefix.'freight' => __('Order Shipping Fees','Shopp'), + $prefix.'tax' => __('Order Taxes','Shopp'), + $prefix.'total' => __('Order Total','Shopp'), + $prefix.'fees' => __('Transaction Fees','Shopp'), + $prefix.'transactionid' => __('Transaction ID','Shopp'), + $prefix.'transtatus' => __('Transaction Status','Shopp'), + $prefix.'gateway' => __('Payment Gateway','Shopp'), + $prefix.'status' => __('Order Status','Shopp'), + $prefix.'data' => __('Order Data','Shopp'), + $prefix.'created' => __('Order Date','Shopp'), + $prefix.'modified' => __('Order Last Updated','Shopp') + ); + } + + function tag ($property,$options=array()) { + global $Shopp; + + + if ($property == "item-unitprice" || $property == "item-total") + $taxrate = shopp_taxrate($options['taxes']); + + // Return strings with no options + switch ($property) { + case "url": return $Shopp->link('cart'); break; + case "id": return $this->id; break; + case "date": + if (empty($options['format'])) $options['format'] = get_option('date_format'); + return _d($options['format'],((is_int($this->created))?$this->created:mktimestamp($this->created))); + break; + case "card": return (!empty($this->card))?sprintf("%'X16d",$this->card):''; break; + case "cardtype": return $this->cardtype; break; + case "transactionid": return $this->transactionid; break; + case "firstname": return $this->firstname; break; + case "lastname": return $this->lastname; break; + case "company": return $this->company; break; + case "email": return $this->email; break; + case "phone": return $this->phone; break; + case "address": return $this->address; break; + case "xaddress": return $this->xaddress; break; + case "city": return $this->city; break; + case "state": + if (strlen($this->state > 2)) return $this->state; + $regions = $Shopp->Settings->get('zones'); + $states = $regions[$this->country]; + return $states[$this->state]; + break; + case "postcode": return $this->postcode; break; + case "country": + $countries = $Shopp->Settings->get('target_markets'); + return $countries[$this->country]; break; + case "shipaddress": return $this->shipaddress; break; + case "shipxaddress": return $this->shipxaddress; break; + case "shipcity": return $this->shipcity; break; + case "shipstate": + if (strlen($this->shipstate > 2)) return $this->shipstate; + $regions = $Shopp->Settings->get('zones'); + $states = $regions[$this->country]; + return $states[$this->shipstate]; + break; + case "shippostcode": return $this->shippostcode; break; + case "shipcountry": + $countries = $Shopp->Settings->get('target_markets'); + return $countries[$this->shipcountry]; break; + case "shipmethod": return $this->shipmethod; break; + case "totalitems": return count($this->purchased); break; + case "hasitems": if (count($this->purchased) > 0) return true; else return false; break; + case "items": + if (!$this->looping) { + reset($this->purchased); + $this->looping = true; + } else next($this->purchased); + + if (current($this->purchased)) return true; + else { + $this->looping = false; + reset($this->purchased); + return false; + } + case "item-id": + $item = current($this->purchased); + return $item->id; break; + case "item-product": + $item = current($this->purchased); + return $item->product; break; + case "item-price": + $item = current($this->purchased); + return $item->price; break; + case "item-name": + $item = current($this->purchased); + return $item->name; break; + case "item-description": + $item = current($this->purchased); + return $item->description; break; + case "item-options": + $item = current($this->purchased); + return (!empty($item->optionlabel))?$options['before'].$item->optionlabel.$options['after']:''; break; + case "item-sku": + $item = current($this->purchased); + return $item->sku; break; + case "item-download": + $item = current($this->purchased); + if (empty($item->download)) return ""; + if (!isset($options['label'])) $options['label'] = __('Download','Shopp'); + $classes = ""; + if (isset($options['class'])) $classes = ' class="'.$options['class'].'"'; + if (SHOPP_PERMALINKS) $url = $Shopp->shopuri."download/".$item->dkey; + else $url = add_query_arg('shopp_download',$item->dkey,$Shopp->link('account')); + return ''.$options['label'].''; break; + case "item-quantity": + $item = current($this->purchased); + return $item->quantity; break; + case "item-unitprice": + $item = current($this->purchased); + return money($item->unitprice+($item->unitprice*$taxrate)); break; + case "item-total": + $item = current($this->purchased); + return money($item->total+($item->total*$taxrate)); break; + case "item-has-inputs": + case "item-hasinputs": + $item = current($this->purchased); + return (count($item->data) > 0); break; + case "item-inputs": + $item = current($this->purchased); + if (!$this->itemdataloop) { + reset($item->data); + $this->itemdataloop = true; + } else next($item->data); + + if (current($item->data)) return true; + else { + $this->itemdataloop = false; + return false; + } + break; + case "item-input": + $item = current($this->purchased); + $data = current($item->data); + $name = key($item->data); + if (isset($options['name'])) return $name; + return $data; + break; + case "item-inputs-list": + case "item-inputslist": + case "item-inputs-list": + case "iteminputslist": + $item = current($this->purchased); + if (empty($item->data)) return false; + $before = ""; $after = ""; $classes = ""; $excludes = array(); + if (!empty($options['class'])) $classes = ' class="'.$options['class'].'"'; + if (!empty($options['exclude'])) $excludes = explode(",",$options['exclude']); + if (!empty($options['before'])) $before = $options['before']; + if (!empty($options['after'])) $after = $options['after']; + + $result .= $before.''; + foreach ($item->data as $name => $data) { + if (in_array($name,$excludes)) continue; + $result .= '
  • '.$name.': '.$data.'
  • '; + } + $result .= ''.$after; + return $result; + break; + case "has-data": + case "hasdata": return (is_array($this->data) && count($this->data) > 0); break; + case "orderdata": + if (!$this->dataloop) { + reset($this->data); + $this->dataloop = true; + } else next($this->data); + + if (current($this->data) !== false) return true; + else { + $this->dataloop = false; + return false; + } + break; + case "data": + if (!is_array($this->data)) return false; + $data = current($this->data); + $name = key($this->data); + if (isset($options['name'])) return $name; + return $data; + break; + case "has-promo": + case "haspromo": + if (empty($options['name'])) return false; + return (in_array($options['name'],$this->promos)); + break; + case "subtotal": return money($this->subtotal); break; + case "hasfreight": return (!empty($this->shipmethod) || $this->freight > 0); + case "freight": return money($this->freight); break; + case "hasdiscount": return ($this->discount > 0); + case "discount": return money($this->discount); break; + case "hastax": return ($this->tax > 0)?true:false; + case "tax": return money($this->tax); break; + case "total": return money($this->total); break; + case "status": + $labels = $Shopp->Settings->get('order_status'); + if (empty($labels)) $labels = array(''); + return $labels[$this->status]; + break; + + } + } + +} // end Purchase class + +class PurchasesExport { + var $sitename = ""; + var $headings = false; + var $data = false; + var $defined = array(); + var $purchase_cols = array(); + var $purchased_cols = array(); + var $selected = array(); + var $recordstart = true; + var $content_type = "text/plain"; + var $extension = "txt"; + var $date_format = 'F j, Y'; + var $time_format = 'g:i:s a'; + + function PurchasesExport () { + global $Shopp; + + $this->purchase_cols = Purchase::exportcolumns(); + $this->purchased_cols = Purchased::exportcolumns(); + $this->defined = array_merge($this->purchase_cols,$this->purchased_cols); + + $this->sitename = get_bloginfo('name'); + $this->headings = ($Shopp->Settings->get('purchaselog_headers') == "on"); + $this->selected = $Shopp->Settings->get('purchaselog_columns'); + $this->date_format = get_option('date_format'); + $this->time_format = get_option('time_format'); + $Shopp->Settings->save('purchaselog_lastexport',mktime()); + } + + function query ($request=array()) { + $db =& DB::get(); + if (empty($request)) $request = $_GET; + + if (!empty($request['start'])) { + list($month,$day,$year) = explode("/",$request['start']); + $starts = mktime(0,0,0,$month,$day,$year); + } + + if (!empty($request['end'])) { + list($month,$day,$year) = explode("/",$request['end']); + $ends = mktime(0,0,0,$month,$day,$year); + } + + $where = "WHERE o.id IS NOT NULL AND p.id IS NOT NULL "; + if (isset($request['status'])) $where .= "AND status='{$request['status']}'"; + if (isset($request['s']) && !empty($request['s'])) $where .= " AND (id='{$request['s']}' OR firstname LIKE '%{$request['s']}%' OR lastname LIKE '%{$request['s']}%' OR CONCAT(firstname,' ',lastname) LIKE '%{$request['s']}%' OR transactionid LIKE '%{$request['s']}%')"; + if (!empty($request['start']) && !empty($request['end'])) $where .= " AND (UNIX_TIMESTAMP(o.created) >= $starts AND UNIX_TIMESTAMP(o.created) <= $ends)"; + + $purchasetable = DatabaseObject::tablename(Purchase::$table); + $purchasedtable = DatabaseObject::tablename(Purchased::$table); + + $c = 0; $columns = array(); + foreach ($this->selected as $column) $columns[] = "$column AS col".$c++; + $query = "SELECT ".join(",",$columns)." FROM $purchasedtable AS p LEFT JOIN $purchasetable AS o ON o.id=p.purchase $where ORDER BY o.created ASC"; + $this->data = $db->query($query,AS_ARRAY); + } + + // Implement for exporting all the data + function output () { + if (!$this->data) $this->query(); + if (!$this->data) return false; + + header("Content-type: $this->content_type; charset=UTF-8"); + header("Content-Disposition: attachment; filename=\"$this->sitename Purchase Log.$this->extension\""); + header("Content-Description: Delivered by WordPress/Shopp ".SHOPP_VERSION); + header("Cache-Control: maxage=1"); + header("Pragma: public"); + + $this->begin(); + if ($this->headings) $this->heading(); + $this->records(); + $this->end(); + } + + function begin() {} + + function heading () { + foreach ($this->selected as $name) + $this->export($this->defined[$name]); + $this->record(); + } + + function records () { + foreach ($this->data as $key => $record) { + foreach(get_object_vars($record) as $column) + $this->export($this->parse($column)); + $this->record(); + } + } + + function parse ($column) { + if (preg_match("/^[sibNaO](?:\:.+?\{.*\}$|\:.+;$|;$)/",$column)) { + $list = unserialize($column); + $column = ""; + foreach ($list as $name => $value) + $column .= (empty($column)?"":";")."$name:$value"; + } + return $column; + } + + function end() {} + + // Implement for exporting a single value + function export ($value) { + echo ($this->recordstart?"":"\t").$value; + $this->recordstart = false; + } + + function record () { + echo "\n"; + $this->recordstart = true; + } + + function settings () {} + +} + +class PurchasesTabExport extends PurchasesExport { + function PurchasesTabExport () { + parent::PurchasesExport(); + $this->output(); + } +} + +class PurchasesCSVExport extends PurchasesExport { + function PurchasesCSVExport () { + parent::PurchasesExport(); + $this->content_type = "text/csv"; + $this->extension = "csv"; + $this->output(); + } + + function export ($value) { + $value = str_replace('"','""',$value); + if (preg_match('/^\s|[,"\n\r]|\s$/',$value)) $value = '"'.$value.'"'; + echo ($this->recordstart?"":",").$value; + $this->recordstart = false; + } + +} + +class PurchasesXLSExport extends PurchasesExport { + function PurchasesXLSExport () { + parent::PurchasesExport(); + $this->content_type = "application/vnd.ms-excel"; + $this->extension = "xls"; + $this->c = 0; $this->r = 0; + $this->output(); + } + + function begin () { + echo pack("ssssss", 0x809, 0x8, 0x0, 0x10, 0x0, 0x0); + } + + function end () { + echo pack("ss", 0x0A, 0x00); + } + + function export ($value) { + if (preg_match('/^[\d\.]+$/',$value)) { + echo pack("sssss", 0x203, 14, $this->r, $this->c, 0x0); + echo pack("d", $value); + } else { + $l = strlen($value); + echo pack("ssssss", 0x204, 8+$l, $this->r, $this->c, 0x0, $l); + echo $value; + } + $this->c++; + } + + function record () { + $this->c = 0; + $this->r++; + } +} + +class PurchasesIIFExport extends PurchasesExport { + function PurchasesIIFExport () { + global $Shopp; + parent::PurchasesExport(); + $this->content_type = "application/qbooks"; + $this->extension = "iif"; + $account = $Shopp->Settings->get('purchaselog_iifaccount'); + if (empty($account)) $account = "Merchant Account"; + $this->selected = array( + "'\nTRNS'", + "DATE_FORMAT(o.created,'\"%m/%d/%Y\"')", + "'\"$account\"'", + "CONCAT('\"',o.firstname,' ',o.lastname,'\"')", + "'\"Shopp Payment Received\"'", + "o.total-o.fees", + "''", + "'\nSPL'", + "DATE_FORMAT(o.created,'\"%m/%d/%Y\"')", + "'\"Other Income\"'", + "CONCAT('\"',o.firstname,' ',o.lastname,'\"')", + "o.total*-1", + "'\nSPL'", + "DATE_FORMAT(o.created,'\"%m/%d/%Y\"')", + "'\"Other Expenses\"'", + "'Fee'", + "o.fees", + "''", + "'\nENDTRNS'" + ); + $this->output(); + } + + function begin () { + echo "!TRNS\tDATE\tACCNT\tNAME\tCLASS\tAMOUNT\tMEMO\n!SPL\tDATE\tACCNT\tNAME\tAMOUNT\tMEMO\n!ENDTRNS"; + } + + function export ($value) { + echo (substr($value,0,1) != "\n")?"\t".$value:$value; + } + + function record () { } + + function settings () { + global $Shopp; + ?> + + + \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/model/Purchased.php b/blog/wp-content/plugins/shopp/core/model/Purchased.php new file mode 100644 index 0000000..4648a8d --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/model/Purchased.php @@ -0,0 +1,47 @@ +init(self::$table); + if ($this->load($id,$key)) return true; + else return false; + } + + function keygen() { + $message = $this->name.$this->purchase.$this->product.$this->price.$this->download; + $key = sha1($message); + if (empty($key)) $key = md5($message); + $this->dkey = $key; + do_action_ref_array('shopp_download_keygen',array(&$this)); + } + + function exportcolumns () { + $prefix = "p."; + return array( + $prefix.'id' => __('Line Item ID','Shopp'), + $prefix.'name' => __('Product Name','Shopp'), + $prefix.'optionlabel' => __('Product Variation Name','Shopp'), + $prefix.'description' => __('Product Description','Shopp'), + $prefix.'sku' => __('Product SKU','Shopp'), + $prefix.'quantity' => __('Product Quantity Purchased','Shopp'), + $prefix.'unitprice' => __('Product Unit Price','Shopp'), + $prefix.'total' => __('Product Total Price','Shopp'), + $prefix.'data' => __('Product Data','Shopp'), + $prefix.'downloads' => __('Product Downloads','Shopp') + ); + } + +} // end Purchased class + +?> \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/model/Settings.php b/blog/wp-content/plugins/shopp/core/model/Settings.php new file mode 100644 index 0000000..c74e3b7 --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/model/Settings.php @@ -0,0 +1,143 @@ +_table = $this->tablename(self::$table); + if (!$this->load()) { + if (!$this->init('setting')) { + $this->unavailable = true; + return false; + } + } + } + + /** + * Load all settings from the database */ + function load ($name="") { + $db = DB::get(); + if (!empty($name)) $results = $db->query("SELECT name,value FROM $this->_table WHERE name='$name'",AS_ARRAY,false); + else $results = $db->query("SELECT name,value FROM $this->_table WHERE autoload='on'",AS_ARRAY,false); + + if (!is_array($results) || sizeof($results) == 0) return false; + while(list($key,$entry) = each($results)) $settings[$entry->name] = $this->restore($entry->value); + + if (!empty($settings)) $this->registry = array_merge($this->registry,$settings); + return true; + } + + /** + * Add a new setting to the registry and store it in the database */ + function add ($name, $value,$autoload = true) { + $db = DB::get(); + $Setting = $this->setting(); + $Setting->name = $name; + $Setting->value = $db->clean($value); + $Setting->autoload = ($autoload)?'on':'off'; + + $data = $db->prepare($Setting); + $dataset = DatabaseObject::dataset($data); + if ($db->query("INSERT $this->_table SET $dataset")) + $this->registry[$name] = $this->restore($db->clean($value)); + else return false; + return true; + } + + /** + * Updates the setting in the registry and the database */ + function update ($name,$value) { + $db = DB::get(); + + if ($this->get($name) == $value) return true; + + $Setting = $this->setting(); + $Setting->name = $name; + $Setting->value = $db->clean($value); + unset($Setting->autoload); + $data = $db->prepare($Setting); // Prepare the data for db entry + $dataset = DatabaseObject::dataset($data); // Format the data in SQL + + if ($db->query("UPDATE $this->_table SET $dataset WHERE name='$Setting->name'")) + $this->registry[$name] = $this->restore($value); // Update the value in the registry + else return false; + return true; + } + + function save ($name,$value,$autoload=true) { + // Update or Insert as needed + if ($this->get($name) === false) $this->add($name,$value,$autoload); + else $this->update($name,$value); + } + + /** + * Remove a setting from the registry and the database */ + function delete ($name) { + $db = DB::get(); + unset($this->registry[$name]); + if (!$db->query("DELETE FROM $this->_table WHERE name='$name'")) return false; + return true; + } + + /** + * Get a specific setting from the registry */ + function get ($name) { + global $Shopp; + + $value = false; + if (isset($this->registry[$name])) { + return $this->registry[$name]; + } elseif ($this->load($name)) { + $value = $this->registry[$name]; + } + + // Return false and add an entry to the registry + // to avoid repeat database queries + if (!isset($this->registry[$name])) { + $this->registry[$name] = false; + return false; + } + + return $value; + } + + function restore ($value) { + if (!is_string($value)) return $value; + // Return unserialized, if serialized value + if (preg_match("/^[sibNaO](?:\:.+?\{.*\}$|\:.+;$|;$)/s",$value)) { + $restored = unserialize($value); + if (!empty($restored)) return $restored; + $restored = unserialize(stripslashes($value)); + if (!empty($restored)) return $restored; + } + return $value; + } + + /** + * Return a blank setting object */ + function setting () { + $setting->_datatypes = array("name" => "string", "value" => "string", "autoload" => "list", + "created" => "date", "modified" => "date"); + $setting->name = null; + $setting->value = null; + $setting->autoload = null; + $setting->created = null; + $setting->modified = null; + return $setting; + } + +} // END class Settings + +?> \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/model/ShipCalcs.php b/blog/wp-content/plugins/shopp/core/model/ShipCalcs.php new file mode 100644 index 0000000..95bcbba --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/model/ShipCalcs.php @@ -0,0 +1,95 @@ +path = $basepath.DIRECTORY_SEPARATOR."shipping"; + $lastscan = $Shopp->Settings->get('shipcalc_lastscan'); + $lastupdate = filemtime($this->path); + + $modfiles = array(); + if ($lastupdate > $lastscan) $modfiles = $this->scanmodules(); + else { + $modfiles = $Shopp->Settings->get('shipcalc_modules'); + if (empty($modfiles)) $modfiles = $this->scanmodules(); + } + + if (!empty($modfiles)) { + foreach ($modfiles as $ShipCalcClass => $file) { + if (!file_exists($this->path.$file)) continue; + include_once($this->path.$file); + $this->modules[$ShipCalcClass] = new $ShipCalcClass(); + $this->modules[$ShipCalcClass]->methods($this); + } + + if (count($this->modules) != count($modfiles)) + $modfiles = $this->scanmodules(); + + } + + } + + function readmeta ($modfile) { + $metadata = array(); + + $meta = get_filemeta($this->path.$modfile); + + if ($meta) { + $lines = explode("\n",substr($meta,1)); + foreach($lines as $line) { + preg_match("/^(?:[\s\*]*?\b([^@\*\/]*))/",$line,$match); + if (!empty($match[1])) $data[] = $match[1]; + preg_match("/^(?:[\s\*]*?@([^\*\/]+?)\s(.+))/",$line,$match); + if (!empty($match[1]) && !empty($match[2])) $tags[$match[1]] = $match[2]; + + } + $module = new stdClass(); + $module->file = $modfile; + $module->name = $data[0]; + $module->description = (!empty($data[1]))?$data[1]:""; + $module->tags = $tags; + return $module; + } + return false; + } + + function scanmodules ($path=false) { + global $Shopp; + if (!$path) $path = $this->path; + $modfilescan = array(); + find_files(".php",$path,$path,$modfilescan); + + if (empty($modfilescan)) return $modfilescan; + foreach ($modfilescan as $file) { + if (! is_readable($path.$file)) continue; + $ShipCalcClass = substr(basename($file),0,-4); + $modfiles[$ShipCalcClass] = $file; + } + + $Shopp->Settings->save('shipcalc_modules',addslashes(serialize($modfiles))); + $Shopp->Settings->save('shipcalc_lastscan',mktime()); + + return $modfiles; + } + + function ui () { + foreach ($this->modules as $ShipCalcClass => &$module) $module->ui(); + } + +} // end ShipCalcs class + +?> \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/model/Shipping.php b/blog/wp-content/plugins/shopp/core/model/Shipping.php new file mode 100644 index 0000000..8674897 --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/model/Shipping.php @@ -0,0 +1,80 @@ +init(self::$table); + if ($id && $this->load($id,$key)) return true; + else return false; + } + + function exportcolumns () { + $prefix = "s."; + return array( + $prefix.'address' => __('Shipping Street Address','Shopp'), + $prefix.'xaddress' => __('Shipping Street Address 2','Shopp'), + $prefix.'city' => __('Shipping City','Shopp'), + $prefix.'state' => __('Shipping State/Province','Shopp'), + $prefix.'country' => __('Shipping Country','Shopp'), + $prefix.'postcode' => __('Shipping Postal Code','Shopp'), + ); + } + + /** + * postarea() + * Determines the domestic area name from a + * U.S. zip code or Canadian postal code */ + function postarea () { + global $Shopp; + $code = $this->postcode; + $areas = $Shopp->Settings->get('areas'); + + // Skip if there are no areas for this country + if (!isset($areas[$this->country])) return false; + + // If no postcode is provided, return the first regional column + if (empty($this->postcode)) return key($areas[$this->country]); + + // Lookup US area name + if (preg_match("/\d{5}(\-\d{4})?/",$code)) { + + foreach ($areas['US'] as $name => $states) { + foreach ($states as $id => $coderange) { + for($i = 0; $i= (int)$coderange[$i] && $code <= (int)$coderange[$i+1]) { + $this->state = $id; + return $name; + } + } + } + } + } + + // Lookup Canadian area name + if (preg_match("/\w\d\w\s*\d\w\d/",$code)) { + + foreach ($areas['CA'] as $name => $provinces) { + foreach ($provinces as $id => $fsas) { + if (in_array(substr($code,0,1),$fsas)) return $name; + } + } + return $name; + + } + + return false; + } + +} // end Shipping class + +?> \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/model/Spec.php b/blog/wp-content/plugins/shopp/core/model/Spec.php new file mode 100644 index 0000000..877329d --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/model/Spec.php @@ -0,0 +1,23 @@ +init(self::$table); + if ($this->load($id)) return true; + else return false; + } + +} // end Spec class + +?> \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/model/Tag.php b/blog/wp-content/plugins/shopp/core/model/Tag.php new file mode 100644 index 0000000..28b9096 --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/model/Tag.php @@ -0,0 +1,23 @@ +init(self::$table); + if ($this->load($id,$key)) return true; + else return false; + } + +} // end Tag class + +?> \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/model/XMLdata.php b/blog/wp-content/plugins/shopp/core/model/XMLdata.php new file mode 100644 index 0000000..1217c61 --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/model/XMLdata.php @@ -0,0 +1,206 @@ +parse($data); + else $this->data = $data; + return true; + } + + /** + * parse() + * Parses a string of XML markup into an associative array */ + function parse (&$string) { + $parser = xml_parser_create(); + xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); + xml_parse_into_struct($parser, $string, $vals, $index); + xml_parser_free($parser); + + $data = array(); + $working = &$data; + foreach ($vals as $r) { + $t=$r['tag']; + if ($r['type'] == 'open') { + if (isset($working[$t])) { + if (isset($working[$t][0])) $working[$t][] = array(); + else $working[$t]=array($working[$t], array()); + $cv = &$working[$t][count($working[$t])-1]; + } else $cv = &$working[$t]; + if (isset($r['attributes'])) { foreach ($r['attributes'] as $k => $v) $cv['ATTRS'][$k] = $v; } + $cv['CHILDREN'] = array(); + $cv['CHILDREN']['_p'] = &$working; + $working = &$cv['CHILDREN']; + + } elseif ($r['type']=='complete') { + if (isset($working[$t])) { // same as open + if (isset($working[$t][0])) $working[$t][] = array(); + else $working[$t] = array($working[$t], array()); + $cv = &$working[$t][count($working[$t])-1]; + } else $cv = &$working[$t]; + if (isset($r['attributes'])) { foreach ($r['attributes'] as $k => $v) $cv['ATTRS'][$k] = $v; } + $cv['CONTENT'] = (isset($r['value']) ? $r['value'] : ''); + + } elseif ($r['type'] == 'close') { + $working = &$working['_p']; + } + } + + $this->remove_p($data); + $this->data = $data; + return true; + } + + /** + * remove_p() + * Removes recursive results in the tree */ + private function remove_p(&$data) { + foreach ($data as $k => $v) { + if ($k === '_p') unset($data[$k]); + elseif (is_array($data[$k])) $this->remove_p($data[$k]); + } + } + + /** + * markup() + * Uses recursion to build and returns XML-markup */ + function markup ($data=false, $depth=0, $forcetag='') { + if (!$data) $data = $this->data; + $res=array(''."\n"); + foreach ($data as $tag=>$r) { + if (isset($r[0])) { + $res[]=$this->markup($r, $depth, $tag); + } else { + if ($forcetag) $tag=$forcetag; + $sp=str_repeat("\t", $depth); + $res[] = "$sp<$tag"; + if (isset($r['ATTRS'])) { foreach ($r['ATTRS'] as $at => $av) $res[] = ' '.$at.'="'.htmlentities($av).'"'; } + $res[] = ">".((isset($r['CHILDREN'])) ? "\n" : ''); + if (isset($r['CHILDREN'])) $res[] = $this->markup($r['CHILDREN'], $depth+1); + elseif (isset($r['CONTENT'])) $res[] = htmlentities($r['CONTENT']); + $res[] = (isset($r['CHILDREN']) ? $sp : '')."\n"; + } + + } + return implode('', $res); + } + + /** + * insert() + * Inserts a new element into the data tree */ + function insert ($element, $pos) { + $working = array_slice($this->data, 0, $pos); $working[] = $element; + $this->data = array_merge($working, array_slice($this->data, $pos)); + } + + /** + * add() + * Adds a new element to the data tree as a child of the $target element */ + function &add ($element,$target=false,$attrs=array(),$content=false) { + $working = array(); + $working[$element] = array(); + if (!empty($attrs) && is_array($attrs)) $working[$element]['ATTRS'] = $attrs; + if ($content) $working[$element]['CONTENT'] = $content; + if ($target) { + if (is_array($target)) $node = &$target; + else $node =& $this->search($target,false,true); + if (!isset($node['CHILDREN'])) $node['CHILDREN'][$element] = $working[$element]; + else $node['CHILDREN'][$element] = $working[$element]; + return $node['CHILDREN'][$element]; + } else $this->data[$element] = $working[$element]; + return $this->data[$element]; + } + + /** + * getRootElement() + * Returns the root element of the tree */ + function getRootElement () { + reset($this->data); + return current($this->data); + } + + /** + * getElementContent() + * Searches the tree for the target $element and returns + * the contents (the value between the tags) */ + function getElementContent ($element) { + $found = $this->search($element); + if (!empty($found)) return $found[0]['CONTENT']; + else return false; + } + + /** + * getElementAttrs() + * Searches the tree for the target $element and returns + * an associative array of attribute names and values () */ + function getElementAttrs ($element) { + $found = $this->search($element); + if (!empty($found)) return $found[0]['ATTRS']; + else return false; + } + + /** + * getElementAttr() + * Searches the tree for the target $element and returns + * value of a specific attribute for a specific element tag () */ + function getElementAttr ($element,$attr) { + $found = $this->search($element); + if (!empty($found)) return $found[0]['ATTRS'][$attr]; + else return false; + } + + /** + * getElement() + * Searches the tree for the target $element and returns + * an array of the element attributes, content and any children */ + function getElement ($element) { + $found = $this->search($element); + if (!empty($found)) return $found[0]; + else return false; + } + + /** + * getElements() + * Searches the tree for the target $element and returns + * an indexed array with each indice including matched elements + * as associative arrays including the element attribtues, content + * and any children */ + function getElements($element) { + return $this->search($element); + } + + /** + * search() + * Helper function to perform recursive searches in the tree + * for a $target and returns the structure */ + private function search ($target,&$dom=false,$ref=false) { + if (!$dom) $dom = &$this->data; + if (!is_array($dom)) $dom = array($dom); + + $results = array(); + foreach($dom as $key => &$element) { + if (is_array($element) && $key == $target && $ref) return $element; + if (is_array($element) && $key == $target) array_push($results,$element); + if (isset($element['CHILDREN'])) { + $found = &$this->search($target,$element['CHILDREN'],$ref); + if ($ref) return $found; + else $results += $found; + } + } + return $results; + } + +} + +?> \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/model/schema.sql b/blog/wp-content/plugins/shopp/core/model/schema.sql new file mode 100644 index 0000000..ef49cf6 --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/model/schema.sql @@ -0,0 +1,328 @@ + +DROP TABLE IF EXISTS ; +CREATE TABLE ( + id bigint(20) unsigned NOT NULL auto_increment, + name varchar(255) NOT NULL default '', + value longtext NOT NULL, + autoload enum('on','off') NOT NULL, + created datetime NOT NULL default '0000-00-00 00:00:00', + modified datetime NOT NULL default '0000-00-00 00:00:00', + PRIMARY KEY id (id) +) ENGINE=MyIsAM DEFAULT CHARSET=utf8; + + +DROP TABLE IF EXISTS ; +CREATE TABLE ( + id bigint(20) unsigned NOT NULL auto_increment, + name varchar(255) NOT NULL default '', + slug varchar(255) NOT NULL default '', + summary text NOT NULL, + description longtext NOT NULL, + published enum('on','off') NOT NULL, + featured enum('off','on') NOT NULL, + variations enum('off','on') NOT NULL, + options text NOT NULL, + created datetime NOT NULL default '0000-00-00 00:00:00', + modified datetime NOT NULL default '0000-00-00 00:00:00', + PRIMARY KEY id (id), + KEY published (published), + KEY featured (featured), + KEY slug (slug), + FULLTEXT search (name,summary,description) +) ENGINE=MyIsAM DEFAULT CHARSET=utf8; + + +DROP TABLE IF EXISTS ; +CREATE TABLE ( + id bigint(20) unsigned NOT NULL auto_increment, + product bigint(20) unsigned NOT NULL default '0', + options text NOT NULL, + optionkey bigint(20) unsigned NOT NULL default '0', + label varchar(100) NOT NULL default '', + context enum('product','variation','addon') NOT NULL, + type enum('Shipped','Virtual','Download','Donation','N/A') NOT NULL, + sku varchar(100) NOT NULL default '', + price float(20,2) NOT NULL default '0.00', + saleprice float(20,2) NOT NULL default '0.00', + weight float(20,3) NOT NULL default '0', + shipfee float(20,2) NOT NULL default '0', + stock int(10) NOT NULL default '0', + inventory enum('off','on') NOT NULL, + sale enum('off','on') NOT NULL, + shipping enum('on','off') NOT NULL, + tax enum('on','off') NOT NULL, + donation varchar(255) NOT NULL default '', + sortorder int(10) unsigned NOT NULL default '0', + created datetime NOT NULL default '0000-00-00 00:00:00', + modified datetime NOT NULL default '0000-00-00 00:00:00', + PRIMARY KEY id (id), + KEY product (product), + KEY catalog (product,type,inventory,stock), + KEY context (context) +) ENGINE=MyIsAM DEFAULT CHARSET=utf8; + + +DROP TABLE IF EXISTS ; +CREATE TABLE ( + id bigint(20) unsigned NOT NULL auto_increment, + product bigint(20) unsigned NOT NULL default '0', + name varchar(255) NOT NULL default '', + content text NOT NULL, + numeral float(20,4) NOT NULL default '0.0000', + sortorder int(10) unsigned NOT NULL default '0', + PRIMARY KEY id (id), + KEY product (product,name), + FULLTEXT name (name,content) +) ENGINE=MyIsAM DEFAULT CHARSET=utf8; + + +DROP TABLE IF EXISTS ; +CREATE TABLE ( + id bigint(20) unsigned NOT NULL auto_increment, + parent bigint(20) unsigned NOT NULL default '0', + name varchar(255) NOT NULL default '', + slug varchar(64) NOT NULL default '', + uri varchar(255) NOT NULL default '', + description text NOT NULL, + spectemplate enum('off','on') NOT NULL, + facetedmenus enum('off','on') NOT NULL, + variations enum('off','on') NOT NULL, + pricerange enum('disabled','auto','custom') NOT NULL, + priceranges text NOT NULL, + specs text NOT NULL, + options text NOT NULL, + prices text NOT NULL, + created datetime NOT NULL default '0000-00-00 00:00:00', + modified datetime NOT NULL default '0000-00-00 00:00:00', + PRIMARY KEY id (id), + KEY parent (parent) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + + +DROP TABLE IF EXISTS ; +CREATE TABLE ( + id bigint(20) unsigned NOT NULL auto_increment, + name varchar(255) NOT NULL default '', + created datetime NOT NULL default '0000-00-00 00:00:00', + modified datetime NOT NULL default '0000-00-00 00:00:00', + PRIMARY KEY id (id) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + + +DROP TABLE IF EXISTS ; +CREATE TABLE ( + id bigint(20) unsigned NOT NULL auto_increment, + product bigint(20) unsigned NOT NULL default '0', + category bigint(20) unsigned NOT NULL default '0', + tag bigint(20) unsigned NOT NULL default '0', + created datetime NOT NULL default '0000-00-00 00:00:00', + modified datetime NOT NULL default '0000-00-00 00:00:00', + PRIMARY KEY id (id), + KEY product (product), + KEY category (category), + KEY tag (tag) +) ENGINE=MyIsAM DEFAULT CHARSET=utf8; + + +DROP TABLE IF EXISTS ; +CREATE TABLE ( + id bigint(20) unsigned NOT NULL auto_increment, + parent bigint(20) unsigned NOT NULL default '0', + context enum('product','price','category') NOT NULL default 'product', + src bigint(20) unsigned NOT NULL default '0', + name varchar(255) NOT NULL default '', + value varchar(255) NOT NULL default '', + properties text NOT NULL, + size bigint(20) unsigned NOT NULL default '0', + data longblob NOT NULL, + datatype enum('metadata','image','small','thumbnail','download') NOT NULL default 'metadata', + sortorder int(10) unsigned NOT NULL default '0', + created datetime NOT NULL default '0000-00-00 00:00:00', + modified datetime NOT NULL default '0000-00-00 00:00:00', + PRIMARY KEY id (id), + KEY parent (parent,context), + KEY src (src) +) ENGINE=MyIsAM DEFAULT CHARSET=utf8; + + +DROP TABLE IF EXISTS ; +CREATE TABLE ( + session varchar(32) NOT NULL, + customer bigint(20) unsigned NOT NULL default '0', + ip varchar(15) NOT NULL default '0.0.0.0', + data longtext NOT NULL, + contents longtext NOT NULL, + created datetime NOT NULL default '0000-00-00 00:00:00', + modified datetime NOT NULL default '0000-00-00 00:00:00', + PRIMARY KEY session (session), + KEY customer (customer) +) ENGINE=MyIsAM DEFAULT CHARSET=utf8; + + +DROP TABLE IF EXISTS ; +CREATE TABLE ( + id bigint(20) unsigned NOT NULL auto_increment, + wpuser bigint(20) unsigned NOT NULL default '0', + password varchar(64) NOT NULL default '', + firstname varchar(32) NOT NULL default '', + lastname varchar(32) NOT NULL default '', + email varchar(96) NOT NULL default '', + phone varchar(24) NOT NULL default '', + company varchar(100) NOT NULL default '', + activation varchar(20) NOT NULL default '', + info longtext NOT NULL, + created datetime NOT NULL default '0000-00-00 00:00:00', + modified datetime NOT NULL default '0000-00-00 00:00:00', + PRIMARY KEY id (id) +) ENGINE=MyIsAM DEFAULT CHARSET=utf8; + + +DROP TABLE IF EXISTS ; +CREATE TABLE ( + id bigint(20) unsigned NOT NULL auto_increment, + customer bigint(20) unsigned NOT NULL default '0', + address varchar(100) NOT NULL default '', + xaddress varchar(100) NOT NULL default '', + city varchar(100) NOT NULL default '', + state varchar(2) NOT NULL default '', + country varchar(2) NOT NULL default '', + postcode varchar(10) NOT NULL default '', + geocode varchar(16) NOT NULL default '', + created datetime NOT NULL default '0000-00-00 00:00:00', + modified datetime NOT NULL default '0000-00-00 00:00:00', + PRIMARY KEY id (id), + KEY customer (customer) +) ENGINE=MyIsAM DEFAULT CHARSET=utf8; + + +DROP TABLE IF EXISTS ; +CREATE TABLE ( + id bigint(20) unsigned NOT NULL auto_increment, + customer bigint(20) unsigned NOT NULL default '0', + card varchar(4) NOT NULL default '', + cardtype varchar(32) NOT NULL default '', + cardexpires date NOT NULL default '0000-00-00', + cardholder varchar(96) NOT NULL default '', + address varchar(100) NOT NULL default '', + xaddress varchar(100) NOT NULL default '', + city varchar(100) NOT NULL default '', + state varchar(2) NOT NULL default '', + country varchar(2) NOT NULL default '', + postcode varchar(10) NOT NULL default '', + created datetime NOT NULL default '0000-00-00 00:00:00', + modified datetime NOT NULL default '0000-00-00 00:00:00', + PRIMARY KEY id (id), + KEY customer (customer) +) ENGINE=MyIsAM DEFAULT CHARSET=utf8; + + +DROP TABLE IF EXISTS ; +CREATE TABLE ( + id bigint(20) unsigned NOT NULL auto_increment, + customer bigint(20) unsigned NOT NULL default '0', + shipping bigint(20) unsigned NOT NULL default '0', + billing bigint(20) unsigned NOT NULL default '0', + currency bigint(20) unsigned NOT NULL default '0', + ip varchar(15) NOT NULL default '0.0.0.0', + firstname varchar(32) NOT NULL default '', + lastname varchar(32) NOT NULL default '', + email varchar(96) NOT NULL default '', + phone varchar(24) NOT NULL default '', + company varchar(100) NOT NULL default '', + card varchar(4) NOT NULL default '', + cardtype varchar(32) NOT NULL default '', + cardexpires date NOT NULL default '0000-00-00', + cardholder varchar(96) NOT NULL default '', + address varchar(100) NOT NULL default '', + xaddress varchar(100) NOT NULL default '', + city varchar(100) NOT NULL default '', + state varchar(100) NOT NULL default '', + country varchar(2) NOT NULL default '', + postcode varchar(10) NOT NULL default '', + shipaddress varchar(100) NOT NULL default '', + shipxaddress varchar(100) NOT NULL default '', + shipcity varchar(100) NOT NULL default '', + shipstate varchar(100) NOT NULL default '', + shipcountry varchar(2) NOT NULL default '', + shippostcode varchar(10) NOT NULL default '', + geocode varchar(16) NOT NULL default '', + promos varchar(255) NOT NULL default '', + subtotal float(20,2) NOT NULL default '0.00', + freight float(20,2) NOT NULL default '0.00', + tax float(20,2) NOT NULL default '0.00', + total float(20,2) NOT NULL default '0.00', + discount float(20,2) NOT NULL default '0.00', + fees float(20,2) NOT NULL default '0.00', + transactionid varchar(64) NOT NULL default '', + transtatus varchar(64) NOT NULL default '', + gateway varchar(64) NOT NULL default '', + shipmethod varchar(100) NOT NULL default '', + shiptrack varchar(100) NOT NULL default '', + status tinyint(3) unsigned NOT NULL default '0', + data longtext NOT NULL, + created datetime NOT NULL default '0000-00-00 00:00:00', + modified datetime NOT NULL default '0000-00-00 00:00:00', + PRIMARY KEY id (id), + KEY customer (customer) +) ENGINE=MyIsAM DEFAULT CHARSET=utf8; + + +DROP TABLE IF EXISTS ; +CREATE TABLE ( + id bigint(20) unsigned NOT NULL auto_increment, + purchase bigint(20) unsigned NOT NULL default '0', + product bigint(20) unsigned NOT NULL default '0', + price bigint(20) unsigned NOT NULL default '0', + download bigint(20) unsigned NOT NULL default '0', + dkey varchar(255) NOT NULL default '', + name varchar(255) NOT NULL default '', + description text NOT NULL, + optionlabel varchar(255) NOT NULL default '', + sku varchar(100) NOT NULL default '', + quantity int(10) unsigned NOT NULL default '0', + downloads int(10) unsigned NOT NULL default '0', + unitprice float(20,2) NOT NULL default '0.00', + shipping float(20,2) NOT NULL default '0.00', + total float(20,2) NOT NULL default '0.00', + variation text NOT NULL, + data longtext NOT NULL, + created datetime NOT NULL default '0000-00-00 00:00:00', + modified datetime NOT NULL default '0000-00-00 00:00:00', + PRIMARY KEY id (id), + KEY purchase (purchase), + KEY product (product), + KEY dkey (dkey(8)) +) ENGINE=MyIsAM DEFAULT CHARSET=utf8; + + +DROP TABLE IF EXISTS ; +CREATE TABLE ( + id bigint(20) unsigned NOT NULL auto_increment, + name varchar(255) NOT NULL default '', + status enum('disabled','enabled') default 'disabled', + type enum('Percentage Off','Amount Off','Free Shipping','Buy X Get Y Free') default 'Percentage Off', + scope enum('Catalog','Order') default 'Catalog', + discount float(20,2) NOT NULL default '0.00', + buyqty int(10) NOT NULL default '0', + getqty int(10) NOT NULL default '0', + search enum('all','any') default 'all', + code varchar(255) NOT NULL default '', + rules text NOT NULL, + starts datetime NOT NULL default '0000-00-00 00:00:00', + ends datetime NOT NULL default '0000-00-00 00:00:00', + created datetime NOT NULL default '0000-00-00 00:00:00', + modified datetime NOT NULL default '0000-00-00 00:00:00', + PRIMARY KEY id (id) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + + +DROP TABLE IF EXISTS ; +CREATE TABLE ( + id bigint(20) unsigned NOT NULL auto_increment, + promo bigint(20) unsigned NOT NULL default '0', + product bigint(20) unsigned NOT NULL default '0', + price bigint(20) unsigned NOT NULL default '0', + PRIMARY KEY id (id), + KEY lookup (product,price) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + diff --git a/blog/wp-content/plugins/shopp/core/ui/behaviors/checkout.js b/blog/wp-content/plugins/shopp/core/ui/behaviors/checkout.js new file mode 100644 index 0000000..d2a5259 --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/ui/behaviors/checkout.js @@ -0,0 +1 @@ +(function($){var validate=function(form){if(!form){return false}var inputs=form.getElementsByTagName("input");var selects=form.getElementsByTagName("select");var passed=true;var passwords=new Array();var error=new Array();for(var i=selects.length-1;i>=0;i--){if(selects[i].className.match(new RegExp("required"))&&!selects[i].disabled){if(selects[i].selectedIndex==0&&selects[i].options[0].value==""){error=new Array(CHECKOUT_REQUIRED_FIELD.replace(/%s/,selects[i].title),selects[i])}}}for(var i=inputs.length-1;i>=0;i--){if(inputs[i].className.match(new RegExp("required"))){if(inputs[i].type=="checkbox"){if(!inputs[i].checked){error=new Array(CHECKOUT_CHECKBOX_CHECKED.replace(/%s/,inputs[i].title),inputs[i])}}else{if(inputs[i].value==null||inputs[i].value==""){error=new Array(CHECKOUT_REQUIRED_FIELD.replace(/%s/,inputs[i].title),inputs[i])}}}if(inputs[i].className.match(new RegExp("email"))){if(!inputs[i].value.match(new RegExp("^[a-zA-Z0-9._-]+@([a-zA-Z0-9.-]+.)+[a-zA-Z0-9.-]{2,4}$"))){error=new Array(CHECKOUT_INVALID_EMAIL,inputs[i])}}if(chars=inputs[i].className.match(new RegExp("min(\\d+)"))){if(inputs[i].value.length0){error[1].focus();alert(error[0]);passed=false}return passed};$(window).ready(function(){var sameshipping=$("#same-shipping");if(sameshipping.length>0){sameshipping.change(function(){if($("#same-shipping").attr("checked")){$("#billing-address-fields").removeClass("half");$("#shipping-address-fields").hide();$("#shipping-address-fields .required").removeClass("required")}else{$("#billing-address-fields").addClass("half");$("#shipping-address-fields input").not("#shipping-xaddress").addClass("required");$("#shipping-address-fields select").addClass("required");$("#shipping-address-fields").show()}}).change();sameshipping.click(function(){$(this).change()})}$("#submit-login").click(function(){$("#checkout.shopp").unbind("submit");$("#checkout.shopp").submit(function(){if($("#account-login").val()==""){alert(CHECKOUT_LOGIN_NAME);$("#account-login").focus();return false}if($("#password-login").val()==""){alert(CHECKOUT_LOGIN_PASSWORD);$("#password-login").focus();return false}$("#process-login").val("true");return true}).submit()});$("#checkout.shopp").submit(function(){if(validate(this)){return true}else{return false}});$("#shipping-country").change(function(){if($("#shipping-state").attr("type")=="text"){return true}$("#shipping-state").empty().attr("disabled",true);$("").val("").html("").appendTo("#shipping-state");if(regions[this.value]){$.each(regions[this.value],function(value,label){option=$("").val(value).html(label).appendTo("#shipping-state")});$("#shipping-state").attr("disabled",false)}});$("#billing-country").change(function(){if($("#billing-state").attr("type")=="text"){return true}$("#billing-state").empty().attr("disabled",true);$("").val("").html("").appendTo("#billing-state");if(regions[this.value]){$.each(regions[this.value],function(value,label){option=$("").val(value).html(label).appendTo("#billing-state")});$("#billing-state").attr("disabled",false)}});$("input.shipmethod").click(function(){$("#shipping, #total").html(SHIPCALC_STATUS);var url=$("#shopp form").attr("action");url+=(url.indexOf("?")==-1)?"?":"&";$.getJSON(url+"shopp_lookup=shipcost&method="+$(this).val(),function(result){var totals=eval(result);$("span.shopp_cart_shipping").html(asMoney(totals.shipping));$("span.shopp_cart_tax").html(asMoney(totals.tax));$("span.shopp_cart_total").html(asMoney(totals.total))})})})})(jQuery); \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/ui/behaviors/editors.js b/blog/wp-content/plugins/shopp/core/ui/behaviors/editors.js new file mode 100644 index 0000000..d48fad9 --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/ui/behaviors/editors.js @@ -0,0 +1 @@ +function NestedMenu(d,e,h,j,b,f,a){var c=jQuery.noConflict();if(!a){a={axis:"y"}}var g=this;this.items=f;this.dataname=h;this.element=c("
  • ").appendTo(c(e).children("ul"));this.index=d;this.moveHandle=c('
    ').appendTo(this.element);this.sortorder=c('').appendTo(this.element);this.id=c('').appendTo(this.element);this.label=c('').appendTo(this.element);this.deleteButton=c('').appendTo(this.element);if(this.items){if(f.type=="list"){this.itemsElement=c("
      ").appendTo(f.target).hide()}else{this.itemsElement=c("
    • ").appendTo(f.target).hide()}}this.selected=function(){c(e).find("ul li").removeClass("selected");c(g.element).addClass("selected");if(f){c(f.target).children().hide();c(g.itemsElement).show()}};this.element.click(this.selected).hover(function(){c(this).addClass("hover")},function(){c(this).removeClass("hover")});this.label.mouseup(function(i){this.select()}).focus(function(){c(this).keydown(function(i){i.stopPropagation();if(i.keyCode==13){c(this).blur().unbind("keydown")}})});this.remove=function(){var i=c(e).find("input.deletes");if(c(g.id).val()!=""){i.val((i.val()=="")?c(g.id).val():i.val()+","+c(g.id).val())}if(f){g.itemsElement.remove()}g.element.remove()};this.deleteButton.click(this.remove);this.id.val(this.index);if(b&&b.id){this.id.val(b.id)}if(b&&b.name){this.label.val(htmlentities(b.name))}else{this.label.val(j+" "+this.index)}if(!c(e).children("ul").hasClass("ui-sortable")){c(e).children("ul").sortable(a)}else{c(e).children("ul").sortable("refresh")}}function NestedMenuContent(b,f,a,e){var d=jQuery.noConflict();var c=d('').appendTo(f);if(e&&e.content){c.val(htmlentities(e.content))}}function NestedMenuOption(d,g,c,b,f){var e=jQuery.noConflict();var a=this;this.index=e(g).contents().length;this.element=e('
    • ').appendTo(g);this.moveHandle=e('
      ').appendTo(this.element);this.id=e('').appendTo(this.element);this.label=e('').appendTo(this.element);this.deleteButton=e('').appendTo(this.element);this.element.hover(function(){e(this).addClass("hover")},function(){e(this).removeClass("hover")});this.label.click(function(){this.select()});this.label.focus(function(){e(this).keydown(function(h){h.stopPropagation();if(h.keyCode==13){e(this).blur().unbind("keydown")}})});this.deleteButton.click(function(){e(a.element).remove()});this.id.val(this.index);if(f.id){this.id.val(f.id)}if(f.name){this.label.val(htmlentities(f.name))}if(!f.name){this.label.val(b+" "+(this.index+1))}}function addDetail(f){var e=jQuery.noConflict();var d=e("#details-menu");var a=e("#details-list");var h=detailsidx++;var g=new NestedMenu(h,d,"details","Detail Name",f,{target:a});if(f&&f.options){var b=e('').appendTo(g.itemsElement);for(var c in f.options){e("").appendTo(b)}if(f&&f.content){b.val(htmlentities(f.content))}}else{g.item=new NestedMenuContent(g.index,g.itemsElement,"details",f)}if(!f||f.add){g.add=e('').appendTo(g.element)}}function loadVariations(a,b){var c=jQuery.noConflict();if(a){c.each(a,function(d,e){addVariationOptionsMenu(e)});c.each(b,function(d,e){if(this.context=="variation"){addPriceLine("#variations-pricing",this.options.split(","),this)}});updateTabIndexes();c.each(a,function(d,e){c.each(e.options,function(f,g){if(g.linked=="on"){linkVariations(g.id)}})})}}function addVariationOptionsMenu(f){var e=jQuery.noConflict();var d=e("#variations-menu");var a=e("#variations-list");var c=e("#addVariationOption");var b=e("#linkOptionVariations");var h=variationsidx;var g=new NestedMenu(h,d,"options",OPTION_MENU_DEFAULT,f,{target:a,type:"list"},{axis:"y",update:function(){orderOptions(d,a)}});g.addOption=function(j){var k=false;if(!j){j=new Object()}if(!j.id){k=true;j.id=optionsidx}else{if(j.id>optionsidx){optionsidx=j.id}}var i=new NestedMenuOption(g.index,g.itemsElement,"options",NEW_OPTION_DEFAULT,j);optionsidx++;i.linkIcon=e('linked').appendTo(i.moveHandle);i.linked=e('').appendTo(i.element);i.linked.change(function(){if(e(this).val()=="off"){i.linkIcon.addClass("invisible")}if(e(this).val()=="on"){i.linkIcon.removeClass("invisible")}});if(j.linked){i.linked.val(j.linked).change()}else{i.linked.val("off").change()}i.selected=function(){if(i.element.hasClass("selected")){a.find("ul li").removeClass("selected");selectedMenuOption=false}else{a.find("ul li").removeClass("selected");e(i.element).addClass("selected");selectedMenuOption=i}b.change()};i.element.click(i.selected);productOptions[i.id.val()]=i.label;i.label.blur(function(){updateVariationLabels()});i.deleteButton.unbind("click");i.deleteButton.click(function(){if(g.itemsElement.children().length==1){deleteVariationPrices([i.id.val()],true)}else{deleteVariationPrices([i.id.val()])}i.element.remove()});if(!k){addVariationPrices(i.id.val())}else{addVariationPrices()}g.items.push(i)};g.items=new Array();if(f&&f.options){e.each(f.options,function(){g.addOption(this)})}else{g.addOption();g.addOption()}g.itemsElement.sortable({axis:"y",update:function(){orderVariationPrices()}});g.element.unbind("click",g.click);g.element.click(function(){g.selected();e(c).unbind("click").click(g.addOption)});optionMenus[variationsidx++]=g;g.deleteButton.unbind("click");g.deleteButton.click(function(){var i=new Array();e(g.itemsElement).find("li").not(".ui-sortable-helper").find("input.id").each(function(j,k){i.push(e(k).val())});deleteVariationPrices(i,true);g.remove()})}function linkVariations(b){if(!b){return}var c=jQuery.noConflict();for(var a in pricingOptions){if(c.inArray(b.toString(),pricingOptions[a].options)!=-1){if(!linkedPricing[b]){linkedPricing[b]=new Array()}linkedPricing[b].push(a);pricingOptions[a].linkInputs(b)}}}function unlinkVariations(a){if(!a){return}var b=jQuery.noConflict();if(!linkedPricing[a]){return}b.each(linkedPricing[a],function(d,c){pricingOptions[c].unlinkInputs(a)});linkedPricing.splice(a,1)}function updateVariationLinks(){var c=jQuery.noConflict();if(!linkedPricing){return}for(var a in pricingOptions){pricingOptions[a].unlinkInputs()}for(var b in linkedPricing){linkedPricing[b]=false;linkVariations(b)}}function buildVariations(){var e=jQuery.noConflict();var m=new Array();var j=e("#variations-list ul");var f=j.length;var a=f-1;var k=new Array(j.length);var d=new Array(j.length);var b=0;j.each(function(n,i){d[n]=e(i).children().length;if(b==0){b=e(i).children().length}else{b=b*e(i).children().length}k[n]=0});for(var c=0;c=d[a]){for(var h=a;h>-1;h--){if(k[h]-1){k[(h-1)]++}}}}return m}function addVariationPrices(f){var e=jQuery.noConflict();if(!f){var a=buildVariations();var d=e("#variations-pricing");var c=e(d).children();var b=false;e(a).each(function(j,g){var h=xorkey(g);var i=xorkey(g.slice(0,g.length-1));if(i==""){i=-1}if(!pricingOptions[h]){if(pricingOptions[i]){pricingOptions[h]=pricingOptions[i];delete pricingOptions[i];pricingOptions[h].options=g;pricingOptions[h].updateKey();pricingOptions[h].updateLabel()}else{if(c.length==0){addPriceLine("#variations-pricing",g,{context:"variation"})}else{addPriceLine(pricingOptions[xorkey(a[(j-1)])].row,g,{context:"variation"},"after")}b=true}}});if(b){updateTabIndexes();updateVariationLinks()}}}function updateTabIndexes(){var b=jQuery.noConflict();var a=b("#variations-pricing").children();b.each(a,function(d,c){key=b(c).find(".pricing-label input.optionkey").val();pricingOptions[key].updateTabindex(d)})}function deleteVariationPrices(d,e){var c=jQuery.noConflict();var b=buildVariations();var a=false;c(b).each(function(m,g){var k=xorkey(g);for(var j=0;j0&&pricingOptions[d]){pricingOptions[d].row.appendTo("#variations-pricing");pricingOptions[d].options=c;pricingOptions[d].updateLabel()}})}function xorkey(c){for(var b=0,a=0;a1000){d=d/1024;c++}return d.toFixed(2)+" "+a[c]}function unsavedChanges(){var a=typeof(tinyMCE)!="undefined"?tinyMCE.activeEditor:false,c,b;if(a&&!a.isHidden()){if(a.isDirty()){return UNSAVED_CHANGES_WARNING}}if(changes&&!saving){return UNSAVED_CHANGES_WARNING}}function ImageUploads(id,type){(function($){var swfu;var settings={button_text:''+ADD_IMAGE_BUTTON_TEXT+"",button_text_style:'.button { text-align: center; font-family:"Lucida Grande","Lucida Sans Unicode",Tahoma,Verdana,sans-serif; font-size: 9px; color: #333333; }',button_text_top_padding:3,button_height:"22",button_width:"100",button_image_url:rsrcdir+"/core/ui/icons/buttons.png",button_placeholder_id:"swf-uploader-button",upload_url:ajaxurl,flash_url:rsrcdir+"/core/ui/behaviors/swfupload/swfupload.swf",file_queue_limit:1,file_size_limit:filesizeLimit+"b",file_types:"*.jpg;*.jpeg;*.png;*.gif",file_types_description:"Web-compatible Image Files",file_upload_limit:filesizeLimit,post_params:{action:"shopp_add_image",type:id},swfupload_loaded_handler:swfuLoaded,file_queued_handler:imageFileQueued,file_queue_error_handler:imageFileQueueError,file_dialog_complete_handler:imageFileDialogComplete,upload_start_handler:startImageUpload,upload_progress_handler:imageUploadProgress,upload_error_handler:imageUploadError,upload_success_handler:imageUploadSuccess,upload_complete_handler:imageUploadComplete,queue_complete_handler:imageQueueComplete,custom_settings:{loaded:false,targetHolder:false,progressBar:false,sorting:false},debug:false};if(wp26){settings.button_image_url=rsrcdir+"/core/ui/icons/wp26button.png";settings.button_height="26";settings.button_width="100";settings.button_text_style='.button { text-align: center; font-family:"Lucida Grande","Lucida Sans Unicode",Tahoma,Verdana,sans-serif; font-size: 11px; color: #284464; }'}if(flashuploader){swfu=new SWFUpload(settings)}var browserImageUploader=$("#image-upload").upload({name:"Filedata",action:ajaxurl,enctype:"multipart/form-data",params:{action:"shopp_add_image",type:id},autoSubmit:true,onSubmit:function(){var cell=$('
    • ').appendTo($("#lightbox"));var sorting=$('').appendTo(cell);var progress=$('
      ').appendTo(cell);var bar=$('
      ').appendTo(progress);var art=$('
      ').appendTo(progress);this.targetHolder=cell;this.progressBar=bar;this.sorting=sorting},onComplete:function(results){if(results==""){$(this.targetHolder).remove();alert(SERVER_COMM_ERROR);return true}var image=eval("("+results+")");if(image.error){$(this.targetHolder).remove();alert(image.error);return true}$(this.targetHolder).attr({id:"image-"+image.src});$(this.sorting).val(image.src);var img=$('').appendTo(this.targetHolder).hide();var deleteButton=$('').appendTo($(this.targetHolder)).hide();var deleteIcon=$('-').appendTo(deleteButton);$(this.progressBar).animate({width:"76px"},250,function(){$(this).parent().fadeOut(500,function(){$(this).remove();$(img).fadeIn("500");enableDeleteButton(deleteButton)})})}});$(document).load(function(){if(!swfu.loaded){$("#product-images .swfupload").remove()}});if($("#lightbox li").size()>0){$("#lightbox").sortable({opacity:0.8})}$("#lightbox li").each(function(){$(this).dblclick(function(){var id=$(this).attr("id")+"-details";$(this).find("input.close").click(function(){tb_remove()});tb_show("Image Details","#TB_inline?height=125&width=320&inlineId="+id)});enableDeleteButton($(this).find("button.deleteButton"))});function swfuLoaded(){$("#browser-uploader").hide();swfu.loaded=true}function imageFileQueued(file){}function imageFileQueueError(file,error,message){if(error==SWFUpload.QUEUE_ERROR.QUEUE_LIMIT_EXCEEDED){alert("You selected too many files to upload at one time. "+(message===0?"You have reached the upload limit.":"You may upload "+(message>1?"up to "+message+" files.":"only one file.")));return}else{alert(message)}}function imageFileDialogComplete(selected,queued){try{this.startUpload()}catch(ex){this.debug(ex)}}function startImageUpload(file){var cell=$('
    • ').appendTo($("#lightbox"));var sorting=$('').appendTo(cell);var progress=$('
      ').appendTo(cell);var bar=$('
      ').appendTo(progress);var art=$('
      ').appendTo(progress);this.targetHolder=cell;this.progressBar=bar;this.sorting=sorting;return true}function imageUploadProgress(file,loaded,total){var progress=Math.ceil((loaded/total)*76);$(this.progressBar).animate({width:progress+"px"},100)}function imageUploadError(file,error,message){}function imageUploadSuccess(file,results){var image=eval("("+results+")");if(image.error){$(this.targetHolder).remove();alert(image.error);return true}$(this.targetHolder).attr({id:"image-"+image.src});$(this.sorting).val(image.src);var img=$('').appendTo(this.targetHolder).hide();var deleteButton=$('').appendTo($(this.targetHolder)).hide();var deleteIcon=$('-').appendTo(deleteButton);$(this.progressBar).animate({width:"76px"},250,function(){$(this).parent().fadeOut(500,function(){$(this).remove();$(img).fadeIn("500");enableDeleteButton(deleteButton)})})}function imageUploadComplete(file){}function imageQueueComplete(uploads){if($("#lightbox li").size()>1){$("#lightbox").sortable("refresh")}else{$("#lightbox").sortable()}}function enableDeleteButton(button){$(button).hide();$(button).parent().hover(function(){$(button).show()},function(){$(button).hide()});$(button).click(function(){if(confirm(DELETE_IMAGE_WARNING)){$("#deleteImages").val(($("#deleteImages").val()=="")?$(button).val():$("#deleteImages").val()+","+$(button).val());$(button).parent().fadeOut(500,function(){$(this).remove()})}})}})(jQuery)}function FileUploader(button,defaultButton,linenum,updates){var _self=this;(function($){_self.settings={button_text:''+UPLOAD_FILE_BUTTON_TEXT+"",button_text_style:'.button { text-align: center; font-family:"Lucida Grande","Lucida Sans Unicode",Tahoma,Verdana,sans-serif; font-size: 9px; color: #333333; }',button_text_top_padding:3,button_height:"22",button_width:"100",button_image_url:rsrcdir+"/core/ui/icons/buttons.png",button_placeholder_id:button,flash_url:rsrcdir+"/core/ui/behaviors/swfupload/swfupload.swf",upload_url:ajaxurl,file_queue_limit:1,file_size_limit:filesizeLimit+"b",file_types:"*.*",file_types_description:"All Files",file_upload_limit:filesizeLimit,post_params:{action:"shopp_add_download"},swfupload_loaded_handler:swfuLoaded,file_queue_error_handler:fileQueueError,file_dialog_complete_handler:fileDialogComplete,upload_start_handler:startUpload,upload_progress_handler:uploadProgress,upload_error_handler:uploadError,upload_success_handler:uploadSuccess,upload_complete_handler:uploadComplete,custom_settings:{loaded:false,targetCell:false,targetLine:false,progressBar:false},debug:false};if(wp26){_self.settings.button_image_url=rsrcdir+"/core/ui/icons/wp26button.png";_self.settings.button_height="26";_self.settings.button_width="100";_self.settings.button_text_style='.button { text-align: center; font-family:"Lucida Grande","Lucida Sans Unicode",Tahoma,Verdana,sans-serif; font-size: 11px; color: #284464; }'}if(flashuploader){_self.swfu=new SWFUpload(_self.settings);_self.swfu.targetCell=updates;_self.swfu.targetLine=linenum}defaultButton.upload({name:"Filedata",action:ajaxurl,enctype:"multipart/form-data",params:{action:"shopp_add_download"},autoSubmit:true,onSubmit:function(){updates.attr("class","").html("");var progress=$('
      ').appendTo(updates);var bar=$('
      ').appendTo(progress);var art=$('
      ').appendTo(progress);this.targetHolder=updates;this.progressBar=bar},onComplete:function(results){var filedata=eval("("+results+")");if(filedata.error){$(this.targetHolder).html("No download file.");alert(filedata.error);return true}var targetHolder=this.targetHolder;filedata.type=filedata.type.replace(/\//gi," ");$(this.progressBar).animate({width:"76px"},250,function(){$(this).parent().fadeOut(500,function(){$(targetHolder).attr("class","file "+filedata.type).html(filedata.name+"
      "+readableFileSize(filedata.size)+'');$(this).remove()})})}});$(_self).load(function(){if(!_self.swfu.loaded){$(defaultButton).parent().parent().find(".swfupload").remove()}});function swfuLoaded(){$(defaultButton).hide();this.loaded=true}function fileQueueError(file,error,message){if(error==SWFUpload.QUEUE_ERROR.QUEUE_LIMIT_EXCEEDED){alert("You selected too many files to upload at one time. "+(message===0?"You have reached the upload limit.":"You may upload "+(message>1?"up to "+message+" files.":"only one file.")));return}else{alert(message)}}function fileDialogComplete(selected,queued){try{this.startUpload()}catch(ex){this.debug(ex)}}function startUpload(file){this.targetCell.attr("class","").html("");var progress=$('
      ').appendTo(this.targetCell);var bar=$('
      ').appendTo(progress);var art=$('
      ').appendTo(progress);this.progressBar=bar;return true}function uploadProgress(file,loaded,total){var progress=Math.ceil((loaded/total)*76);$(this.progressBar).animate({width:progress+"px"},100)}function uploadError(file,error,message){}function uploadSuccess(file,results){var filedata=eval("("+results+")");if(filedata.error){$(this.targetHolder).html(no_download);alert(filedata.error);return true}var targetCell=this.targetCell;var i=this.targetLine;filedata.type=filedata.type.replace(/\//gi," ");$(this.progressBar).animate({width:"76px"},250,function(){$(this).parent().fadeOut(500,function(){$(this).remove();$(targetCell).attr("class","file "+filedata.type).html(filedata.name+"
      "+readableFileSize(filedata.size)+'')})})}function uploadComplete(file){}})(jQuery)}function SlugEditor(c,b){var a=this;(function(d){a.edit_permalink=function(){var f,k=0;var h=d("#editable-slug");var e=h.html();var m=d("#slug_input");var n=m.html();var l=d("#edit-slug-buttons");var j=l.html();var g=d("#editable-slug-full").html();l.html(' ");l.children(".save").click(function(){var i=h.children("input").val();d.post(editslug_url+"&action=wp_ajax_shopp_edit_slug",{id:c,type:b,slug:i},function(o){h.html(e);l.html(j);if(o!=-1){h.html(o);d("#editable-slug-full").html(o);m.val(o)}a.enable()},"text")});d("#edit-slug-buttons .cancel").click(function(){h.html(e);l.html(j);m.attr("value",n);a.enable()});for(f=0;fg.length/4)?"":g;h.html('').children("input").keypress(function(o){var i=o.which;if(i==13||i==27){o.preventDefault()}if(13==i){l.children(".save").click()}if(27==i){l.children(".cancel").click()}m.val(this.value)}).focus()};a.enable=function(){d("#edit-slug-buttons").children(".edit-slug").click(function(){a.edit_permalink()});d("#editable-slug").click(function(){d("#edit-slug-buttons").children(".edit-slug").click()})};a.enable()})(jQuery)}; \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/ui/behaviors/ocupload.js b/blog/wp-content/plugins/shopp/core/ui/behaviors/ocupload.js new file mode 100644 index 0000000..eed9719 --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/ui/behaviors/ocupload.js @@ -0,0 +1 @@ +(function(a){a.fn.upload=function(b){b=a.extend({name:"file",enctype:"multipart/form-data",action:"",autoSubmit:true,onSubmit:function(){},onComplete:function(){},onSelect:function(){},params:{}},b);return new a.ocupload(this,b)},a.ocupload=function(f,e){var d=this;var i=new Date().getTime().toString().substr(8);var g=a('').css({display:"none"});var h=a('
      ').css({margin:0,padding:0});var c=a('').css({position:"relative",display:"block",marginLeft:-175+"px",opacity:0});f.wrap("
      ");h.append(c);f.after(h);f.after(g);elementHeight=f.outerHeight();elementWidth=f.outerWidth();var b=f.parent().css({position:"relative",height:(elementHeight+10)+"px",width:(elementWidth+10)+"px",overflow:"hidden",cursor:"pointer",margin:0,padding:0});c.css("marginTop",-b.height()-10+"px");b.mousemove(function(j){c.css({top:j.pageY-b.offset().top+"px",left:j.pageX-b.offset().left+"px"})});c.change(function(){d.onSelect();if(d.autoSubmit){d.submit()}});a.extend(this,{autoSubmit:true,onSubmit:e.onSubmit,onComplete:e.onComplete,onSelect:e.onSelect,filename:function(){return c.attr("value")},params:function(j){var j=j?j:false;if(j){e.params=a.extend(e.params,j)}else{return e.params}},name:function(j){var j=j?j:false;if(j){c.attr("name",value)}else{return c.attr("name")}},action:function(j){var j=j?j:false;if(j){h.attr("action",j)}else{return h.attr("action")}},enctype:function(j){var j=j?j:false;if(j){h.attr("enctype",j)}else{return h.attr("enctype")}},set:function(l,k){var k=k?k:false;function j(n,m){switch(n){default:throw new Error("[jQuery.ocupload.set] '"+n+"' is an invalid option.");break;case"name":d.name(m);break;case"action":d.action(m);break;case"enctype":d.enctype(m);break;case"params":d.params(m);break;case"autoSubmit":d.autoSubmit=m;break;case"onSubmit":d.onSubmit=m;break;case"onComplete":d.onComplete=m;break;case"onSelect":d.onSelect=m;break}}if(k){j(l,k)}else{a.each(l,function(m,n){j(m,n)})}},submit:function(){this.onSubmit();a.each(e.params,function(j,k){h.append(a(''))});h.submit();g.unbind().load(function(){var k=document.getElementById(g.attr("name"));var j=a(k.contentWindow.document.body).text();d.onComplete(j)})}})}})(jQuery); \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/ui/behaviors/priceline-wp26.js b/blog/wp-content/plugins/shopp/core/ui/behaviors/priceline-wp26.js new file mode 100644 index 0000000..52f7e4a --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/ui/behaviors/priceline-wp26.js @@ -0,0 +1 @@ +function addPriceLine(a,x,an,v){var N=jQuery.noConflict();var ak=pricingidx;var n=N('');if(v=="after"){n.insertAfter(a)}else{if(v=="before"){n.insertBefore(a)}else{n.appendTo(a)}}var V=N('').appendTo(n);var ae=N('').appendTo(V);var ad=N('').appendTo(V);ad.change(function(){ae.text(N(this).val())});var s=N('').appendTo(V);var q=N('').appendTo(V);var ap=N('').appendTo(V);var R=N('').appendTo(V);var l=N('').appendTo(V);var C=N('').appendTo(V);var ac=N("").appendTo(n);var am=N("").addClass("pricing-table").appendTo(ac);var O=N("").appendTo(am);var y=N("").appendTo(am);var b=N('").appendTo(O);var ao=N("").appendTo(O);var D=N('').prependTo(d);N('').prependTo(d);var Y=N("").appendTo(O);var g=N('').prependTo(u);N('').prependTo(u);var al=N("").appendTo(O);var k=N('').prependTo(T);N('').prependTo(d);var U=N("').appendTo(O);var W=N('
      ").appendTo(y);var aa="";N(priceTypes).each(function(i,av){aa+='"});var at=N('').html(aa).appendTo(ao);var S=N("").appendTo(O);var au=N('").appendTo(S);var ab=N("").appendTo(y);var M=N('').appendTo(ab);N("
      ").appendTo(ab);N('').appendTo(ab);var P=N('').appendTo(ab);var af=N('
      ").appendTo(ab);var d=N('
      ").appendTo(y);var H=N(""+NOT_ON_SALE_TEXT+"").addClass("status").appendTo(Y);var z=N("").addClass("fields").appendTo(Y).hide();var G=N('').appendTo(z);var B=N("").appendTo(O);var w=N('').appendTo(y);N('').appendTo(w);var L=N('').appendTo(w);N('
      ").appendTo(w);N('').appendTo(w);var c=N('').appendTo(w);N('").appendTo(w);var u=N('
      ").appendTo(y);var h=N(""+FREE_SHIPPING_TEXT+"").addClass("status").appendTo(al);var K=N("").addClass("fields").appendTo(al).hide();var X=N('').appendTo(K);var t=N('
      ").appendTo(K);var f=N('').appendTo(K);var E=N('
      ").appendTo(K);var T=N('
      ").appendTo(y);var m=N(""+NOT_TRACKED_TEXT+"").addClass("status").appendTo(U);var ar=N("").addClass("fields").appendTo(U).hide();var ah=N('').appendTo(ar);var J=N('").appendTo(ar);var Q=N("
      ").appendTo(ar);var A=N('').appendTo(ar);var j=N('").appendTo(ar);var ag=N('
      ').appendTo(y);var I=N("
      ").html("No product download.").appendTo(W);var p=N('
      ').appendTo(O);if(storage=="fs"){var F=N("
      ").prependTo(W).hide();var aq=N('').appendTo(F).change(function(){N(this).removeClass("warning").addClass("verifying");N.ajax({url:fileverify_url+"&action=wp_ajax_shopp_verify_file",type:"POST",data:"filepath="+N(this).val(),timeout:10000,dataType:"text",success:function(i){aq.removeClass("verifying");if(i=="OK"){return}if(i=="NULL"){aq.addClass("warning").attr("title",FILE_NOT_FOUND_TEXT)}if(i=="ISDIR"){aq.addClass("warning").attr("title",FILE_ISDIR_TEXT)}if(i=="READ"){aq.addClass("warning").attr("title",FILE_NOT_READ_TEXT)}}})});var r=N('').appendTo(p).click(function(){F.slideToggle()})}var aj=N('
      ').appendTo(p);var o=N('").appendTo(p);var ai=new FileUploader(N(aj).attr("id"),o,ak,I);var Z=new Object();Z.id=pricingidx;Z.options=x;Z.data=an;Z.row=n;Z.rowid=0;Z.label=ad;Z.links=new Array();Z.inputs=new Array(at,M,P,D,G,L,c,g,X,f,k,ah,A);Z.disable=function(){at.val("N/A").trigger("change.value")};Z.updateKey=function(){R.val(xorkey(this.options))};Z.updateLabel=function(){var i="";var av="";if(this.options){N(this.options).each(function(aw,ax){if(i==""){i=N(productOptions[ax]).val()}else{i+=", "+N(productOptions[ax]).val()}if(av==""){av=ax}else{av+=","+ax}})}if(i==""){i=DEFAULT_PRICELINE_LABEL}this.label.val(htmlentities(i)).change();l.val(av)};Z.updateTabindex=function(i){N.each(this.inputs,function(aw,av){N(av).attr("tabindex",((i+1)*100)+aw)})};Z.linkInputs=function(i){Z.links.push(i);N.each(Z.inputs,function(aw,av){if(!av){return}var ax="change.linkedinputs";if(N(av).attr("type")=="checkbox"){ax="click.linkedinputs"}N(av).bind(ax,function(){var az=N(this).val();var ay=N(this).attr("checked");N.each(Z.links,function(aA,aB){N.each(linkedPricing[aB],function(aD,aC){if(aC==xorkey(Z.options)){return}if(!pricingOptions[aC]){return}if(N(av).attr("type")=="checkbox"){N(pricingOptions[aC].inputs[aw]).attr("checked",ay)}else{N(pricingOptions[aC].inputs[aw]).val(az)}N(pricingOptions[aC].inputs[aw]).trigger("change.value")})})})})};Z.unlinkInputs=function(i){if(i!==false){index=N.inArray(i,Z.links);Z.links.splice(index,1)}N.each(Z.inputs,function(aw,av){if(!av){return}var ax="blur.linkedinputs";if(N(av).attr("type")=="checkbox"){ax="click.linkedinputs"}N(av).unbind(ax)})};Z.updateKey();Z.updateLabel();var e=new Object();e.All=new Array(S,ab,d,Y,u,al,T,U,ag,W,p,B,w);if(pricesPayload){e.Shipped=new Array(S,ab,d,Y,u,al,T,U);e.Virtual=new Array(S,ab,d,Y,T,U);e.Download=new Array(S,ab,d,Y,ag,W,p)}else{e.Shipped=new Array(S,ab,u,al);e.Virtual=new Array(S,ab);e.Download=new Array(S,ab)}e.Donation=new Array(S,ab,B,w);at.bind("change.value",function(){var i=at.val();for(var av in e.All){N(e.All[av]).hide()}au.html(PRICE_LABEL);if(e[i]){for(var av in e[i]){N(e[i][av]).show()}}if(at.val()=="Donation"){au.html(AMOUNT_LABEL);P.attr("checked","true").change()}});D.bind("change.value",function(){if(this.checked){H.hide();z.show()}else{H.show();z.hide()}if(N.browser.msie){N(this).blur()}}).click(function(){if(N.browser.msie){N(this).trigger("change.value")}if(this.checked){G.focus().select()}});g.bind("change.value",function(){if(this.checked){h.hide();K.show()}else{h.show();K.hide()}if(N.browser.msie){N(this).blur()}}).click(function(){if(N.browser.msie){N(this).trigger("change.value")}if(this.checked){X.focus().select()}});k.bind("change.value",function(){if(this.checked){m.hide();ar.show()}else{m.show();ar.hide()}if(N.browser.msie){N(this).blur()}}).click(function(){if(N.browser.msie){N(this).trigger("change.value")}if(this.checked){ah.focus().select()}});M.bind("change.value",function(){this.value=asMoney(this.value)}).trigger("change.value");G.bind("change.value",function(){this.value=asMoney(this.value)}).trigger("change.value");f.bind("change.value",function(){this.value=asMoney(this.value)}).trigger("change.value");X.bind("change.value",function(){var i=new Number(this.value);this.value=i.roundFixed(3)}).trigger("change.value");if(an&&an.context){ap.val(an.context)}else{ap.val("product")}if(an&&an.label){ad.val(htmlentities(an.label)).change();at.val(an.type);s.val(an.id);q.val(an.product);A.val(an.sku);M.val(asMoney(an.price));if(an.sale=="on"){D.attr("checked","true").trigger("change.value")}if(an.shipping=="on"){g.attr("checked","true").trigger("change.value")}if(an.inventory=="on"){k.attr("checked","true").trigger("change.value")}if(an.donation){if(an.donation["var"]=="on"){L.attr("checked",true)}if(an.donation.min=="on"){c.attr("checked",true)}}G.val(asMoney(an.saleprice));f.val(asMoney(an.shipfee));X.val(an.weight).trigger("change.value");ah.val(an.stock);if(an.download){if(an.filedata.mimetype){an.filedata.mimetype=an.filedata.mimetype.replace(/\//gi," ")}I.attr("class","file "+an.filedata.mimetype).html(an.filename+"
      "+readableFileSize(an.filesize)+"").click(function(){window.location.href=adminurl+"admin.php?page=shopp-lookup&download="+an.download})}if(an.tax=="off"){P.attr("checked","true")}}else{if(at.val()=="Shipped"){g.attr("checked","true").trigger("change.value")}}quickSelects(n);at.change();if(x){pricingOptions[xorkey(x)]=Z}N("#prices").val(pricingidx++);return n}; \ No newline at end of file diff --git a/blog/wp-content/plugins/shopp/core/ui/behaviors/priceline.js b/blog/wp-content/plugins/shopp/core/ui/behaviors/priceline.js new file mode 100644 index 0000000..a4eee73 --- /dev/null +++ b/blog/wp-content/plugins/shopp/core/ui/behaviors/priceline.js @@ -0,0 +1 @@ +function addPriceLine(a,w,am,u){var M=jQuery.noConflict();var aj=pricingidx;var m=M('
      ');if(u=="after"){m.insertAfter(a)}else{if(u=="before"){m.insertBefore(a)}else{m.appendTo(a)}}var U=M('
      ').appendTo(m);var ad=M('