Plugin Directory

Changeset 865739


Ignore:
Timestamp:
02/26/2014 10:00:04 PM (12 years ago)
Author:
jond
Message:

Added 1.3.2 release

Location:
shopp/trunk
Files:
5 added
68 edited

Legend:

Unmodified
Added
Removed
  • shopp/trunk/Shopp.php

    r831831 r865739  
    44 * Plugin URI: http://shopplugin.com
    55 * Description: An ecommerce framework for WordPress.
    6  * Version: 1.3.1
     6 * Version: 1.3.2
    77 * Author: Ingenesis Limited
    88 * Author URI: http://ingenesis.net
     
    1010 * Tested up to: 3.5.2
    1111 *
    12  *    Portions created by Ingenesis Limited are Copyright © 2008-2013 by Ingenesis Limited
     12 *    Portions created by Ingenesis Limited are Copyright © 2008-2014 by Ingenesis Limited
    1313 *
    1414 *    This file is part of Shopp.
     
    2929 **/
    3030
    31 defined( 'WPINC' ) || header( 'HTTP/1.1 403' ) & exit; // Prevent direct access
     31// Prevent direct access
     32defined( 'WPINC' ) || header( 'HTTP/1.1 403' ) & exit;
    3233
     34// Start the bootloader
    3335require 'core/library/Loader.php';
    3436
    35 /**
    36  * Shopp core plugin management class
    37  *
    38  * @author Jonathan Davis
    39  * @since 1.0
    40  * @version 1.3
    41  * @package shopp
    42  **/
    43 class Shopp extends ShoppCore {
    44 
    45     private static $object = false;
    46 
    47     private function __construct () {
    48 
    49         $this->paths();             // Determine Shopp paths
    50         $this->constants();         // Setup Shopp constants
    51         $this->textdomain();        // Load the translation file
    52 
    53         // Load the Developer API
    54         ShoppDeveloperAPI::load( SHOPP_PATH );
    55 
    56         // Initialize error system
    57         ShoppErrors();
    58 
    59         // Initialize application control processing
    60         $this->Flow = new ShoppFlow();
    61 
    62         // Init deprecated properties for legacy add-on module compatibility
    63         $this->Shopping = ShoppShopping();
    64         $this->Settings = ShoppSettings();
    65 
    66         // Hooks
    67         add_action('init', array($this, 'init'));
    68 
    69         // Core WP integration
    70         add_action('shopp_init', array($this, 'pages'));
    71         add_action('shopp_init', array($this, 'collections'));
    72         add_action('shopp_init', array($this, 'taxonomies'));
    73         add_action('shopp_init', array($this, 'products'), 99);
    74         add_action('shopp_init', array($this, 'rebuild'), 99);
    75 
    76         add_filter('rewrite_rules_array', array($this, 'rewrites'));
    77         add_filter('query_vars', array($this, 'queryvars'));
    78 
    79         // Theme integration
    80         add_action('widgets_init', array($this, 'widgets'));
    81 
    82     }
    83 
    84     /**
    85      * Singleton accessor method
    86      *
    87      * @author Jonathan Davis
    88      * @since 1.3
    89      *
    90      * @return Shopp Provides the running Shopp object
    91      **/
    92     public static function object () {
    93         if ( ! self::$object instanceof self )
    94             self::$object = new self;
    95         return self::$object;
    96     }
    97 
    98     /**
    99      * Boot up the core plugin
    100      *
    101      * @author Jonathan Davis
    102      * @since 1.3
    103      *
    104      * @return void
    105      **/
    106     public static function plugin () {
    107         global $Shopp; // Provide global for backwards compatibility
    108         $Shopp = Shopp::object();
    109         do_action('shopp_loaded');
    110     }
    111 
    112     /**
    113      * Initializes the Shopp runtime environment
    114      *
    115      * @author Jonathan Davis
    116      * @since 1.0
    117      *
    118      * @return void
    119      **/
    120     public function init () {
    121 
    122         $this->Collections = array();
    123         $this->Order = new ShoppOrder();
    124         $this->Gateways = new GatewayModules();
    125         $this->Shipping = new ShippingModules();
    126         $this->Storage = new StorageEngines();
    127         $this->APIs = new ShoppAPIModules();
    128 
    129         new ShoppLogin();
    130         do_action('shopp_init');
    131     }
    132 
    133     /**
    134      * Setup configurable constants
    135      *
    136      * @author Jonathan Davis
    137      * @since 1.3
    138      *
    139      * @return void
    140      **/
    141     public function constants () {
    142         if ( ! defined('SHOPP_VERSION') )               define( 'SHOPP_VERSION', ShoppVersion::release() );
    143         if ( ! defined('SHOPP_GATEWAY_USERAGENT') )     define( 'SHOPP_GATEWAY_USERAGENT', ShoppVersion::agent() );
    144 
    145         // @deprecated
    146         if ( ! defined('SHOPP_HOME') )                  define( 'SHOPP_HOME', ShoppSupport::HOMEPAGE );
    147         if ( ! defined('SHOPP_CUSTOMERS') )             define( 'SHOPP_CUSTOMERS', ShoppSupport::FORUMS);
    148         if ( ! defined('SHOPP_DOCS') )                  define( 'SHOPP_DOCS', ShoppSupport::DOCS );
    149 
    150         // Helper for line break output
    151         if ( ! defined('BR') )                          define('BR', '<br />');
    152 
    153         // Overrideable config macros
    154         if ( ! defined('SHOPP_NOSSL') )                 define('SHOPP_NOSSL', false);                   // Require SSL to protect transactions, overrideable for development
    155         if ( ! defined('SHOPP_PREPAYMENT_DOWNLOADS') )  define('SHOPP_PREPAYMENT_DOWNLOADS', false);    // Require payment capture granting access to downloads
    156         if ( ! defined('SHOPP_SESSION_TIMEOUT') )       define('SHOPP_SESSION_TIMEOUT', 172800);        // Sessions live for 2 days
    157         if ( ! defined('SHOPP_CART_EXPIRES') )          define('SHOPP_CART_EXPIRES', 1209600);          // Carts are stashed for up to 2 weeks
    158         if ( ! defined('SHOPP_QUERY_DEBUG') )           define('SHOPP_QUERY_DEBUG', false);             // Debugging queries is disabled by default
    159         if ( ! defined('SHOPP_GATEWAY_TIMEOUT') )       define('SHOPP_GATEWAY_TIMEOUT', 10);            // Gateway connections timeout after 10 seconds
    160         if ( ! defined('SHOPP_SHIPPING_TIMEOUT') )      define('SHOPP_SHIPPING_TIMEOUT', 10);           // Shipping provider connections timeout after 10 seconds
    161         if ( ! defined('SHOPP_TEMP_PATH') )             define('SHOPP_TEMP_PATH', sys_get_temp_dir());  // Use the system defined temporary directory
    162         if ( ! defined('SHOPP_ADDONS') )                define('SHOPP_ADDONS', WP_CONTENT_DIR . '/shopp-addons');   // A configurable directory to keep Shopp addons
    163         if ( ! defined('SHOPP_NAMESPACE_TAXONOMIES') )  define('SHOPP_NAMESPACE_TAXONOMIES', true);     // Add taxonomy namespacing for permalinks /shop/category/category-name, /shopp/tag/tag-name
    164 
    165     }
    166 
    167     /**
    168      * Setup path related constants
    169      *
    170      * @author Jonathan Davis
    171      * @since 1.3
    172      *
    173      * @return void
    174      **/
    175     public function paths () {
    176 
    177         $path = sanitize_path(dirname(__FILE__));
    178         $file = basename(__FILE__);
    179         $directory = basename($path);
    180 
    181         // Paths
    182         define('SHOPP_PATH', $path );
    183         define('SHOPP_DIR', $directory );
    184         define('SHOPP_PLUGINFILE', "$directory/$file" );
    185         define('SHOPP_PLUGINURI', set_url_scheme(WP_PLUGIN_URL . "/$directory") );
    186 
    187         define('SHOPP_ADMIN_DIR', '/core/ui');
    188         define('SHOPP_ADMIN_PATH', SHOPP_PATH . SHOPP_ADMIN_DIR);
    189         define('SHOPP_ADMIN_URI',  SHOPP_PLUGINURI . SHOPP_ADMIN_DIR);
    190         define('SHOPP_ICONS_URI',  SHOPP_ADMIN_URI . '/icons');
    191         define('SHOPP_FLOW_PATH',  SHOPP_PATH . '/core/flow');
    192         define('SHOPP_MODEL_PATH', SHOPP_PATH . '/core/model');
    193         define('SHOPP_GATEWAYS',   SHOPP_PATH . '/gateways');
    194         define('SHOPP_SHIPPING',   SHOPP_PATH . '/shipping');
    195         define('SHOPP_STORAGE',    SHOPP_PATH . '/storage');
    196         define('SHOPP_THEME_APIS', SHOPP_PATH . '/api/theme');
    197         define('SHOPP_DBSCHEMA',   SHOPP_MODEL_PATH . '/schema.sql');
    198 
    199     }
    200 
    201     /**
    202      * Load the text domain translation file
    203      *
    204      * @author Jonathan Davis
    205      * @since 1.3
    206      * @uses SHOPP_LANG_DIR
    207      * @uses SHOPP_ADDONS
    208      * @uses SHOPP_DIR
    209      *
    210      * @return void
    211      **/
    212     public function textdomain () {
    213 
    214         if ( ! defined('SHOPP_LANG_DIR') )  // Add configurable path for language files
    215             define('SHOPP_LANG_DIR', ( is_dir(SHOPP_ADDONS . '/languages') ? SHOPP_ADDONS . '/languages' : SHOPP_DIR . '/lang' ) );
    216 
    217         load_textdomain(__CLASS__, SHOPP_LANG_DIR . '/' . __CLASS__ . '-' . get_locale() . '.mo');
    218 
    219     }
    220 
    221     /**
    222      * Sets up permalink handling for ShoppStorefront pages
    223      *
    224      * @author Jonathan Davis
    225      * @since 1.2
    226      *
    227      * @return void
    228      **/
    229     public function pages () {
    230 
    231         shopp_register_page( 'ShoppCatalogPage' );
    232         shopp_register_page( 'ShoppAccountPage' );
    233         shopp_register_page( 'ShoppCartPage' );
    234         shopp_register_page( 'ShoppCheckoutPage' );
    235         shopp_register_page( 'ShoppConfirmPage' );
    236         shopp_register_page( 'ShoppThanksPage' );
    237 
    238         do_action( 'shopp_init_storefront_pages' );
    239 
    240     }
    241 
    242     /**
    243      * Register smart collections
    244      *
    245      * @author Jonathan Davis
    246      * @since 1.3
    247      *
    248      * @return void
    249      **/
    250     public function collections () {
    251 
    252         shopp_register_collection( 'CatalogProducts' );
    253         shopp_register_collection( 'NewProducts' );
    254         shopp_register_collection( 'FeaturedProducts' );
    255         shopp_register_collection( 'OnSaleProducts' );
    256         shopp_register_collection( 'BestsellerProducts' );
    257         shopp_register_collection( 'SearchResults' );
    258         shopp_register_collection( 'MixProducts' );
    259         shopp_register_collection( 'TagProducts' );
    260         shopp_register_collection( 'RelatedProducts' );
    261         shopp_register_collection( 'AlsoBoughtProducts' );
    262         shopp_register_collection( 'ViewedProducts' );
    263         shopp_register_collection( 'RandomProducts' );
    264         shopp_register_collection( 'PromoProducts' );
    265 
    266     }
    267 
    268     /**
    269      * Register custom taxonomies
    270      *
    271      * @author Jonathan Davis
    272      * @since 1.3
    273      *
    274      * @return void
    275      **/
    276     public function taxonomies () {
    277         ProductTaxonomy::register( 'ProductCategory' );
    278         ProductTaxonomy::register( 'ProductTag' );
    279     }
    280 
    281     /**
    282      * Register the product custom post type
    283      *
    284      * @author Jonathan Davis
    285      * @since 1.3
    286      *
    287      * @return void
    288      **/
    289     public function products () {
    290         WPShoppObject::register( 'ShoppProduct', ShoppPages()->baseslug() );
    291     }
    292 
    293     /**
    294      * Registers theme widgets
    295      *
    296      * @author Jonathan Davis
    297      * @since 1.0
    298      * @version 1.3
    299      *
    300      * @return void
    301      **/
    302     public function widgets () {
    303 
    304         register_widget( 'ShoppAccountWidget' );
    305         register_widget( 'ShoppCartWidget' );
    306         register_widget( 'ShoppCategoriesWidget' );
    307         register_widget( 'ShoppFacetedMenuWidget' );
    308         register_widget( 'ShoppProductWidget' );
    309         register_widget( 'ShoppSearchWidget' );
    310         register_widget( 'ShoppCategorySectionWidget' );
    311         register_widget( 'ShoppShoppersWidget' );
    312         register_widget( 'ShoppTagCloudWidget' );
    313 
    314     }
    315 
    316     /**
    317      * Adds Shopp-specific mod_rewrite rule for low-resource, speedy image server and downloads request handler
    318      *
    319      * @author Jonathan Davis
    320      * @since 1.0
    321      *
    322      * @param array $wp_rewrite_rules An array of existing WordPress rewrite rules
    323      * @return array Rewrite rules
    324      **/
    325     public function rewrites ($wp_rewrite_rules) {
    326         global $is_IIS;
    327         $structure = get_option('permalink_structure');
    328         if ( '' == $structure ) return $wp_rewrite_rules;
    329 
    330         $path = str_replace('%2F', '/', urlencode(join('/', array(PLUGINDIR, SHOPP_DIR, 'services'))));
    331 
    332         // Download URL rewrites
    333         $AccountPage = ShoppPages()->get('account');
    334         $downloads = array( ShoppPages()->baseslug(), $AccountPage->slug(), 'download', '([a-f0-9]{40})', '?$' );
    335         if ( $is_IIS && 0 === strpos($structure, '/index.php/') ) array_unshift($downloads, 'index.php');
    336         $rules = array( join('/', $downloads)
    337                 => 'index.php?src=download&shopp_download=$matches[1]',
    338         );
    339 
    340         // Image URL rewrite
    341         $images = array( ShoppPages()->baseslug(), 'images', '(\d+)', "?\??(.*)$" );
    342         add_rewrite_rule(join('/', $images), $path . '/image.php?siid=$1&$2');
    343 
    344         return $rules + (array) $wp_rewrite_rules;
    345     }
    346 
    347     /**
    348      * Force rebuilding rewrite rules when necessary
    349      *
    350      * @author Jonathan Davis
    351      * @since 1.2
    352      *
    353      * @return void
    354      **/
    355     public function rebuild () {
    356         if ( ! shopp_setting_enabled('rebuild_rewrites') ) return;
    357 
    358         flush_rewrite_rules();
    359         shopp_set_setting('rebuild_rewrites', 'off');
    360     }
    361 
    362     /**
    363      * Registers the query variables used by Shopp
    364      *
    365      * @author Jonathan Davis
    366      * @since 1.0
    367      * @version 1.2
    368      *
    369      * @param array $vars The current list of handled WordPress query vars
    370      * @return array Augmented list of query vars including Shopp vars
    371      **/
    372     public function queryvars ($vars) {
    373 
    374         $vars[] = 's_iid';          // Shopp image id
    375         $vars[] = 's_cs';           // Catalog (search) flag
    376         $vars[] = 's_ff';           // Category filters
    377         $vars[] = 'src';            // Shopp resource
    378         $vars[] = 'shopp_page';
    379         $vars[] = 'shopp_download';
    380 
    381         return $vars;
    382     }
    383 
    384     /**
    385      * Handles request services like the image server and script server
    386      *
    387      * @author Jonathan Davis
    388      * @since 1.3
    389      *
    390      * @return boolean The service load status
    391      **/
    392     public static function services () {
    393         if ( WP_DEBUG ) define('SHOPP_MEMORY_PROFILE_BEFORE', memory_get_peak_usage(true) );
    394 
    395         // Image Server request handling
    396         if ( isset($_GET['siid']) || 1 == preg_match('{^/.+?/images/\d+/.*$}', $_SERVER['REQUEST_URI']) )
    397             return require 'services/image.php';
    398 
    399         // Script Server request handling
    400         if ( isset($_GET['sjsl']) )
    401             return require 'services/scripts.php';
    402     }
    403 
    404     // Deprecated properties
    405 
    406     public $Settings;       // @deprecated Shopp settings registry
    407     public $Flow;           // @deprecated Controller routing
    408     public $Catalog;        // @deprecated The main catalog
    409     public $Category;       // @deprecated Current category
    410     public $Product;        // @deprecated Current product
    411     public $Purchase;       // @deprecated Currently requested order receipt
    412     public $Shopping;       // @deprecated The shopping session
    413     public $Errors;         // @deprecated Error system
    414     public $Order;          // @deprecated The current session Order
    415     public $Promotions;     // @deprecated Active promotions registry
    416     public $Collections;    // @deprecated Collections registry
    417     public $Gateways;       // @deprecated Gateway modules
    418     public $Shipping;       // @deprecated Shipping modules
    419     public $APIs;           // @deprecated Loaded API modules
    420     public $Storage;        // @deprecated Storage engine modules
    421 
    422 } // END class Shopp
    423 
    424 if ( Shopp::services() || Shopp::unsupported() ) return; // Prevent loading the plugin
     37// Prevent loading the plugin in special circumstances
     38if ( Shopp::services() || Shopp::unsupported() ) return;
    42539
    42640/* Start the core */
  • shopp/trunk/api/collection.php

    r821385 r865739  
    4141    $slugs = SmartCollection::slugs($name);
    4242
    43     add_rewrite_tag( "%$permastruct%", "$namespace/([^/]+)" );
    44     add_permastruct( $permastruct, ShoppPages()->baseslug() . '/%shopp_collection%', false );
     43    add_rewrite_tag( "%$permastruct%", "([^/]+)" );
     44    add_permastruct( $permastruct, ShoppPages()->baseslug() . "/$namespace/%shopp_collection%", false );
    4545
    4646    add_filter( $permastruct . '_rewrite_rules', array('ProductCollection', 'pagerewrites') );
     
    5656        add_filter( 'shopp_themeapi_storefront_' . $collection . 'collection', $apicall, 10, 3 );
    5757    }
     58
     59    // Add special default permalink handling for collection URLs (only add it once)
     60    global $wp_rewrite;
     61    if ( ! $wp_rewrite->using_permalinks() && false === has_filter('term_link', array('SmartCollection', 'defaultlinks')) )
     62        add_filter('term_link', array('SmartCollection', 'defaultlinks'), 10, 3);
    5863
    5964}
     
    7782
    7883    $args['rewrite']['slug'] = SHOPP_NAMESPACE_TAXONOMIES ? ShoppPages()->baseslug().'/'.$rewrite_slug : $rewrite_slug;
    79     register_taxonomy($taxonomy,ShoppProduct::$posttype,$args);
     84    register_taxonomy($taxonomy, ShoppProduct::$posttype,$args);
    8085}
    8186
  • shopp/trunk/api/core.php

    r821385 r865739  
    193193
    194194/**
    195  * Determines if the requested page is the catalog front page.
     195 * Determines if the requested page is the storefront catalog page
    196196 *
    197197 * @author Jonathan Davis
     
    393393    if ( false === $wp_query ) { global $wp_the_query; $wp_query = $wp_the_query; }
    394394
     395    if ( empty($wp_query->tax_query) ) return false; // No taxonomy request {@see #2748}
     396
    395397    $object = $wp_query->get_queried_object();
     398
    396399    $taxonomies = get_object_taxonomies(ShoppProduct::$posttype, 'names');
    397400
  • shopp/trunk/api/theme/cart.php

    r831831 r865739  
    119119    public static function applycode ( $result, $options, $O ) {
    120120
    121         $submit_attrs = array('title', 'value', 'disabled', 'tabindex', 'accesskey', 'class');
     121        $submit_attrs = array( 'title', 'value', 'disabled', 'tabindex', 'accesskey', 'class', 'autocomplete', 'placeholder', 'required' );
    122122
    123123        // Skip if discounts are not available
     
    149149    public static function applygiftcard ( $result, $options, $O ) {
    150150
    151         $submit_attrs = array('title', 'value', 'disabled', 'tabindex', 'accesskey', 'class');
     151        $submit_attrs = array( 'title', 'value', 'disabled', 'tabindex', 'accesskey', 'class', 'autocomplete', 'placeholder', 'required' );
    152152
    153153        if ( ! isset($options['value']) ) $options['value'] = Shopp::__('Add Gift Card');
     
    267267
    268268    public static function empty_button ( $result, $options, $O ) {
    269         $submit_attrs = array('title', 'value', 'disabled', 'tabindex', 'accesskey', 'class');
     269        $submit_attrs = array( 'title', 'value', 'disabled', 'tabindex', 'accesskey', 'class', 'autocomplete', 'placeholder', 'required' );
    270270        if ( ! isset($options['value']) ) $options['value'] = __('Empty Cart', 'Shopp');
    271271        return '<input type="submit" name="empty" id="empty-button" ' . inputattrs($options,$submit_attrs) . ' />';
     
    467467
    468468    public static function update_button ( $result, $options, $O ) {
    469         $submit_attrs = array('title', 'value', 'disabled', 'tabindex', 'accesskey', 'class');
     469        $submit_attrs = array( 'title', 'value', 'disabled', 'tabindex', 'accesskey', 'class', 'autocomplete', 'placeholder', 'required' );
    470470        if ( ! isset($options['value']) ) $options['value'] = __('Update Subtotal', 'Shopp');
    471471        if ( isset($options['class']) ) $options['class'] .= ' update-button';
  • shopp/trunk/api/theme/cartitem.php

    r821385 r865739  
    121121    public static function product ( $result, $options, $O ) {
    122122        if ( isset($options['priceline']) && Shopp::str_true($options['priceline']) )
    123             return $O->$priceline;
     123            return $O->priceline;
    124124        return $O->product;
    125125    }
  • shopp/trunk/api/theme/checkout.php

    r831831 r865739  
    486486        /// Allowable attributes for textarea inputs
    487487        $textarea_attrs = array('accesskey', 'title', 'tabindex', 'class', 'disabled', 'required');
    488         $select_attrs = array('title', 'required', 'class', 'disabled', 'required', 'size', 'tabindex', 'accesskey');
     488        $select_attrs = array( 'title', 'required', 'class', 'disabled', 'required', 'size', 'tabindex', 'accesskey', 'placeholder' );
    489489
    490490        if ( ! $name ) {// Iterator for customer info fields
  • shopp/trunk/api/theme/collection.php

    r831831 r865739  
    390390
    391391    public static function has_images ( $result, $options, $O ) {
     392
    392393        if ( ! is_a($O, 'ProductCategory') ) return false;
    393         if ( empty($O->images) ) $O->load_images();
    394         reset($O->images);
    395         if ( empty($O->images) ) return false;
    396         return true;
     394
     395        if ( empty($O->images) ) {
     396            $O->load_images();
     397            reset($O->images);
     398        }
     399
     400        return ( ! empty($O->images) );
     401
    397402    }
    398403
     
    412417     **/
    413418    public static function image ( $result, $options, $O ) {
    414         if (!self::has_images( $result, $options, $O )) return '';
     419        if ( ! self::has_images( $result, $options, $O )) return '';
    415420        return ShoppStorefrontThemeAPI::image( $result, $options, $O );
    416421    }
    417422
    418423    public static function images ( $result, $options, $O ) {
    419         if (!isset($O->_images_loop)) {
     424        if ( ! isset($O->_images_loop) ) {
    420425            reset($O->images);
    421426            $O->_images_loop = true;
    422427        } else next($O->images);
    423428
    424         if (current($O->images) !== false) return true;
     429        if ( current($O->images) !== false ) return true;
    425430        else {
    426431            unset($O->_images_loop);
     
    669674
    670675    public static function url ( $result, $options, $O ) {
    671         global $wp_rewrite;
    672 
    673         $namespace = get_class_property( get_class($O) ,'namespace');
    674         $prettyurls = $wp_rewrite->using_permalinks();
    675 
    676         $url = Shopp::url( $prettyurls ? "$namespace/$O->slug" : array($O->taxonomy => $O->slug), false );
     676        $url = get_term_link($O);
    677677        if ( isset($options['page']) ) $url = $O->pagelink((int)$options['page']);
    678678        return $url;
  • shopp/trunk/api/theme/customer.php

    r831831 r865739  
    220220        if ( array_key_exists('filetype', $options) ) $string .= $download->mime;
    221221        if ( array_key_exists('size', $options) ) $string .= readableFileSize($download->size);
    222         if ( array_key_exists('date', $options) ) $string .= _d($df, mktimestamp($download->created));
     222        if ( array_key_exists('date', $options) ) $string .= _d($df, $download->created);
    223223        if ( array_key_exists('url', $options) )
    224224            $string .= Shopp::url( ('' == get_option('permalink_structure') ?
  • shopp/trunk/api/theme/product.php

    r831831 r865739  
    140140            'discounts' => 'on',
    141141            'taxes' => null,
    142             'input' => null
     142            'input' => null,
     143            'money' => 'on',
     144            'number' => 'off'
    143145        );
    144146        $options = array_merge($defaults, $options);
     
    160162        if ( array_key_exists('sku', $options) )        $_[] = $addon->sku;
    161163
    162         if ( array_key_exists('price', $options) )
    163             $_[] = money(self::_taxed((float)$addon->price, $O, $addon->tax, $taxes));
     164        if ( array_key_exists('price', $options) ) {
     165            $price = Shopp::roundprice(self::_taxed((float)$addon->price, $O, $addon->tax, $taxes));
     166            if ( Shopp::str_true($money) ) $_[] = Shopp::money($price);
     167            else $_[] = $price;
     168        }
    164169
    165170        if ( array_key_exists('saleprice', $options) ) {
    166             if ( Shopp::str_true($discounts) )
    167                 $_[] = money(self::_taxed((float)$addon->promoprice, $O, $addon->tax, $taxes));
    168             else $_[] = money(self::_taxed((float)$addon->saleprice, $O, $addon->tax, $taxes));
     171            $saleprice = Shopp::str_true($discounts) ? $addon->promoprice : $addon->saleprice;
     172            $saleprice = Shopp::roundprice( self::_taxed((float)$addon->promoprice, $O, $addon->tax, $taxes) );
     173            if ( Shopp::str_true($money) ) $_[] = Shopp::money($saleprice);
     174            else $_[] = $saleprice;
    169175        }
    170176
     
    172178        if ( array_key_exists('weight', $options) )
    173179            $_[] = round($addon->weight, 3) . (false !== $weightunit ? " $weightunit" : false);
    174         if ( array_key_exists('shipfee', $options) )
    175             $_[] = money(Shopp::floatval($addon->shipfee));
     180
     181        if ( array_key_exists('shipfee', $options) ) {
     182            $shipfee = Shopp::roundprice($addon->shipfee);
     183            if ( Shopp::str_true($money) ) $_[] = Shopp::money($shipfee);
     184            else $_[] = $shipfee;
     185        }
     186
    176187        if ( array_key_exists('sale', $options) )
    177188            return Shopp::str_true($addon->sale);
     
    425436        unset($options['id']);
    426437        $options['index'] = 0;
     438        $options['load'] = 'coverimages';
    427439        return self::image( $result, $options, $O );
    428440    }
     
    503515
    504516        // Populate defaults from named settings, if provided
    505         $ImageSettings = ImageSettings::__instance();
     517        $ImageSettings = ImageSettings::object();
    506518
    507519        if ( ! empty($options['p_setting']) ) {
     
    551563        // Setup preview images
    552564        $previews = '';
     565
     566        if ( 'transparent' == strtolower($p_bg) ) $fill = -1;
     567        else $fill = $p_bg ? hexdec(ltrim($p_bg, '#')) : false;
     568
     569        $lowest_quality = min(ImageSetting::$qualities);
     570
     571        $scale = $p_fit ? array_search($p_fit, ImageAsset::$defaults['scaling']) : false;
     572        $sharpen = $p_sharpen ? max($p_sharpen, ImageAsset::$defaults['sharpen']) : false;
     573        $quality = $p_quality ? max($p_quality, $lowest_quality) : false;
     574
    553575        foreach ( $O->images as $Image ) {
    554576            $firstPreview = false;
     
    556578                $firstPreview = $previews .=
    557579                    '<li class="fill">' .
    558                     '<img src="' .  Shopp::clearpng() . '" alt="" style="width: ' . (int) $maxwidth . 'px; height: auto;" />' .
     580                    '<img src="' .  Shopp::clearpng() . '" alt="" style="width: ' . (int) $maxwidth . 'px; height: ' . (int) $maxheight . 'px;" />' .
    559581                    '</li>';
    560582            }
    561 
    562             $scale = $p_fit ? array_search($p_fit, ImageAsset::$defaults['scaling']) : false;
    563             $sharpen = $p_sharpen ? min($p_sharpen, ImageAsset::$defaults['sharpen']) : false;
    564             $quality = $p_quality ? min($p_quality, ImageAsset::$defaults['quality']) : false;
    565 
    566             if ( 'transparent' == strtolower($p_bg) ) $fill = -1;
    567             else $fill = $p_bg ? hexdec(ltrim($p_bg, '#')) : false;
    568583
    569584            $scaled = $Image->scaled($width, $height, $scale);
     
    575590            $img = '<img src="' . $src . '"' . $titleattr . ' alt="' . $alt . '" width="' . (int) $scaled['width'] . '" height="' . (int) $scaled['height'] . '" />';
    576591
    577 
    578592            if ( $p_link ) {
    579 
    580593                $hrefattr = $Image->url();
    581594                $relattr = empty($rel) ? '' : ' rel="' . esc_attr($rel) . '"';
     
    583596
    584597                $img = '<a href="' . $hrefattr . '" class="' . join(' ', $linkclasses) . '"' . $relattr . $titleattr . '>' . $img . '</a>';
    585 
    586598            }
    587599
     
    728740     **/
    729741    public static function image ( $result, $options, $O ) {
    730         if ( empty($O->images) ) $O->load_data( array('images') );
     742        $loadset = array('images', 'coverimages');
     743        if ( empty($options['load']) || ! in_array($options['load'], $loadset) )
     744            $options['load'] = $loading[0];
     745
     746        // Load images if no images are loaded or we're loading all images after the coverimage was loaded
     747        if ( empty($O->images) || 'images' == $options['load'] )
     748            $O->load_data( array($options['load']) );
     749
    731750        return ShoppStorefrontThemeAPI::image( $result, $options, $O );
    732751    }
     
    807826            if ( isset($options['rows']) ) $rows = ' rows="' . (int)$options['rows'] . '"';
    808827
    809             $result .= '<textarea name="products[' . (int)$O->id . '][data][' . esc_attr($name) . ']" id="'.$id.'"'.$cols.$rows.inputattrs($options).'>'.esc_html($value).'</textarea>';
     828            $result = '<textarea name="products[' . (int)$O->id . '][data][' . esc_attr($name) . ']" id="'.$id.'"'.$cols.$rows.inputattrs($options).'>'.esc_html($value).'</textarea>';
    810829
    811830        } else {
     
    11361155
    11371156    public static function summary ( $result, $options, $O ) {
    1138         return apply_filters('shopp_product_summary',$O->summary);
     1157        $summary = $O->summary;
     1158
     1159        $overflow = isset($options['overflow']) ? esc_html($options['overflow']) : '&hellip;';
     1160
     1161        if ( ! empty($options['clip']) && strlen($O->summary) > (int)$options['clip'] )
     1162            $summary = substr($summary, 0, strpos(wordwrap($summary, (int)$options['clip'], "\b"), "\b")) . $overflow;
     1163
     1164        return apply_filters('shopp_product_summary', $summary);
    11391165    }
    11401166
     
    11981224            'promos' => 'on',
    11991225            'discounts' => 'on',
    1200             'taxes' => null
     1226            'taxes' => null,
     1227            'money' => 'on',
     1228            'number' => 'off'
    12011229        );
    12021230        $options = array_merge($defaults, $options);
     
    12141242        if ( array_key_exists('stock', $options) )  $_[] = $variation->stock;
    12151243
    1216         if ( array_key_exists('price', $options) )
    1217             $_[] = money(self::_taxed((float)$variation->price, $O, $variation->tax, $taxes));
     1244        if ( array_key_exists('price', $options) ) {
     1245            $price = Shopp::roundprice(self::_taxed((float)$variation->price, $O, $variation->tax, $taxes));
     1246            if ( Shopp::str_true($money) ) $_[] = money($price);
     1247            else $_[] = $price;
     1248        }
    12181249
    12191250        if ( array_key_exists('saleprice', $options) ) {
    1220             if (Shopp::str_true($discounts) ) $_[] = money(self::_taxed((float)$variation->promoprice, $O, $variation->tax, $taxes));
    1221             else $_[] = money(self::_taxed((float)$variation->saleprice, $O, $variation->tax, $taxes));
     1251            $saleprice = Shopp::str_true($discounts) ? $variation->promoprice : $variation->saleprice;
     1252            $saleprice = Shopp::roundprice( self::_taxed((float)$saleprice, $O, $variation->tax, $taxes) );
     1253            if ( Shopp::str_true($money) ) $_[] = money($saleprice);
     1254            else $_[] = $saleprice;
    12221255        }
    12231256
     
    12251258            $_[] = round($variation->weight, 3) . ($weightunit ? " $weightunit" : false);
    12261259
    1227         if ( array_key_exists('shipfee', $options) )
    1228             $_[] = money(Shopp::floatval($variation->shipfee));
     1260        if ( array_key_exists('shipfee', $options) ) {
     1261            $shipfee = Shopp::roundprice($variation->shipfee);
     1262            if ( Shopp::str_true($money) ) $_[] = money($shipfee);
     1263            else $_[] = $shipfee;
     1264        }
    12291265
    12301266        if ( array_key_exists('sale', $options) )
  • shopp/trunk/api/theme/purchase.php

    r831831 r865739  
    201201    public static function data ( $result, $options, $O ) {
    202202        if ( ! is_array($O->data) ) return false;
     203
    203204        $data = current($O->data);
    204205        $name = key($O->data);
    205         if ( isset($options['name']) ) return esc_html($name);
     206
     207        if ( ! empty($options['name']) ) {
     208            if ( isset($O->data[ $options['name'] ]) )
     209                $data = $O->data[ $options['name'] ];
     210            else $data = false;
     211        } elseif ( isset($options['name']) ) return esc_html($name);
     212
    206213        return apply_filters('shopp_purchase_order_data', $data);
    207214    }
  • shopp/trunk/api/theme/storefront.php

    r831831 r865739  
    126126        if ( ! empty($options['setting']) ) {
    127127            $setting = $options['setting'];
    128             $ImageSettings = ImageSettings::__instance();
     128            $ImageSettings = ImageSettings::object();
    129129            $settings = $ImageSettings->get($setting);
    130130            if ( $settings ) $defaults = array_merge($defaults, $settings->options());
     
    138138            if ( isset($O->images[ $id ]) ) $Image = $O->images[ $id ];
    139139            else {
    140                 shopp_debug( sprintf('No %s image exists at with the specified database ID of %d.', get_class($O), $id) );
     140                shopp_debug( sprintf('No %s image exists with the specified database ID of %d.', get_class($O), $id) );
    141141                return '';
    142142            }
     
    401401        $options = array_merge($defaults, $options);
    402402
    403         $options['style'] = 'list';
     403        $options['style'] = '';
    404404        if ( Shopp::str_true($options['dropdown']) )
    405405            $options['style'] = 'dropdown';
    406         elseif ( ! Shopp::str_true($options['hierarchy']) || ! Shopp::str_true($options['wraplist']) )
    407             $options['style'] = '';
     406        elseif ( Shopp::str_true($options['hierarchy']) || Shopp::str_true($options['wraplist']) )
     407            $options['style'] = 'list';
    408408
    409409        if ( ! empty($options['showsmart']) && empty($options['smart']) )
     
    485485            $Collection->slug = $slug;
    486486            $Collection->term_group = 0;
    487             $Collection->taxonomy = 'shopp_collection';
     487            $Collection->taxonomy = get_class_property('SmartCollection','taxon');
    488488            $Collection->description = '';
    489489            $Collection->parent = 0;
     
    11771177        $smartcollection = $category->taxonomy == get_class_property( 'SmartCollection', 'taxon');
    11781178
    1179         if ( $smartcollection ) {
    1180             global $wp_rewrite;
    1181             $termlink = $wp_rewrite->get_extra_permastruct($category->taxonomy);
    1182             if ( ! empty($termlink) ) $category->slug = get_class_property( 'SmartCollection', 'namespace') . '/' . $category->slug;
    1183         }
    1184 
    11851179        $categoryname = $category->name;
     1180
    11861181        $link = get_term_link($category);
     1182
    11871183        $classes = '';
    11881184        if ( 'list' == $args['style'] ) {
    11891185            $classes = 'cat-item cat-item-' . $category->term_id;
    1190             if ( ! empty($current_category) ) {
    1191                 $_current_category = get_term( $current_category, $category->taxonomy );
    1192                 if ( $category->term_id == $current_category )
    1193                     $classes .=  ' current-cat current';
    1194                 elseif ( $category->term_id == $_current_category->parent )
    1195                     $classes .=  ' current-cat-parent current-parent';
    1196             }
    1197         }
     1186
     1187            $Collection = ShoppCollection();
     1188            if ( isset($Collection->slug) && $Collection->slug == $category->slug)
     1189                $classes .= ' current-cat current';
     1190
     1191            if ( ! empty($Collection->parent) && $Collection->parent == $category->term_id)
     1192                $classes .= ' current-cat-parent current-parent';
     1193        }
     1194
    11981195        $total = isset($category->count) ? $category->count : false;
    11991196
     
    12031200        extract($filtered, EXTR_OVERWRITE);
    12041201
    1205         $link = '<a href="' . esc_url( $link ) . '" title="' . esc_attr( $title ) . '"';
     1202        $link = '<a href="' . esc_url( $link ) . '" title="' . esc_attr( $title ) . '" class="' . $classes . '"';
    12061203        $link .= '>';
    12071204        $link .= $categoryname . '</a>';
     
    12151212        if ( 'list' == $args['style'] ) {
    12161213            $output .= "\t<li";
    1217             if ( ! empty($class) ) $output .=  ' class="' . $class . '"';
     1214            if ( ! empty($classes) ) $output .=  ' class="' . $classes . '"';
    12181215            $output .= ">$link\n";
    12191216        } else {
     
    12681265        $pad = str_repeat('&nbsp;', $depth * 3);
    12691266
     1267        $link = get_term_link($category);
     1268
    12701269        $cat_name = apply_filters('shopp_storefront_categorylist_option', $category->name, $category);
    1271         $output .= "\t<option class=\"level-$depth\" value=\"".$category->slug."\"";
     1270        $output .= "\t<option class=\"level-$depth\" value=\"" . $link . "\"";
    12721271        if ( $category->term_id == $args['selected'] )
    12731272            $output .= ' selected="selected"';
    12741273        $output .= '>';
    12751274        $output .= $pad.$cat_name;
    1276         if ( $args['products'] )
     1275        if ( $args['products'] && isset($category->count) )
    12771276            $output .= '&nbsp;&nbsp;('. $category->count .')';
    12781277        $output .= "</option>\n";
  • shopp/trunk/core/flow/Admin.php

    r821385 r865739  
    492492        add_action('after_plugin_row_' . SHOPP_PLUGINFILE, array('ShoppSupport', 'pluginsnag'), 10, 2);
    493493
    494         $updates = array('load-plugins', 'load-update.php', 'load-update-core.php', 'wp_update_plugins', 'shopp_check_updates');
    495         foreach ( $updates as $action )
    496             add_action($action, array('ShoppSupport', 'updates'));
    497 
    498494    }
    499495
  • shopp/trunk/core/flow/Ajax.php

    r831831 r865739  
    747747    }
    748748
    749     // @todo Investigate if it is possible to inject a formatted error log message as an XSS vector
     749    /**
     750     * Automatic refresh of the log is possible when $_REQUEST['refresh'] is set to something other than 'off'.
     751     *
     752     * @todo Investigate if it is possible to inject a formatted error log message as an XSS vector
     753     */
    750754    public function logviewer () {
    751755        check_admin_referer('wp_ajax_shopp_debuglog'); ?>
    752756        <html>
    753757        <head>
    754         <?php if ( ! isset( $_REQUEST['refresh'] ) && 'off' === $_REQUEST['refresh'] ): ?>
     758        <?php if ( isset( $_REQUEST['refresh'] ) && 'off' !== $_REQUEST['refresh'] ): ?>
    755759        <meta http-equiv="refresh" content="10">
    756760        <?php endif; ?>
  • shopp/trunk/core/flow/Checkout.php

    r831831 r865739  
    107107        $this->Register->shipaddress();
    108108
    109         if ( $Cart->shipped() ) do_action('shopp_update_destination');
     109        if ( $Cart->shipped() )
     110            do_action('shopp_update_destination');
    110111
    111112    }
     
    132133
    133134        $selection = $this->form('shipmethod');
    134         if ( $selection == $Shiprates->selected()->slug ) return;
     135        $selected = isset($Shiprates->selected()->slug) ? $Shiprates->selected()->slug : '';
     136        if ( $selection == $selected ) return;
    135137
    136138        // Verify shipping method exists first
     
    151153
    152154        // Special case for updating/tracking billing locale
     155        $form = $this->form('billing');
    153156        if ( ! empty($form['locale']) )
    154157            $BillingAddress->locale = $form['locale'];
    155158
    156         if ( ! $Cart->shipped() || ! empty($form['locale']) )
     159        if ( ! $Cart->shipped() || ! empty($form['locale']) || 'shipping' == ShoppOrder()->sameaddress )
    157160            do_action('shopp_update_destination');
    158161
  • shopp/trunk/core/flow/Dashboard.php

    r821385 r865739  
    266266        $purchasetable = ShoppDatabaseObject::tablename(ShoppPurchase::$table);
    267267        $purchasedtable = ShoppDatabaseObject::tablename(Purchased::$table);
     268        $txnstatus_labels = Lookup::txnstatus_labels();
    268269
    269270        if ( ! ( $Orders = get_transient('shopp_dashboard_orders') ) ) {
     
    277278            echo '<tbody id="orders" class="list orders">';
    278279            $even = false;
    279             foreach ($Orders as $Order) {
    280                 echo '<tr'.((!$even)?' class="alternate"':'').'>';
    281                 $even = !$even;
     280            foreach ( $Orders as $Order ) {
     281                $classes = array();
     282                if ( $even = !$even ) $classes[] = 'alternate';
     283                $txnstatus = isset($txnstatus_labels[ $Order->txnstatus ]) ? $txnstatus_labels[$Order->txnstatus] : $Order->txnstatus;
     284                $classes[] = strtolower(preg_replace('/[^\w]/', '_', $Order->txnstatus));
     285
     286                echo '<tr class="' . join(' ',$classes) . '">';
    282287                echo '<td><a class="row-title" href="'.add_query_arg(array('page'=>ShoppAdmin()->pagename('orders'),'id'=>$Order->id),admin_url('admin.php')).'" title="View &quot;Order '.$Order->id.'&quot;">'.((empty($Order->firstname) && empty($Order->lastname))?'(no contact name)':$Order->firstname.' '.$Order->lastname).'</a></td>';
    283288                echo '<td>'.date("Y/m/d",mktimestamp($Order->created)).'</td>';
    284                 echo '<td class="num">'.$Order->items.'</td>';
    285                 echo '<td class="num">'.money($Order->total).'</td>';
    286                 echo '<td class="num">'.$statusLabels[ $Order->status ].'</td>';
     289                echo '<td class="num items">'.$Order->items.'</td>';
     290                echo '<td class="num total">'.money($Order->total).'</td>';
     291                echo '<td class="num status">'.$statusLabels[ $Order->status ].'</td>';
    287292                echo '</tr>';
    288293            }
  • shopp/trunk/core/flow/Discounter.php

    r821385 r865739  
    179179        if ( 'new' !== $_POST['id'] ) {
    180180            $Promotion = new ShoppPromo($_POST['id']);
     181            $wascatalog = ( 'Catalog' == $Promotion->target );
    181182        } else $Promotion = new ShoppPromo();
    182183
     
    195196        do_action_ref_array('shopp_promo_saved', array(&$Promotion));
    196197
    197         // $Promotion->reset_discounts();
    198         if ( 'Catalog' == $Promotion->target )
     198        // Apply catalog promotion discounts to catalog product price lines
     199        if ( 'Catalog' == $Promotion->target ) {
    199200            $Promotion->catalog();
     201        } elseif ( $wascatalog ) {
     202            // Unapply catalog discounts for discounts that no longer target catalog products
     203            $priceids = ShoppPromo::discounted_prices(array($Promotion->id));
     204            $Promotion->uncatalog($priceids);
     205        }
    200206
    201207        // Set confirmation notice
  • shopp/trunk/core/flow/Install.php

    r821385 r865739  
    55 * Flow controller for installation and upgrades
    66 *
    7  * @author Jonathan Davis
    87 * @version 1.0
    9  * @copyright Ingenesis Limited, January  6, 2010
     8 * @copyright Ingenesis Limited, January 2010-2014
    109 * @package shopp
    11  * @subpackage shopp
    1210 **/
    1311
    1412defined( 'WPINC' ) || header( 'HTTP/1.1 403' ) & exit; // Prevent direct access
    1513
    16 /**
    17  * ShoppInstallation
    18  *
    19  * @package shopp
    20  * @author Jonathan Davis
    21  **/
    2214class ShoppInstallation extends ShoppFlowController {
    2315
     
    2921     *
    3022     * @return void
    31      * @author Jonathan Davis
    3223     **/
    3324    function __construct () {
    34         add_action('shopp_activate',array($this,'activate'));
    35         add_action('shopp_deactivate',array($this,'deactivate'));
    36         add_action('shopp_reinstall',array($this,'install'));
    37 
    38         add_action('shopp_setup',array($this,'setup'));
    39         add_action('shopp_setup',array($this,'images'));
    40         add_action('shopp_setup',array($this,'roles'));
    41         add_action('shopp_setup',array($this,'maintenance'));
     25
     26        add_action('shopp_activate',   array($this, 'activate'));
     27        add_action('shopp_deactivate', array($this, 'deactivate'));
     28        add_action('shopp_reinstall',  array($this, 'install'));
     29
     30        add_action('shopp_setup', array($this, 'setup'));
     31        add_action('shopp_setup', array($this, 'images'));
     32        add_action('shopp_setup', array($this, 'roles'));
     33        add_action('shopp_setup', array($this, 'maintenance'));
    4234
    4335        $this->errors = array(
     
    8981
    9082        shopp_set_setting('updates', false);
    91         shopp_set_setting('rebuild_rewrites', 'on');
    92 
     83
     84        add_action('init', 'flush_rewrite_rules', 99);
    9385        return true;
    9486    }
     
    166158        shopp_set_setting('maintenance', 'on');
    167159
    168         // Process any database schema changes
    169         $this->upschema();
    170 
    171         // @todo Remove before release
    172         if ( in_array($installed, array(1300,1301)) ) $installed = 1148;
    173 
    174160        if ( $installed < 1100 ) $this->upgrade_110();
    175161        if ( $installed < 1200 ) $this->upgrade_120();
    176162        if ( $installed < 1300 ) $this->upgrade_130();
     163
     164        $db = sDB::object();
     165        file_put_contents(SHOPP_PATH . '/shopp_queries.txt', json_encode($db->queries));
    177166
    178167        ShoppSettings()->save('db_version', ShoppVersion::db());
     
    195184     * @return void
    196185     **/
    197     public function upschema () {
     186    public function upschema ( $filename = 'schema.sql' ) {
     187
     188        $path = SHOPP_PATH . '/core/schema';
     189        $schema = "$path/$filename";
     190
     191        // Check for the schema definition file
     192        if ( ! file_exists($schema) ) $this->error('nodbschema-upgrade');
     193
    198194        // Test to ensure Shopp can create/drop tables
    199195        $testtable = 'shopp_db_permissions_test_'.time();
     
    210206            require(ABSPATH.'wp-admin/includes/upgrade.php');
    211207
    212         // Check for the schema definition file
    213         if (!file_exists(SHOPP_DBSCHEMA)) $this->error('nodbschema-upgrade');
    214208
    215209        ob_start();
    216         include(SHOPP_DBSCHEMA);
    217         $schema = ob_get_contents();
    218         ob_end_clean();
     210        include($schema);
     211        $schema = ob_get_clean();
    219212
    220213        // Update the table schema
     
    248241     * shopp_products
    249242     * shopp_categories
     243     * shopp_export_orders
     244     * shopp_export_customers
     245     * shopp_delete_orders
     246     * shopp_delete_customers
     247     * shopp_void
     248     * shopp_refund
    250249     * shopp_orders                     shopp-csr
    251250     * shopp_customers
     251     * shopp_capture
    252252     * shopp_menu
    253253     *
     
    255255     * @since 1.1
    256256     *
     257     * @return void
    257258     **/
    258259    public function roles () {
     
    268269
    269270        $caps['shopp-csr'] = array(
     271                'shopp_capture',
    270272                'shopp_customers',
    271273                'shopp_orders',
     
    283285                'shopp_delete_orders',
    284286                'shopp_delete_customers',
    285                 'shopp_capture',
    286287                'shopp_void',
    287288                'shopp_refund'
     
    299300
    300301        $caps = apply_filters('shopp_role_caps', $caps, $shopp_roles);
     302
    301303        foreach ( $shopp_roles as $role => $display ) {
    302304            if ( $wp_roles->is_role($role) ) {
     
    449451     **/
    450452    public function upgrade_110 () {
    451 
    452453        $meta_table = ShoppDatabaseObject::tablename('meta');
    453454        $setting_table = ShoppDatabaseObject::tablename('setting');
     455        $product_table = ShoppDatabaseObject::tablename('product');
     456
     457        // 1.1 schema changes
     458        $this->upschema('schema-110.sql');
    454459
    455460        // Update product status from the 'published' column
    456         $product_table = ShoppDatabaseObject::tablename('product');
    457461        sDB::query("UPDATE $product_table SET status=CAST(published AS unsigned)");
    458462
     
    622626
    623627    public function upgrade_120 () {
     628        // 1.2 schema changes
     629        $this->upschema('schema-120.sql');
    624630        global $wpdb;
    625631
     
    10261032
    10271033    public function upgrade_130 () {
     1034        // 1.3 schema changes
     1035        $this->upschema();
    10281036        global $wpdb;
    10291037
  • shopp/trunk/core/flow/Order.php

    r831831 r865739  
    6161        if ( ! defined('SHOPP_TXNLOCK_TIMEOUT')) define('SHOPP_TXNLOCK_TIMEOUT',10);
    6262
    63         add_action('parse_request', array($this, 'locate')); // location updates must come before other requests
     63        // Request handling
    6464        add_action('parse_request', array($this, 'request'));
    6565        add_action('parse_request', array($this->Discounts, 'requests'));
     66
     67
     68        // Address handling
     69        add_action('shopp_update_destination', array($this->Billing, 'fromshipping'));
     70        add_action('shopp_update_destination', array($this, 'taxlocale'));
     71
    6672
    6773        // Order processing
     
    102108        // but before gateway-order specific handlers are established
    103109        add_action('shopp_init', array($this, 'txnupdates'), 20);
     110
     111        // Initialize/reinitalize the current location
     112        add_action('shopp_init', array($this, 'locate'), 20);
     113
    104114
    105115    }
     
    179189    }
    180190
    181     /**It
     191    /**
    182192     * Update addresses throughout the system
    183193     *
     
    189199    public function locate () {
    190200
    191         add_action('shopp_update_destination', array($this->Billing, 'fromshipping'));
    192         add_action('shopp_update_destination', create_function('','
    193             $Order = ShoppOrder();
    194             $Order->Tax->address($Order->Billing, $Order->Shipping, $Order->Cart->shipped());
    195         '));
    196         add_filter('shopp_tax_country', array('ShoppTax', 'euvat'), 10, 3);
    197 
    198         if ( isset($_REQUEST['shipping']) ) {
    199             $request = $_REQUEST['shipping'];
    200 
    201             $this->Billing->locate($request);
    202             $this->Shipping->locate($request);
    203 
    204             do_action_ref_array( 'shopp_update_destination', array($request) );
    205 
    206         } else {
    207             $this->Billing->locate();
    208             $this->Shipping->locate();
    209         }
    210 
    211         $this->Tax->address($this->Billing, $this->Shipping, $this->Cart->shipped());
     201        $request = isset($_REQUEST['shipping']) ? $_REQUEST['shipping'] : false;
     202
     203        $this->Billing->locate($request);
     204        $this->Shipping->locate($request);
     205
     206        do_action_ref_array( 'shopp_update_destination', array($request) );
     207
     208    }
     209
     210    /**
     211     * Send the current taxable address to the Tax system
     212     *
     213     * @author Jonathan Davis
     214     * @since 1.3.2
     215     *
     216     * @return void
     217     **/
     218    public function taxlocale ( ) {
     219
     220        $Address = $this->Billing;
     221        if ( $this->Cart->shipped() || shopp_setting_enabled('tax_destination') ) // @todo add setting for "Apply tax to the shipping address"
     222            $Address = $this->Shipping;
     223
     224        // Locale is always tracked with the billing address even though it is may be a shipping locale
     225        $locale = empty($this->Billing->locale) ? null : $this->Billing->locale;
     226
     227        $this->Tax->location($Address->country, $Address->state, $locale); // Update the ShoppTax working location
     228
    212229    }
    213230
     
    236253     * @return void
    237254     **/
    238     public function invoice ($Purchase) {
     255    public function invoice ( ShoppPurchase $Purchase ) {
    239256        shopp_add_order_event($Purchase->id, 'invoiced', array(
    240257            'gateway' => $Purchase->gateway,            // Gateway handler name (module name from @subpackage)
     
    455472        // Catch Purchase record save errors
    456473        if ( empty($Purchase->id) ) {
    457             shopp_add_error( Shopp::__('The order could not be created because of a technical problem on the server. Please try again, or contact the website adminstrator.') );
     474            shopp_add_error( Shopp::__('The order could not be created because of a technical problem on the server. Please try again, or contact the website administrator.') );
    458475            return;
    459476        }
     
    566583        }
    567584
    568         $registration = $Purchase->registration();
    569         if ( empty($registration) ) {
    570             shopp_debug('No purchase registration data available for account registration processing.');
    571             return;
    572         }
    573 
    574         $this->Customer->copydata($registration['Customer']);
    575         $this->Billing->copydata($registration['Billing']);
    576         $this->Shipping->copydata($registration['Shipping']);
    577 
    578 
    579         add_filter('shopp_validate_registration', create_function('', 'return true;') ); // Validation already conducted during the checkout process
    580         add_filter('shopp_registration_redirect', create_function('', 'return false;') ); // Prevent redirection to account page after registration
     585        if ( ! $this->Customer->exists() ) {
     586
     587            $registration = $Purchase->registration();
     588            if ( empty($registration) ) {
     589                shopp_debug('No purchase registration data available for account registration processing.');
     590                return;
     591            }
     592
     593            $this->Customer->copydata($registration['Customer']);
     594            $this->Billing->copydata($registration['Billing']);
     595            $this->Shipping->copydata($registration['Shipping']);
     596
     597            add_filter('shopp_validate_registration', create_function('', 'return true;') ); // Validation already conducted during the checkout process
     598            add_filter('shopp_registration_redirect', create_function('', 'return false;') ); // Prevent redirection to account page after registration
     599
     600        }
    581601
    582602        ShoppRegistration::process();
  • shopp/trunk/core/flow/Pages.php

    r831831 r865739  
    296296    public function templates () {
    297297        $templates = parent::templates();
    298         if ( is_catalog_frontpage() )
     298        if ( $this->is_frontpage() )
    299299            array_unshift($templates, 'front-page.php');
    300300        return $templates;
     
    321321
    322322    public function styleclass ( $classes ) {
    323         if ( is_catalog_frontpage() )
     323        if ( $this->is_frontpage() )
    324324            $classes[] = 'home';
    325325        $classes[] = $this->name();
     
    333333        $stub = parent::poststub();
    334334        $wp_query->is_post_type_archive = false;
    335         // if ( is_catalog_frontpage() )
    336         // $wp_query->is_home = true;
     335        if ( $this->is_frontpage() )
     336            $wp_query->is_home = true;
    337337
    338338        return $stub;
    339339    }
    340340
     341    public function is_frontpage () {
     342        return self::frontid() == get_option('page_on_front');
     343    }
    341344}
    342345
     
    392395        if ( ! $request) $request = ShoppStorefront()->account['request'];
    393396        $templates = array( 'account-'.$request.'.php', 'account.php' );
    394 
    395397        if ( 'login' == $request || ! ShoppCustomer()->loggedin() ) $templates = array( 'login-' . $request . '.php', 'login.php' );
    396 
     398        $context = ShoppStorefront::intemplate(); // Set account page context
     399
     400        $Errors = ShoppErrorStorefrontNotices();
    397401        ob_start();
    398         if ( apply_filters( 'shopp_show_account_errors', true ) && ShoppErrors()->exist( SHOPP_AUTH_ERR ) )
     402        if ( apply_filters( 'shopp_show_account_errors', true ) && $Errors->exist() )
    399403            echo ShoppStorefront::errors( array( "errors-$context", 'account-errors.php', 'errors.php' ) );
    400404        Shopp::locate_template( $templates, true );
     
    404408        if ($widget) $content = '<!-- id="shopp" -->' . $content;
    405409
    406         return apply_filters( 'shopp_account_template', $content );
     410        return apply_filters( 'shopp_account_template', $content, $request );
    407411
    408412    }
  • shopp/trunk/core/flow/Registration.php

    r821385 r865739  
    3636        'shipping' => array(),
    3737        'info' => array(),
     38        'marketing' => '',
    3839        'loginname' => '',
    3940        'password' => '',
     
    8889            'phone' => $this->form('phone'),
    8990            'info' => $this->form('info'),
    90             'password' => $this->form('password', true),
     91            'marketing' => $this->form('marketing'),
     92            'password' => $this->form('password', true),
    9193            'loginname' => $this->form('loginname', true)
    9294        );
     
    108110
    109111    public function shipaddress () {
     112
    110113        $ShippingAddress = ShoppOrder()->Shipping;
    111114        $BillingAddress = ShoppOrder()->Billing;
     
    138141        // Prevent overwriting the card data when updating the BillingAddress
    139142        $ignore = array();
    140         if ( ! empty($form['card']) && preg_replace('/[^\d]/','',$form['card']) == substr($BillingAddress->card,-4) )
     143        if ( ! empty($form['card']) && preg_replace('/[^\d]/', '', $form['card']) == substr($BillingAddress->card, -4) )
    141144            $ignore[] = 'card';
    142145
    143         $BillingAddress->updates($form,$ignore);
     146        $BillingAddress->updates($form, $ignore);
    144147
    145148        // Handle same address copying
  • shopp/trunk/core/flow/Report.php

    r831831 r865739  
    2727
    2828    private $view = 'dashboard';
     29    protected $ui = 'reports';
    2930
    3031    private $defaults = array();    // Default request options
     
    4546        shopp_enqueue_script('reports');
    4647
    47         add_filter('shopp_reports',array($this,'xreports'));
    48         add_action('load-'.$this->screen,array($this,'loader'));
     48        add_filter('shopp_reports', array(__CLASS__, 'xreports'));
     49        add_action('load-'.$this->screen, array($this, 'loader'));
    4950    }
    5051
     
    144145    static function load () {
    145146        $options = self::request();
    146         extract($options,EXTR_SKIP);
     147        extract($options, EXTR_SKIP);
    147148
    148149        $reports = self::reports();
     
    151152        $report = isset($_GET['report']) ? $_GET['report'] : 'sales';
    152153
    153         $ReportClass = $reports[$report]['class'];
     154        if ( empty($reports[ $report ]['class']) )
     155            return wp_die(Shopp::__('The requested report does not exist.'));
     156
     157        $ReportClass = $reports[ $report ]['class'];
    154158        $Report = new $ReportClass($options);
    155159        $Report->load();
     
    188192
    189193        $Report = $this->Report;
     194        $Report->pagination();
    190195        $ListTable = ShoppUI::table_set_pagination ($this->screen, $Report->total, $Report->pages, $per_page );
    191196
     
    223228
    224229        $report_title = isset($reports[ $report ])? $reports[ $report ]['name'] : __('Report','Shopp');
    225         include(SHOPP_ADMIN_PATH."/reports/reports.php");
     230
     231        include $this->ui('reports.php');
     232
    226233    }
    227234
     
    708715
    709716    ?>
     717
     718
    710719            <table class="widefat" cellspacing="0">
    711720                <thead>
  • shopp/trunk/core/flow/Resources.php

    r821385 r865739  
    9595        if ( ! current_user_can('shopp_financials') || ! current_user_can('shopp_export_orders') ) exit();
    9696
     97        add_filter('shopp_reports', array('ShoppAdminReport', 'xreports'));
    9798        $reports = ShoppAdminReport::reports();
    9899        $Report = ShoppAdminReport::load();
     
    233234        }
    234235
    235         if ( apply_filters('shopp_download_forbidden', $forbidden) ) {
     236        if ( apply_filters('shopp_download_forbidden', $forbidden, $Purchased) ) {
    236237            Shopp::redirect( add_query_arg('downloads', '', Shopp::url(false, 'account') ), true, 303 );
    237238        }
  • shopp/trunk/core/flow/Setup.php

    r821385 r865739  
    9797        if ( ! empty($_POST['setup']) ) {
    9898            $_POST['settings']['display_welcome'] = 'off';
    99             $this->settings_save();
     99            shopp_set_formsettings();
    100100        }
    101101
     
    135135                asort($_POST['settings']['target_markets']);
    136136
    137             $this->settings_save();
     137            shopp_set_formsettings();
    138138            $updated = __('Shopp settings saved.', 'Shopp');
    139139        }
     
    179179            }
    180180
    181             $this->settings_save();
     181            shopp_set_formsettings();
    182182        }
    183183
     
    245245
    246246
    247             $this->settings_save();
     247            shopp_set_formsettings();
    248248            $updated = __('Shopp checkout settings saved.','Shopp');
    249249        }
     
    338338            $defaults = ShoppPages()->settings();
    339339            $_POST['settings']['storefront_pages'] = array_merge($defaults,$_POST['settings']['storefront_pages']);
    340             $this->settings_save();
     340            shopp_set_formsettings();
    341341
    342342            // Re-register page, collection, taxonomies and product rewrites
     
    454454    }
    455455
    456     public function settings_save () {
    457         if (empty($_POST['settings']) || !is_array($_POST['settings'])) return false;
    458         foreach ($_POST['settings'] as $setting => $value)
    459             shopp_set_setting($setting,stripslashes_deep($value));
    460     }
    461 
    462456}
  • shopp/trunk/core/flow/Storefront.php

    r831831 r865739  
    10601060        if ( in_array($view, $views) ) $classes[] = $view;
    10611061
    1062         if ( 'grid' == $view ) {
    1063             $boxes = shopp_setting('row_products');
    1064             if ( empty($boxes) ) $boxes = 3;
    1065             $classes[] = 'shopp_grid-' . abs($boxes);
    1066         }
     1062        $boxes = shopp_setting('row_products');
     1063        if ( empty($boxes) ) $boxes = 3;
     1064        $classes[] = 'shopp_grid-' . abs($boxes);
    10671065
    10681066        // Add collection slug
  • shopp/trunk/core/flow/System.php

    r821385 r865739  
    152152            }
    153153
    154             $this->settings_save();
     154            shopp_set_formsettings();
    155155            $updated = __('Shipping settings saved.','Shopp');
    156156        }
     
    242242
    243243            if ($id == $module) {
    244                 if (isset($_POST['settings'])) $this->settings_save();
     244                if (isset($_POST['settings'])) shopp_set_formsettings();
    245245                /** Save shipping service settings **/
    246246                $active[$module] = true;
     
    440440        if (!empty($_POST['save'])) {
    441441            check_admin_referer('shopp-settings-taxes');
    442             $this->settings_save();
     442            shopp_set_formsettings();
    443443            $updated = __('Tax settings saved.','Shopp');
    444444        }
     
    686686                    $gateway = str_replace($matched[0], '', $gateway);
    687687
     688                    // Merge the existing gateway settings with the newly updated settings
    688689                    if ( isset($Gateways->active[ $gateway ]) ) {
    689690                        $Gateway = $Gateways->active[ $gateway ];
    690                         $_POST['settings'][$gateway] = array_merge($Gateway->settings,$_POST['settings'][ $gateway ]);
     691                        // Cannot use array_merge() because it adds numeric index values instead of overwriting them
     692                        $_POST['settings'][ $gateway ] = (array) $_POST['settings'][ $gateway ] + (array) $Gateway->settings;
    691693                    }
     694
    692695                }
    693696
     
    700703            } // END isset($_POST['gateway])
    701704
    702             $this->settings_save();
     705            shopp_set_formsettings();
    703706            $updated = __('Shopp payments settings saved.','Shopp');
    704707        }
     
    880883    }
    881884
    882 
    883     public function settings_save () {
    884         if (empty($_POST['settings']) || !is_array($_POST['settings'])) return false;
    885         foreach ($_POST['settings'] as $setting => $value)
    886             shopp_set_setting($setting,stripslashes_deep($value));
    887     }
    888 
    889885    public static function reindex () {
    890886
  • shopp/trunk/core/flow/Warehouse.php

    r821385 r865739  
    2121    public $products = array();
    2222    public $subs = array();
     23
     24    protected $ui = 'products';
    2325
    2426    /**
     
    383385        );
    384386
    385         if ( isset($ordercols) ) {
     387        if ( ! empty($ordercols) ) {
    386388            unset($loading['order']);
    387389            $loading['orderby'] = $ordercols;
     
    641643            wp_die(__('You do not have sufficient permissions to access this page.'));
    642644
    643         if (empty($Shopp->Product)) {
     645        if ( empty($Shopp->Product) ) {
    644646            $Product = new ShoppProduct();
    645647            $Product->status = "publish";
    646648        } else $Product = $Shopp->Product;
    647649
    648         $Product->slug = apply_filters('editable_slug',$Product->slug);
     650        $Product->slug = apply_filters('editable_slug', $Product->slug);
    649651        $permalink = trailingslashit(Shopp::url());
    650652
     
    655657
    656658        $workflows = array(
    657             "continue" => __('Continue Editing','Shopp'),
    658             "close" => __('Products Manager','Shopp'),
    659             "new" => __('New Product','Shopp'),
    660             "next" => __('Edit Next','Shopp'),
    661             "previous" => __('Edit Previous','Shopp')
     659            'continue' => Shopp::__('Continue Editing'),
     660            'close' =>    Shopp::__('Products Manager'),
     661            'new' =>      Shopp::__('New Product'),
     662            'next' =>     Shopp::__('Edit Next'),
     663            'previous' => Shopp::__('Edit Previous')
    662664        );
    663665
    664666        $taglist = array();
    665         foreach ($Product->tags as $tag) $taglist[] = $tag->name;
    666 
    667         if ($Product->id && !empty($Product->images)) {
    668             $ids = join(',',array_keys($Product->images));
     667        foreach ( $Product->tags as $tag )
     668            $taglist[] = $tag->name;
     669
     670        if ( $Product->id && ! empty($Product->images) ) {
     671            $ids = join(',', array_keys($Product->images));
    669672            $CoverImage = reset($Product->images);
    670673            $image_table = $CoverImage->_table;
     
    673676
    674677        $shiprates = shopp_setting('shipping_rates');
    675         if (!empty($shiprates)) ksort($shiprates);
     678        if ( ! empty($shiprates) )
     679            ksort($shiprates);
    676680
    677681        $uploader = shopp_setting('uploader_pref');
    678         if (!$uploader) $uploader = 'flash';
    679 
    680         $process = (!empty($Product->id)?$Product->id:'new');
    681         $_POST['action'] = add_query_arg(array_merge($_GET,array('page'=>$this->Admin->pagename('products'))),admin_url('admin.php'));
     682        if ( ! $uploader ) $uploader = 'flash';
     683
     684        $process = empty($Product->id) ? 'new' : $Product->id;
     685        $_POST['action'] = add_query_arg(array_merge($_GET, array('page' => $this->Admin->pagename('products'))), admin_url('admin.php'));
    682686        $post_type = ShoppProduct::posttype();
    683687
    684         // For inclusive taxes, add tax to base product price (so tax is part of the price)
    685         // This has to take place outside of ShoppProduct::pricing() so that the summary system
    686         // doesn't cache the results causing strange/unexpected price jumps
    687         // if ( shopp_setting_enabled('tax_inclusive') && ! Shopp::str_true($Product->excludetax) ) {
    688         //  foreach ($Product->prices as &$price) {
    689         //      if ( ! Shopp::str_true($price->tax) ) continue;
    690         //
    691         //      $taxes = array();
    692         //      $Tax = new ShoppTax();
    693         //      $Tax->address(ShoppOrder()->Billing);
    694         //      $Tax->rates($taxes, $Tax->item($Product));
    695         //
    696         //      $price->price += ShoppTax::calculate($taxes, $price->price);
    697         //      $price->saleprice += ShoppTax::calculate($taxes, $price->saleprice);
    698         //  }
    699         // }
    700 
    701688        do_action('add_meta_boxes', ShoppProduct::$posttype, $Product);
    702         do_action('add_meta_boxes_'.ShoppProduct::$posttype, $Product);
     689        do_action('add_meta_boxes_' . ShoppProduct::$posttype, $Product);
    703690
    704691        do_action('do_meta_boxes', ShoppProduct::$posttype, 'normal', $Product);
     
    706693        do_action('do_meta_boxes', ShoppProduct::$posttype, 'side', $Product);
    707694
    708         include(SHOPP_ADMIN_PATH."/products/editor.php");
     695        include $this->ui('editor.php');
    709696    }
    710697
  • shopp/trunk/core/library/Core.php

    r821385 r865739  
    10791079                'title','value');
    10801080        }
     1081        $allowed = apply_filters( 'shopp_allowed_inputattrs', $allowed, $options );
    10811082        $string = "";
    10821083        $classes = "";
     
    22502251     * @return boolean True if Suhosin is dectected and has configuration issues
    22512252     **/
    2252     function suhosin_warning () {
     2253    public static function suhosin_warning () {
    22532254
    22542255        return ( // Is Suhosin loaded or available?
     
    22672268    }
    22682269
     2270    /**
     2271     * Trim whitespace from the beggingin
     2272     *
     2273     * @author Jonathan Davis
     2274     * @since 1.3
     2275     *
     2276     * @return void Description...
     2277     **/
     2278    public function trim_deep ( $value ) {
     2279
     2280        if ( is_object($value) ) {
     2281            $vars = get_object_vars( $value );
     2282            foreach ( $vars as $key => $data )
     2283                $value->{$key} = self::trim_deep( $data );
     2284        } elseif ( is_array($value) ) {
     2285            $value = array_map(array(__CLASS__, 'trim_deep'), $value);
     2286        } elseif ( is_string( $value ) ) {
     2287            $value = trim($value);
     2288        }
     2289
     2290        return $value;
     2291
     2292    }
     2293
    22692294} // End abstract class ShoppCore
    22702295
  • shopp/trunk/core/library/DB.php

    r831831 r865739  
    655655            return sDB::object();
    656656        }
     657       
     658        public static function instance () {
     659            return sDB::object();
     660        }
    657661
    658662    }
  • shopp/trunk/core/library/Email.php

    r821385 r865739  
    8383
    8484    static function FixSymbols ( $message ) {
    85         $entities = htmlentities( $message, ENT_NOQUOTES  | ENT_DISALLOWED, 'UTF-8', false ); // Translate HTML entities (special symbols)
     85        if ( ! defined( 'ENT_DISALLOWED' ) ) define( 'ENT_DISALLOWED', 128 ); // ENT_DISALLOWED added in PHP 5.4
     86        $entities = htmlentities( $message, ENT_NOQUOTES | ENT_DISALLOWED, 'UTF-8', false ); // Translate HTML entities (special symbols)
    8687        return htmlspecialchars_decode( $entities, ENT_NOQUOTES ); // Translate HTML tags back
    8788    }
  • shopp/trunk/core/library/Loader.php

    r831831 r865739  
    502502    'shippingreport' => '/ui/reports/shipping.php',
    503503    'shippingsettingsui' => '/model/Shipping.php',
     504    'shopp' => '/library/Shopp.php',
    504505    'shoppaccountdashboardpage' => '/flow/Pages.php',
    505506    'shoppaccountpage' => '/flow/Pages.php',
  • shopp/trunk/core/library/Lookup.php

    r821385 r865739  
    777777    public static function payment_status_labels () {
    778778        $_ = array(
    779             'PENDING' => __('Pending','Shopp'),
    780             'CHARGED' => __('Charged','Shopp'),
    781             'REFUNDED' => __('Refunded','Shopp'),
    782             'VOID' => __('Void','Shopp')
    783         );
    784         return apply_filters('shopp_payment_status_labels',$_);
     779            'PENDING'  => Shopp::__('Pending'),
     780            'CHARGED'  => Shopp::__('Charged'),
     781            'REFUNDED' => Shopp::__('Refunded'),
     782            'VOID'     => Shopp::__('Void')
     783        );
     784        return apply_filters('shopp_payment_status_labels', $_);
    785785    }
    786786
    787787    public static function txnstatus_labels () {
    788788        $_ = array(
    789             'review' => __('Review','Shopp'),
    790             'purchase' => __('Purchase Order','Shopp'),
    791             'invoiced' => __('Invoiced','Shopp'),
    792             'authed' => __('Authorized','Shopp'),
    793             'captured' => __('Paid','Shopp'),
    794             'shipped' => __('Shipped','Shopp'),
    795             'refunded' => __('Refunded','Shopp'),
    796             'voided' => __('Void','Shopp'),
    797             'closed' => __('Closed','Shopp')
    798         );
    799         return apply_filters('shopp_txnstatus_labels',$_);
     789            'review'      => Shopp::__('Review'),
     790            'purchase'    => Shopp::__('Purchase Order'),
     791            'invoiced'    => Shopp::__('Invoiced'),
     792            'authed'      => Shopp::__('Authorized'),
     793            'auth-failed' => Shopp::__('Declined'),
     794            'captured'    => Shopp::__('Paid'),
     795            'shipped'     => Shopp::__('Shipped'),
     796            'refunded'    => Shopp::__('Refunded'),
     797            'voided'      => Shopp::__('Void'),
     798            'closed'      => Shopp::__('Closed')
     799        );
     800        return apply_filters('shopp_txnstatus_labels', $_);
    800801    }
    801802
  • shopp/trunk/core/library/Support.php

    r831831 r865739  
    2727
    2828    /**
    29      * Checks for available updates
    30      *
    31      * @author Jonathan Davis
    32      * @since 1.1
    33      *
    34      * @return array List of available updates
    35      **/
    36     public static function updates () {
    37 
    38         if ( ! wp_next_scheduled('shopp_check_updates') )
    39             wp_schedule_event(time(), 'twicedaily', 'shopp_check_updates');
    40 
    41         global $pagenow;
    42         if ( is_admin()
    43             && 'plugins.php' == $pagenow
    44             && isset($_GET['action'])
    45             && 'deactivate' == $_GET['action']) return array();
    46 
    47         $updates = new StdClass();
    48         if (function_exists('get_site_transient')) $plugin_updates = get_site_transient('update_plugins');
    49         else $plugin_updates = get_transient('update_plugins');
    50 
    51         switch ( current_filter() ) {
    52             case 'load-update-core.php': $timeout = 60; break; // 1 minute
    53             case 'load-plugins.php': // 1 hour
    54             case 'load-update.php': $timeout = 3600; break;
    55             default: $timeout = 43200; // 12 hours
    56         }
    57 
    58         $justchecked = isset( $plugin_updates->last_checked_shopp ) && $timeout > ( time() - $plugin_updates->last_checked_shopp );
    59         $changed = isset($plugin_updates->response[ SHOPP_PLUGINFILE ]);
    60         if ( $justchecked && ! $changed ) return;
    61 
    62         $Shopp = Shopp::object();
    63         $addons = array_merge(
    64             $Shopp->Gateways->checksums(),
    65             $Shopp->Shipping->checksums(),
    66             $Shopp->Storage->checksums()
    67         );
    68 
    69         $request = array('ShoppServerRequest' => 'update-check');
    70         /**
    71          * Update checks collect environment details for faster support service only,
    72          * none of it is linked to personally identifiable information.
    73          **/
    74         $data = array(
    75             'core' => ShoppVersion::release(),
    76             'addons' => join("-", $addons),
    77             'site' => get_bloginfo('url'),
    78         );
    79 
    80         if ( shopp_setting_enabled('support_data') ) {
    81             $optional = array(
    82                 'wp' => get_bloginfo('version').(is_multisite()?' (multisite)':''),
    83                 'mysql' => mysql_get_server_info(),
    84                 'php' => phpversion(),
    85                 'uploadmax' => ini_get('upload_max_filesize'),
    86                 'postmax' => ini_get('post_max_size'),
    87                 'memlimit' => ini_get('memory_limit'),
    88                 'server' => $_SERVER['SERVER_SOFTWARE'],
    89                 'agent' => $_SERVER['HTTP_USER_AGENT']
    90             );
    91             $data = array_merge($data, $optional);
    92         }
    93 
    94         $response = ShoppSupport::callhome($request, $data);
    95 
    96         if ($response == '-1') return; // Bad response, bail
    97         $response = unserialize($response);
    98         unset($updates->response);
    99 
    100         if ( isset($response->key) && ! Shopp::str_true($response->key) )
    101             delete_transient('shopp_activation');
    102 
    103         if ( isset($response->addons) ) {
    104             $updates->response[ SHOPP_PLUGINFILE . '/addons' ] = $response->addons;
    105             unset($response->addons);
    106         }
    107 
    108         if ( isset($response->id) )
    109             $updates->response[ SHOPP_PLUGINFILE ] = $response;
    110 
    111         if (isset($updates->response)) {
    112             shopp_set_setting('updates', $updates);
    113 
    114             // Add Shopp to the WP plugin update notification count
    115             if ( isset($updates->response[ SHOPP_PLUGINFILE ]) )
    116                 $plugin_updates->response[ SHOPP_PLUGINFILE ] = $updates->response[ SHOPP_PLUGINFILE ];
    117 
    118         } else unset($plugin_updates->response[ SHOPP_PLUGINFILE ]); // No updates, remove Shopp from the plugin update count
    119 
    120         $plugin_updates->last_checked_shopp = time();
    121         if ( function_exists('set_site_transient') ) set_site_transient('update_plugins', $plugin_updates);
    122         else set_transient('update_plugins', $plugin_updates);
    123 
    124         return $updates;
    125     }
    126 
    127     /**
    12829     * Loads the change log for an available update
    12930     *
     
    16162        if ( is_network_admin() || ! is_multisite() ) {
    16263        $wp_list_table = _get_list_table('WP_Plugins_List_Table');
    163             echo '<tr class="plugin-update-tr"><td colspan="' . $wp_list_table->get_column_count() . '" class="plugin-update colspanchange"><div class="update-message">';
     64            echo '<tr class="plugin-update-tr active"><th colspan="' . $wp_list_table->get_column_count() . '" class="check-column plugin-update colspanchange"><div class="update-message">';
    16465            echo self::buykey();
    165             echo '<style type="text/css">#shopp th,#shopp td{border-bottom:0;}</style>';
     66            echo '<br class="clear" /><br /><style type="text/css">#shopp th,#shopp td{border-bottom:0;-webkit-box-shadow:none;box-shadow:none;}
     67                #shopp+.plugin-update-tr .update-message {box-sizing:border-box;} #shopp+.plugin-update-tr .button {float:left; margin:20px 20px 0 0;} #shopp+.plugin-update-tr big { display:block; } #shopp+.plugin-update-tr .update-message::before {content:"";}</style>';
    16668            echo '</div></td></tr>';
    16769        }
     
    238140
    239141            $wp_list_table = _get_list_table('WP_Plugins_List_Table');
    240             echo '<tr class="plugin-update-tr"><td colspan="' . $wp_list_table->get_column_count() . '" class="plugin-update colspanchange"><div class="update-message">';
     142            echo '<tr class="plugin-update-tr"><th colspan="' . $wp_list_table->get_column_count() . '" class="plugin-update colspanchange"><div class="update-message">';
    241143            Shopp::_emi(
    242144                'You&apos;re missing out on important updates! %1$s &nbsp; %2$s Buy a Shopp Support Key! %3$s', empty($updates) ? '' : join(' ', $updates), '<a href="' . ShoppSupport::STORE . '" class="button button-primary">', '</a>'
     
    262164        $url = add_query_arg('action', 'shopp_nonag', wp_nonce_url(admin_url('admin-ajax.php'), 'wp_ajax_shopp_nonag'));
    263165        $_ = array();
    264         $_[] = '<div id="shopp-activation-nag" class="notice wp-core-ui">';
     166        $_[] = '<div id="shopp-activation-nag" class="update-nag">';
    265167
    266168        $_[] = '<p class="dismiss shoppui-remove-sign alignright"></p>';
     
    276178
    277179    public static function buykey () {
    278         return Shopp::_mi('You&apos;re missing out on **expert support**, **early access** to Shopp updates, and **one-click add-on updates**! Don&apos;t have a Shopp Support Key? %sBuy a Shopp Support Key!%s', '<a href="' . ShoppSupport::STORE . '" class="button button-primary" target="_blank">', '</a>');
     180        return Shopp::_mi('%s<big>Upgrade for Support</big>%s<big>You&apos;re missing out on **expert support**, **early access** to Shopp updates, and **one-click add-on updates**!</big>Don&apos;t have a Shopp Support Key? Support the project and get expert support, buy one today!', '<a href="' . ShoppSupport::STORE . '?utm_source=wpadmin-' . $_REQUEST['page'] . '&utm_medium=Shopp&utm_campaign=Plugin" class="button button-primary" target="_blank">', '</a>');
    279181    }
    280182
  • shopp/trunk/core/library/Version.php

    r831831 r865739  
    2222
    2323    /** @type int PATCH The maintenance patch version number */
    24     const PATCH = 1;
     24    const PATCH = 2;
    2525
    2626    /** @type string PRERELEASE The prerelease designation (dev, beta, RC1) */
     
    2828
    2929    /** @type string CODENAME The release project code name */
    30     const CODENAME = 'Deimos';
     30    const CODENAME = 'Phobos';
    3131
    3232    /** @type int DB The database schema version */
  • shopp/trunk/core/model/Asset.php

    r831831 r865739  
    261261        $url = Shopp::url( '' != get_option('permalink_structure') ? trailingslashit($this->id) . $this->filename : $this->id, 'images' );
    262262
     263        // Get the current URI
     264        $uri = $this->uri;
     265
    263266        // Handle resize requests
    264267        $params = func_get_args();
     
    269272            // Build the path to the cached copy of the file (if it exists)
    270273            $size = $this->cachefile( $request );
    271             $this->uri = "cache_{$size}_{$this->filename}";
    272 
     274            $uri = "cache_{$size}_{$this->filename}"; // Override the URI for the request
    273275        }
    274276
    275277        // Ask the engine if we have a direct URL
    276         $direct_url = $this->engine()->direct($this->uri);
     278        $direct_url = $this->engine()->direct($uri);
    277279        if ( false !== $direct_url ) return $direct_url;
    278280
    279281        if ( empty($request) ) return $url;
    280282        else return Shopp::add_query_string( $request, $url);
    281 
    282283    }
    283284
     
    633634if ( !class_exists('ListFramework') ) return;
    634635
     636/**
     637 * ImageSetting
     638 *
     639 * Data model for handling image setting data
     640 *
     641 * @since 1.2
     642 * @package shopp
     643 **/
    635644class ImageSetting extends ShoppMetaObject {
    636645
    637     static $qualities = array(100,92,80,70,60);
    638     static $fittings = array('all','matte','crop','width','height');
     646    static $qualities = array(100, 92, 80, 70, 60);
     647    static $fittings = array('all', 'matte', 'crop', 'width', 'height');
    639648
    640649    public $width;
     
    646655    public $context = 'setting';
    647656    public $type = 'image_setting';
    648     public $_xcols = array('width','height','fit','quality','sharpen','bg');
    649 
    650     public function __construct ($id=false,$key='id') {
     657    public $_xcols = array('width', 'height', 'fit', 'quality', 'sharpen', 'bg');
     658
     659    public function __construct ( $id = false, $key = 'id' ) {
    651660        $this->init(self::$table);
    652661        $this->load($id,$key);
    653662    }
    654663
     664    /**
     665     * Provides a translated list of image "fit" setting labels
     666     *
     667     * @since 1.2
     668     *
     669     * @return array List of translated settings labels
     670     **/
    655671    public function fit_menu () {
    656         return array(   __('All','Shopp'),
    657             __('Fill','Shopp'),
    658             __('Crop','Shopp'),
    659             __('Width','Shopp'),
    660             __('Height','Shopp')
     672        return array(
     673            Shopp::__('All'),
     674            Shopp::__('Fill'),
     675            Shopp::__('Crop'),
     676            Shopp::__('Width'),
     677            Shopp::__('Height')
    661678        );
    662679    }
    663680
     681    /**
     682     * Provides a menu of readable image quality labels
     683     *
     684     * @since 1.3
     685     *
     686     * @return array List of quality labels
     687     **/
    664688    public function quality_menu () {
    665         return array(   __('Highest quality, largest file size','Shopp'),
    666             __('Higher quality, larger file size','Shopp'),
    667             __('Balanced quality &amp; file size','Shopp'),
    668             __('Lower quality, smaller file size','Shopp'),
    669             __('Lowest quality, smallest file size','Shopp')
     689        return array(
     690            Shopp::__('Highest quality, largest file size'),
     691            Shopp::__('Higher quality, larger file size'),
     692            Shopp::__('Balanced quality &amp; file size'),
     693            Shopp::__('Lower quality, smaller file size'),
     694            Shopp::__('Lowest quality, smallest file size')
    670695        );
    671696    }
    672697
    673     public function fit_value ($value) {
    674         if (isset(self::$fittings[$value])) return self::$fittings[$value];
     698    /**
     699     * Converts a numeric ImageSetting fit setting to a Theme API compatible option name
     700     *
     701     * @since 1.3
     702     *
     703     * @param integer $setting The numeric ImageSetting value
     704     * @return string The option name
     705     **/
     706    public function fit ( integer $setting ) {
     707        if ( isset(self::$fittings[ $setting ]) )
     708            return self::$fittings[ $setting ];
    675709        return self::$fittings[0];
    676710    }
    677711
    678     public function quality_value ($value) {
    679         if (isset(self::$qualities[$value])) return self::$qualities[$value];
     712    /**
     713     * Converts a numeric ImageSetting quality setting to a Theme API compatible option value
     714     *
     715     * @since 1.3
     716     *
     717     * @param integer $setting The numeric ImageSetting value
     718     * @return integer The option value
     719     **/
     720    public function quality ( integer $setting ) {
     721        if ( isset(self::$qualities[ $setting ])  )
     722            return self::$qualities[ $setting ];
    680723        return self::$qualities[2];
    681724    }
    682725
    683     public function options ($prefix='') {
     726    /**
     727     * Convert the ImageSetting values to a Theme API compatible array of image options
     728     *
     729     * @since 1.3
     730     *
     731     * @param string $prefix (optional) Prefix for the option keys
     732     * @return array The image options
     733     **/
     734    public function options ( $prefix = '' ) {
    684735        $settings = array();
    685         $properties = array('width','height','fit','quality','sharpen','bg');
    686         foreach ($properties as $property) {
     736        $properties = array('width', 'height', 'fit', 'quality', 'sharpen', 'bg');
     737        foreach ( $properties as $property ) {
    687738            $value = $this->{$property};
    688             if ('quality' == $property) $value = $this->quality_value($this->{$property});
    689             if ('fit' == $property) $value = $this->fit_value($this->{$property});
    690             $settings[$prefix.$property] = $value;
     739            if ( 'quality' == $property ) $value = $this->quality((int)$this->{$property});
     740            if ( 'fit' == $property ) $value = $this->fit((int)$this->{$property});
     741            $settings[ $prefix . $property ] = $value;
    691742        }
    692743        return $settings;
     
    695746} // END class ImageSetting
    696747
     748/**
     749 * Loads the collection of image settings
     750 *
     751 * @since 1.2
     752 * @package shopp
     753 **/
    697754class ImageSettings extends ListFramework {
    698755
    699     private static $instance;
    700 
    701     public function __construct () {
     756    private static $object;
     757
     758    private function __construct () {
    702759        $ImageSetting = new ImageSetting();
    703760        $table = $ImageSetting->_table;
     
    706763            "context='$ImageSetting->context'"
    707764        );
    708         $options = compact('table','where');
     765        $options = compact('table', 'where');
    709766        $query = sDB::select($options);
    710         $this->populate(sDB::query($query,'array',array($ImageSetting,'loader'),false,'name'));
     767        $this->populate(sDB::query($query, 'array', array($ImageSetting, 'loader'), false, 'name'));
    711768        $this->found = sDB::found();
    712769    }
     
    735792     * @return DB Returns a reference to the DB object
    736793     **/
    737     public static function &__instance () {
    738         if (!self::$instance instanceof self)
    739             self::$instance = new self;
    740         return self::$instance;
    741     }
    742 
     794    public static function &object () {
     795        if ( ! self::$object instanceof self )
     796            self::$object = new self;
     797        return self::$object;
     798    }
    743799
    744800} // END class ImageSettings
  • shopp/trunk/core/model/Cart.php

    r831831 r865739  
    114114        $commands = array('add', 'empty', 'update', 'remove');
    115115
     116        if ( isset($_REQUEST['empty']) )
     117            $_REQUEST['cart'] = 'empty';
     118
    116119        $request = isset($_REQUEST['cart']) ? strtolower($_REQUEST['cart']) : false;
    117120
     
    125128            'item' => false,
    126129            'items' => array(),
    127             'remove' => array()
     130            'remove' => array(),
    128131        );
    129132        $request = array_intersect_key($_REQUEST,$allowed); // Filter for allowed arguments
     
    287290     *
    288291     * @param int $quantity The quantity of the item to add to the cart
    289      * @param ShoppProduct $Product Product object to add to the cart
    290      * @param ShoppPrice $Price Price object to add to the cart
     292     * @param ShoppProduct|ShoppCartItem $Product Product object or cart item object to add to the cart
     293     * @param mixed $Price Price object to add to the cart
    291294     * @param int $category The id of the category navigated to find the product
    292295     * @param array $data Any custom item data to carry through
     
    294297     **/
    295298    public function additem ( $quantity = 1, &$Product, &$Price, $category = false, $data = array(), $addons = array() ) {
    296         $NewItem = new ShoppCartItem($Product, $Price, $category, $data, $addons);
    297 
    298         if ( ! $NewItem->valid() || ! $this->addable($NewItem) ) return false;
     299
     300        if ( 'ShoppCartItem' == get_class($Product) ) $NewItem = $Product;
     301        else {
     302            $NewItem = new ShoppCartItem($Product, $Price, $category, $data, $addons);
     303            if ( ! $NewItem->valid() || ! $this->addable($NewItem) ) return false;
     304        }
    299305
    300306        $id = $NewItem->fingerprint();
     
    625631        $Discounts->credits();
    626632
    627         if ( $Shipping->free() && $Totals->total('shipping') > 0 ) {
     633        if ( $Discounts->shipping() ) // If shipping discounts changed, recalculate shipping amount
    628634            $Totals->register( new OrderAmountShipping( array('id' => 'cart', 'amount' => $Shipping->calculate() ) ) );
    629         }
    630635
    631636        do_action_ref_array('shopp_cart_retotal', array(&$Totals) );
  • shopp/trunk/core/model/Collection.php

    r831831 r865739  
    7070            'adjacent' => false,    //
    7171            'product' => false,     //
    72             'load' => array(),      // Product data to load
     72            'load' => array('coverimages'),     // Product data to load
    7373            'inventory' => false,   // Flag for detecting inventory-based queries
    7474            'taxquery' => false,    // Cross taxonomy queries
     
    14671467    public $name = false;
    14681468    public $loading = array();
     1469    protected $_options = array();
    14691470
    14701471    public function __construct ( array $options = array() ) {
     1472
     1473        $this->taxonomy = self::$taxon;
     1474
     1475        $thisclass = get_class($this);
     1476        $slugs = SmartCollection::slugs($thisclass);
     1477
     1478        $this->slug = $this->uri = $slugs[0];
     1479
     1480        $this->name = call_user_func(array($thisclass, 'name'));
     1481        $this->_options = $options;
     1482    }
     1483
     1484    public static function name () {
     1485        return Shopp::__('Collection');
     1486    }
     1487
     1488    public static function slugs ( string $class ) {
     1489        return apply_filters( 'shopp_' . strtolower($class) . '_collection_slugs', get_class_property($class, 'slugs') );
     1490    }
     1491
     1492    public function load ( array $options = array() ) {
     1493        $options = array_merge( $this->_options, $options );
     1494        $this->smart($options);
    14711495
    14721496        if ( isset($options['show']) )
     
    14761500            $this->loading['pagination'] = $options['pagination'];
    14771501
    1478         $this->taxonomy = self::$taxon;
    1479 
    1480         $thisclass = get_class($this);
    1481         $slugs = SmartCollection::slugs($thisclass);
    1482 
    1483         $this->slug = $this->uri = $slugs[0];
    1484 
    1485         $this->name = call_user_func(array($thisclass, 'name'));
    1486     }
    1487 
    1488     public static function name () {
    1489         return Shopp::__('Collection');
    1490     }
    1491 
    1492     public static function slugs ( string $class ) {
    1493         return apply_filters( 'shopp_' . strtolower($class) . '_collection_slugs', get_class_property($class, 'slugs') );
    1494     }
    1495 
    1496     public function load ( array $options = array() ) {
    1497         $this->smart($options);
    14981502        parent::load($this->loading);
     1503    }
     1504
     1505    public static function defaultlinks ( $termlink, $term, $taxonomy ) {
     1506        if ( false !== strpos($termlink, "taxonomy=" . self::$taxon) )
     1507            return home_url("?" . self::$taxon . "=$term->slug");
     1508        return $termlink;
    14991509    }
    15001510
     
    17551765            'groupby' => 'p.ID',
    17561766            'orderby' => 'score DESC');
    1757         if ( ! empty($pricematch) ) $this->loading['having'] = array($pricematch);
     1767        if ( ! empty($pricematch) ) $this->loading[ empty( $search )? 'where':'having' ] = array($pricematch);
    17581768        if ( isset($options['show']) ) $this->loading['limit'] = $options['show'];
    17591769        if ( isset($options['published']) ) $this->loading['published'] = $options['published'];
  • shopp/trunk/core/model/Customer.php

    r821385 r865739  
    287287            extract((array)$Storefront->account);
    288288
    289         if ( isset($id) )
     289        if ( ! empty($id) )
    290290            $this->order($id);
    291291
     
    493493    public function load_downloads () {
    494494        // Bail out if the downloads property is already populated or we can't load customer order data
    495         if ( /*! empty($this->downloads) ||*/ ! ($purchases = shopp_customer_orders($this->id)) ) return; # Decomment before commit!
     495        if ( /*! empty($this->downloads) ||*/ ! ($purchases = shopp_customer_orders($this->id)) ) return; // Decomment before commit!
    496496        $this->downloads = array();
    497497
    498498        foreach ( $purchases as $Purchase ) {
     499            if ( ! in_array($Purchase->txnstatus, array('captured')) ) continue;
    499500            reset($Purchase->purchased);
    500501            $this->extract_downloads($Purchase->purchased);
     
    502503    }
    503504
    504     protected function extract_downloads ($items) {
     505    protected function extract_downloads ( $items ) {
     506
     507        $this->_filemap = array(); // Temporary property to hold the file mapping index
     508
    505509        while ( list($index, $Purchased) = each($items) ) {
    506510            // Check for downloadable addons
     
    514518            // Is it a downloadable item? Do not add the same dkey twice
    515519            if ( empty($Purchased->download) ) continue;
     520
     521            // Load download file data
    516522            $this->downloads[ $Purchased->dkey ] = $Purchased;
    517         }
     523            $this->_filemap[ $Purchased->download ] = $Purchased->dkey;
     524        }
     525
     526        $this->load_downloadfiles( array_keys($this->_filemap) );
     527
     528    }
     529
     530    /**
     531     * Loads the extra download file data for the loaded customer downloads
     532     *
     533     * @author Jonathan Davis
     534     * @since 1.3.2
     535     *
     536     * @param array $downloads a list of download meta record ids to load
     537     * @return void
     538     **/
     539    public function load_downloadfiles ( array $downloads = array() ) {
     540
     541        if ( empty($downloads) ) return false;
     542
     543        $meta_table = ShoppDatabaseObject::tablename(ShoppMetaObject::$table);
     544        sDB::query("SELECT * FROM $meta_table WHERE id IN (" . join(',', $downloads) . ")", 'array', array($this, 'download_loader'));
     545
     546        unset($this->_filemap);
     547    }
     548
     549    /**
     550     * Download record loader to map download file data to the loaded downloads for the customer
     551     *
     552     * @author Jonathan Davis
     553     * @since 1.3.2
     554     *
     555     * @param array $records The records to map to (unused becase they are mapped to the Customer->downloads records)
     556     * @param array $record The record data loaded from the query
     557     * @return void
     558     **/
     559    public function download_loader ( &$records, &$record ) {
     560
     561        // Lookup the download key
     562        if ( empty($this->_filemap[ $record->id ]) ) return;
     563        $dkey = $this->_filemap[ $record->id ];
     564
     565        // Find the purchased download
     566        if ( empty($this->downloads[ $dkey ]) ) return;
     567        $Purchased = &$this->downloads[ $dkey ];
     568
     569        // Unserialize the file data
     570        $data = maybe_unserialize($record->value);
     571        if ( is_object($data) ) $properties = get_object_vars($data);
     572        else return;
     573
     574        // Map the file data to the purchased download record
     575        foreach ( (array)$properties as $property => $value )
     576            $Purchased->$property = sDB::clean($value);
     577
    518578    }
    519579
     
    743803    }
    744804} // END class CustomerXLSExport
    745 
    746 /**
    747  * CustomerAccountPage class
    748  *
    749  * A property container for Shopp's customer account page meta
    750  *
    751  * @author Jonathan Davis
    752  * @since 1.1
    753  * @package customer
    754  **/
    755 class CustomerAccountPage {
    756 
    757     public $request = '';
    758     public $label = '';
    759     public $handler = false;
    760 
    761     public function __construct ( $request, $label, $handler ) {
    762         $this->request = $request;
    763         $this->label = $label;
    764         $this->handler = $handler;
    765     }
    766 
    767 } // END class CustomerAccountPage
  • shopp/trunk/core/model/Discounts.php

    r831831 r865739  
    2929    private $request = false;   // Current code request
    3030    private $credit = false;    // Credit request types
    31 
    32     /**
    33      * Get or set the current request
    34      *
    35      * @author Jonathan Davis
    36      * @since 1.3
    37      *
    38      * @param string $request The request string to set
    39      * @return string The current request
    40      **/
    41     public function request ( string $request = null ) {
    42 
    43         if ( isset($request) ) $this->request = $request;
    44         return $this->request;
    45 
    46     }
    47 
    48     public function credit ( string $request = null ) {
    49 
    50         if ( isset($request) ) $this->credit = $request;
    51         return $this->credit;
    52 
    53     }
    54 
    55     /**
    56      * Handle parsing and routing discount code related requests
    57      *
    58      * @author Jonathan Davis
    59      * @since 1.3
    60      *
    61      * @return void
    62      **/
    63     public function requests () {
    64 
    65         if ( isset($_REQUEST['discount']) && ! empty($_REQUEST['discount']) )
    66             $this->request( trim($_REQUEST['discount']) );
    67         elseif ( isset($_REQUEST['credit']) && ! empty($_REQUEST['credit']) )
    68             $this->credit( trim($_REQUEST['credit']) );
    69 
    70         if ( isset($_REQUEST['removecode']) && ! empty($_REQUEST['removecode']) )
    71             $this->undiscount(trim($_REQUEST['removecode']));
    72 
    73     }
     31    private $shipping = false;  // Track shipping discount changes
    7432
    7533    /**
     
    11674    }
    11775
    118     public function credits () {
    119         $credits = array();
    120 
    121         $CartTotals = ShoppOrder()->Cart->Totals;
    122 
    123         // Wipe out any existing credit calculations
    124         $CartTotals->takeoff('discount', 'credit');
    125         $discounts = $CartTotals->total('discount'); // Resum the applied discounts (without credits)
    126 
    127         foreach ( $this as $Discount ) {
    128             if ( $Discount->type() != ShoppOrderDiscount::CREDIT ) continue;
    129             $Discount->calculate(); // Recalculate based on current total to apply an appropriate amount
    130             $credits[] = $Discount->amount();
    131             $CartTotals->register( new OrderAmountDiscount( array('id' => 'credit', 'amount' => array_sum($credits) ) ) );
    132         }
    133 
    134         $amount = array_sum($credits);
    135 
    136         return (float) $amount;
    137     }
    138 
    13976    /**
    14077     * Match the promotions that apply to the order
     
    15491        // Match applied first
    15592        $Promotions->sort( array($this, 'sortapplied'), 'keys' );
     93
     94        // Reset shipping flag to track changes
     95        $this->shipping = false;
    15696
    15797        // Iterate over each promo to determine whether it applies
     
    239179
    240180        $this->add($Promo->id, $Discount);
     181        $this->shipping($Discount); // Flag shipping changes
    241182
    242183    }
     
    349290
    350291        $Discount = $this->get($id);
     292        $Discount->unapply();
    351293
    352294        $_REQUEST['cart'] = true;
    353295
    354         $this->remove($id);
     296        $this->remove($Discount->id()); // Remove it from the discount stack if it is there
     297        $this->shipping($Discount);     // Flag shipping changes
    355298
    356299        if ( isset($this->codes[ $Discount->code() ]) ) {
     
    371314     **/
    372315    private function reset ( ShoppOrderPromo $Promo ) {
    373 
    374         $this->remove($Promo->id);      // Remove it from the discount stack if it is there
    375 
     316        $this->undiscount((int)$Promo->id);
    376317    }
    377318
     
    398339
    399340        return false;
     341    }
     342
     343    /**
     344     * Get or set the current request
     345     *
     346     * @author Jonathan Davis
     347     * @since 1.3
     348     *
     349     * @param string $request The request string to set
     350     * @return string The current request
     351     **/
     352    public function request ( string $request = null ) {
     353
     354        if ( isset($request) ) $this->request = $request;
     355        return $this->request;
     356
     357    }
     358
     359    public function credit ( string $request = null ) {
     360
     361        if ( isset($request) ) $this->credit = $request;
     362        return $this->credit;
     363
     364    }
     365
     366    /**
     367     * Handle parsing and routing discount code related requests
     368     *
     369     * @author Jonathan Davis
     370     * @since 1.3
     371     *
     372     * @return void
     373     **/
     374    public function requests () {
     375
     376        if ( isset($_REQUEST['discount']) && ! empty($_REQUEST['discount']) )
     377            $this->request( trim($_REQUEST['discount']) );
     378        elseif ( isset($_REQUEST['credit']) && ! empty($_REQUEST['credit']) )
     379            $this->credit( trim($_REQUEST['credit']) );
     380
     381        if ( isset($_REQUEST['removecode']) && ! empty($_REQUEST['removecode']) )
     382            $this->undiscount(trim($_REQUEST['removecode']));
     383
     384    }
     385
     386    /**
     387     * Applies credits to the order discounts register
     388     *
     389     * @author Jonathan Davis
     390     * @since 1.3
     391     *
     392     * @return float The total amount credited
     393     **/
     394    public function credits () {
     395        $credits = array();
     396
     397        $CartTotals = ShoppOrder()->Cart->Totals;
     398
     399        // Wipe out any existing credit calculations
     400        $CartTotals->takeoff('discount', 'credit');
     401        $discounts = $CartTotals->total('discount'); // Resum the applied discounts (without credits)
     402
     403        foreach ( $this as $Discount ) {
     404            if ( $Discount->type() != ShoppOrderDiscount::CREDIT ) continue;
     405            $Discount->calculate(); // Recalculate based on current total to apply an appropriate amount
     406            $credits[] = $Discount->amount();
     407        }
     408
     409        $amount = array_sum($credits);
     410        if ( $amount > 0 )
     411            $CartTotals->register( new OrderAmountDiscount( array('id' => 'credit', 'amount' => $amount ) ) );
     412
     413        return (float) $amount;
     414    }
     415
     416
     417    /**
     418     * Report shipping discount changes
     419     *
     420     * @author Jonathan Davis
     421     * @since 1.3.2
     422     *
     423     * @param ShoppOrderDiscount $Discount (optional) The applied or unapplied discount to check for shipping changes
     424     * @return boolean True if shipping discounts changed, false otherwise
     425     **/
     426    public function shipping ( ShoppOrderDiscount $Discount = null ) {
     427
     428        if ( isset($Discount) && ShoppOrderDiscount::SHIP_FREE == $Discount->type() )
     429            $this->shipping = true;
     430
     431        return $this->shipping;
    400432    }
    401433
     
    568600
    569601            case 'discounts applied':   return ShoppOrder()->Discounts->count();
    570             case 'total quantity':      return $Cart->Totals->total('quantity');
    571             case 'shipping amount':     return $Cart->Totals->total('shipping');
    572             case 'subtotal amount':     return $Cart->Totals->total('order');
     602            case 'total quantity':      return $Cart->total('quantity');
     603            case 'shipping amount':     return $Cart->total('shipping');
     604            case 'subtotal amount':     return $Cart->total('order');
    573605            case 'customer type':       return ShoppOrder()->Customer->type;
    574606            case 'ship-to country':     return ShoppOrder()->Shipping->country;
     
    697729
    698730            // Numeric operations
    699             case 'is greater than':             return (Shopp::floatval($subject,false) > Shopp::floatval($value,false));
    700             case 'is greater than or equal to': return (Shopp::floatval($subject,false) >= Shopp::floatval($value,false));
    701             case 'is less than':                return (Shopp::floatval($subject,false) < Shopp::floatval($value,false));
    702             case 'Is less than or equal to':    return (Shopp::floatval($subject,false) <= Shopp::floatval($value,false));
     731            case 'is greater than':             return (Shopp::floatval($subject, false) >  Shopp::floatval($value, false));
     732            case 'is greater than or equal to': return (Shopp::floatval($subject, false) >= Shopp::floatval($value, false));
     733            case 'is less than':                return (Shopp::floatval($subject, false) <  Shopp::floatval($value, false));
     734            case 'is less than or equal to':    return (Shopp::floatval($subject, false) <= Shopp::floatval($value, false));
    703735        }
    704736
     
    860892     **/
    861893    public function applies () {
    862         $applies = ( $this->amount() > 0 || ( $this->amount() == 0 && $this->shipfree() ) );
     894        $applies = ( $this->amount() > 0 || ( $this->amount() == 0 && $this->shipfree() ) || count($this->items) > 0 );
    863895        return apply_filters('shopp_discount_applies', $applies, $this );
    864896    }
     
    10871119            case self::PERCENT_OFF: $amount = $Item->unitprice * ($this->discount() / 100); break;
    10881120            case self::AMOUNT_OFF:  $amount = $this->discount(); break;
    1089             case self::SHIP_FREE:   $Item->freeshipping = true; $amount = 0; break;
     1121            case self::SHIP_FREE:   $Item->freeshipping = true; $this->shipping = true; $amount = 0; break;
    10901122            case self::BOGOF:
    10911123                list($buy, $get) = $this->discount();
     
    11201152    public function hasitem ( string $key ) {
    11211153        return isset($this->items[ $key ]);
     1154    }
     1155
     1156    /**
     1157     * Unapply the discount
     1158     *
     1159     * This primarily involves resetting the Cart Item->freeshipping property.
     1160     *
     1161     * @author Jonathan Davis
     1162     * @since 1.3
     1163     *
     1164     * @return void Description...
     1165     **/
     1166    public function unapply () {
     1167        $Cart = ShoppOrder()->Cart;
     1168
     1169        if ( self::SHIP_FREE == $this->type ) {
     1170            $Shiprates = ShoppOrder()->Shiprates;
     1171            foreach ( $this->items as $id => $item ) {
     1172                $CartItem = $Cart->get($id);
     1173                $CartItem->freeshipping = false;
     1174            }
     1175        }
     1176
    11221177    }
    11231178
     
    11331188class ShoppPurchaseDiscount {
    11341189
    1135     public $id = false;                     // The originating promotion object id
    1136     public $name = '';                      // The name of the promotion
    1137     public $amount = 0.00;                  // The total amount of the discount
    1138     public $type = ShoppOrderDiscount::AMOUNT_OFF;      // The discount type
    1139     public $target = ShoppOrderDiscount::ORDER;         // The discount target
    1140     public $discount = false;               // The calculated discount amount (float or array for BOGOFs)
    1141     public $code = false;                   // The code associated with the discount
    1142     public $shipfree = false;               // A flag for free shipping
     1190    public $id = false;                             // The originating promotion object id
     1191    public $name = '';                              // The name of the promotion
     1192    public $amount = 0.00;                          // The total amount of the discount
     1193    public $type = ShoppOrderDiscount::AMOUNT_OFF;  // The discount type
     1194    public $target = ShoppOrderDiscount::ORDER;     // The discount target
     1195    public $discount = false;                       // The calculated discount amount (float or array for BOGOFs)
     1196    public $code = false;                           // The code associated with the discount
     1197    public $shipfree = false;                       // A flag for free shipping
    11431198
    11441199    public function __construct ( ShoppOrderDiscount $Discount ) {
  • shopp/trunk/core/model/Gateway.php

    r821385 r865739  
    345345    public function amount ( $amount, array $format = array() ) {
    346346
     347        $register = false;
     348
    347349        if ( is_string($amount) ) {
     350            $register = $amount;
    348351            $Cart = ShoppOrder()->Cart;
    349352            $amount = $Cart->total($amount);
     353            if ( false === $amount ) $register = false;
    350354        } elseif ( ! ( is_int($amount) || is_float($amount) ) ) return $amount;
    351355
     
    358362        extract($format);
    359363
     364        if ( ! empty($register) ) // Allow targeting specific amounts for filtering
     365            $amount = apply_filters("shopp_gateway_{$register}_amount", $amount);
     366
    360367        $amount = apply_filters('shopp_gateway_amount', abs($amount));
     368
    361369        return number_format($amount, $precision, $decimals, $thousands);
     370    }
     371
     372    /**
     373     * Zeros out tax amounts when tax inclusive is enabled
     374     *
     375     * Prevents inclusive-tax unaware payment systems (such as PayPal Standard)
     376     * from adding extra taxes to the order
     377     *
     378     * @author Jonathan Davis
     379     * @since 1.3.2
     380     *
     381     * @param float $amount The tax amount to filter
     382     * @return float The correct amount
     383     **/
     384    public function notaxinclusive ( $amount ) {
     385        if ( shopp_setting_enabled('tax_inclusive') ) return 0.0;
     386        else return $amount;
    362387    }
    363388
  • shopp/trunk/core/model/Item.php

    r831831 r865739  
    7979     * @return void
    8080     **/
    81     public function __construct ( $Product, $pricing, $category = false, $data = array(), $addons = array() ) {
     81    public function __construct ( ShoppProduct $Product = null, $pricing = null, $category = false, array $data = array(), array $addons = array() ) {
     82
    8283        $args = func_get_args();
    8384        if ( empty($args) ) return;
     
    8687        else $this->load($Product, $pricing, $category, $data, $addons);
    8788
    88         // $this->__wakeup();
    89         // $this->rediscount();
    90         // $this->totals();
    91 
    92     }
    93 
    94     // public function __wakeup () {
    95     //  add_action('shopp_cart_item_totals', array($this, 'rediscount'));
    96     //  add_action('shopp_cart_item_totals', array($this, 'totals'));
    97     // }
    98 
    99     /**
    100      * load
    101      *
    102      * loads/constructs the Item object from parameters
    103      *
    104      * @author John Dillick
     89    }
     90
     91    /**
     92     * Loads or constructs the Item object from product and product pricing parameters
     93     *
     94     * @author John Dillick, Jonathan Davis
    10595     * @since 1.2
    10696     *
     
    120110            if ( ! isset($Product->pricekey[ $optionkey ]) ) $optionkey = $Product->optionkey($pricing, true); // deprecated prime
    121111            if ( isset($Product->pricekey[ $optionkey ]) ) $Price = $Product->pricekey[ $optionkey ];
    122         } elseif ( $pricing ) {
     112        } elseif ( is_numeric($pricing) ) {
    123113            $Price = $Product->priceid[ $pricing ];
     114        } elseif ( is_a($pricing, 'ShoppPrice') ) {
     115            $Price = $pricing;
    124116        }
    125117
     
    254246    }
    255247
     248    /**
     249     * Loads or constructs item properties from a purchased product
     250     *
     251     * @author Jonathan Davis
     252     * @since 1.3
     253     *
     254     * @return void
     255     **/
    256256    public function load_purchased ( $Purchased ) {
    257257
     
    281281        // no product or no price specified
    282282        if ( ! $this->product || ! $this->priceline ) {
    283             new ShoppError(__('The product could not be added to the cart because it could not be found.','Shopp'),'cart_item_invalid',SHOPP_ERR);
     283            new ShoppError(__('The product could not be added to the cart because it could not be found.','Shopp'), 'cart_item_invalid', SHOPP_ERR);
    284284            return false;
    285285        }
     
    287287        // the item is not in stock
    288288        if ( ! $this->instock() ) {
    289             new ShoppError(__('The product could not be added to the cart because it is not in stock.','Shopp'),'cart_item_invalid',SHOPP_ERR);
     289            new ShoppError(__('The product could not be added to the cart because it is not in stock.','Shopp'), 'cart_item_invalid', SHOPP_ERR);
    290290            return false;
    291291        }
     
    303303    public function fingerprint () {
    304304        $_  = $this->product.$this->priceline;
     305        if ( empty($_) ) $_ = $this->name;
     306        if ( empty($_) ) $_ = mktime();
    305307        if ( ! empty($this->addons) )   $_ .= serialize($this->addons);
    306308        if ( ! empty($this->data) )     $_ .= serialize($this->data);
    307         return __CLASS__ . '_' . hash('crc32',$_);
     309        return __CLASS__ . '_' . hash('crc32', $_);
    308310    }
    309311
  • shopp/trunk/core/model/Meta.php

    r821385 r865739  
    3636     * @return void
    3737     **/
    38     function __construct ($id=false,$key='id') {
     38    function __construct ( $id = false, $key = 'id' ) {
    3939        $this->init(self::$table);
    40         if (!$id) return;
    41         if (is_array($id)) $this->load($id);
    42         else $this->load(array($key=>$id,'type'=>$this->type));
    43 
    44         if (!empty($this->id) && !empty($this->_xcols))
     40        if ( ! $id ) return;
     41
     42        if ( is_array($id) )
     43            $this->load($id);
     44        else $this->load(array($key => $id, 'type' => $this->type));
     45
     46        if ( ! empty($this->id) && ! empty($this->_xcols) )
    4547            $this->expopulate();
    4648    }
     
    5557     **/
    5658    function expopulate () {
    57         if (!is_object($this->value)) return;
     59        if ( ! is_object($this->value) ) return;
    5860        $properties = $this->value;
    5961        $this->copydata($properties);
  • shopp/trunk/core/model/Payments.php

    r821385 r865739  
    134134        $this->rewind();
    135135        $selected = $this->key();
     136
     137        if ( empty($selected) ) return false;
    136138
    137139        return $this->selected($selected);
  • shopp/trunk/core/model/Product.php

    r831831 r865739  
    1616
    1717class ShoppProduct extends WPShoppObject {
     18
    1819    static $table = 'posts';
    1920    static $_taxonomies = array(
     
    6465    );
    6566
     67    // Keep track of data load efforts
     68    protected $_loaded = array();
     69
    6670    /**
    6771     * Product constructor
     
    7276     * @return void
    7377     **/
    74     public function __construct ($id=false,$key='ID') {
    75         if (isset($this->_map[$key])) $key = $this->_map[$key];
    76         $this->init(self::$table,$key);
     78    public function __construct ( $id = false, $key = 'ID' ) {
     79        if ( isset($this->_map[ $key ]) ) $key = $this->_map[ $key ];
     80        $this->init(self::$table, $key);
    7781        $this->type = self::$posttype;
    78         $this->load($id,$key);
     82        $this->load($id, $key);
    7983    }
    8084
     
    111115    public static function labels () {
    112116        return apply_filters( 'shopp_product_labels', array(
    113             'name' => __('Products','Shopp'),
    114             'singular_name' => __('Product','Shopp'),
    115             'edit_item' => __('Edit Product','Shopp'),
    116             'new_item' => __('New Product','Shopp')
     117            'name' => __('Products', 'Shopp'),
     118            'singular_name' => __('Product', 'Shopp'),
     119            'edit_item' => __('Edit Product', 'Shopp'),
     120            'new_item' => __('New Product', 'Shopp')
    117121        ));
    118122    }
     
    141145     * @return void
    142146     **/
    143     public function load_data ($options=array('prices','specs','images','categories','tags','meta','summary'),&$products=array()) {
     147    public function load_data ( array $options = array('prices', 'specs', 'images', 'categories', 'tags', 'meta', 'summary'), array &$products = array() ) {
    144148        // Load summary before prices to ensure summary can be overridden by fresh pricing aggregation
    145149        $loaders = array(
    146         //  'name'      'callback_method'
    147             'summary'    => 'load_summary',
    148             'meta'       => 'load_meta',
    149             'prices'     => 'load_prices',
    150             'specs'      => 'load_meta',
    151             'images'     => 'load_meta',
    152             'coverimages'=> 'load_coverimages',
    153             'categories' => 'load_taxonomies',
    154             'tags'       => 'load_taxonomies'
     150        //  'name'           'callback_method'
     151            'summary'     => 'load_summary',
     152            'meta'        => 'load_meta',
     153            'prices'      => 'load_prices',
     154            'specs'       => 'load_meta',
     155            'images'      => 'load_meta',
     156            'coverimages' => 'load_coverimages',
     157            'categories'  => 'load_taxonomies',
     158            'tags'        => 'load_taxonomies'
    155159
    156160        );
    157 
    158         $options = array_map('strtolower',$options);
    159         $load = array_flip(array_intersect($options,array_keys($loaders)));
    160         $loadcalls = array_unique(array_values(array_intersect_key($loaders,$load)));
    161 
    162         if (!empty($products) ) {
    163             $ids = join(',',array_keys($products));
     161        // allow case-insensitive options
     162        $options = array_map('strtolower', $options);
     163
     164        // prevent loading data sets already requested and processed
     165        $options = array_diff($options, $this->_loaded);
     166
     167        // Only allow white-listed load operations
     168        $load = array_flip(array_intersect($options, array_keys($loaders)));
     169
     170        // Convert load requests to loading callbacks while preventing duplicate calls
     171        $loadcalls = array_unique(array_values(array_intersect_key($loaders, $load)));
     172
     173        if ( ! empty($products) ) { // Handle loading data across a collection of products
     174            $ids = join(',', array_keys($products));
    164175            $this->products = &$products;
    165         } else $ids = $this->id;
     176            foreach ( $products as $product )
     177                $product->_loaded = array_merge($product->_loaded, $options);
     178        } else { // Handle loading data for a single product
     179            $ids = $this->id;
     180            $this->_loaded = array_unique(array_merge($this->_loaded, $options));
     181        }
    166182
    167183        if ( empty($ids) ) return;
    168         foreach ($loadcalls as $loadmethod) {
     184
     185        foreach ( $loadcalls as $loadmethod )
    169186            if ( method_exists($this, $loadmethod) )
    170                 call_user_func_array(array($this,$loadmethod),array($ids));
    171         }
     187                call_user_func_array(array($this, $loadmethod), array($ids));
    172188
    173189    }
     
    202218
    203219        // Reset summary properties for correct price range and stock sums in single product (product page) loading contexts
    204         if ( ! empty($this->id) && $this->id == $ids && empty($this->checksum) ) {
     220        if (!empty($this->id) && $this->id == $ids) {
    205221            $this->load_summary($ids);
    206222            $this->resum();
     
    398414    public function metasetloader ( &$records, &$record, $id = 'id', $property = false, $collate = true, $merge = false ) {
    399415
    400         if (isset($this->products) && !empty($this->products)) $products = &$this->products;
     416        if ( isset($this->products) && ! empty($this->products) ) $products = &$this->products;
    401417        else $products = array();
    402418
     
    436452        }
    437453
    438         if ('images' == $property) {
     454        if ( 'images' == $property ) {
    439455            // Prevent double-loading images (can occur when images are specifically loaded, then all meta is generically loaded)
    440             if (isset($target->$property) && isset($target->{$property}[$record->id])) return;
     456            if ( isset($target->$property) && isset($target->{$property}[ $record->id ]) ) return;
    441457            $collate = 'id';
    442         }
    443 
    444         if ('specs' == $property) {
     458            // Prevent extra image queries since we already tried
     459        }
     460
     461        if ( 'specs' == $property ) {
    445462            $property = 'specnames';
    446463            parent::metaloader($records, $record, $products, $id, $property, $collate, $merge);
     
    577594        }
    578595
     596        $price->_sale = $price->sale; // Keep a copy of the price record "sale" setting {@see issue #2797}
    579597        if ($price->promoprice < $price->price) $target->sale = $price->sale = 'on';
    580598
     
    11101128            if ( ! isset($term->taxonomy) || empty($term->taxonomy) ) continue;     // Skip invalid entries
    11111129
    1112             if (!isset($terms[$term->taxonomy])) $terms[$term->taxonomy] = array();
    1113             $terms[$term->taxonomy][] = (int)$term->term_id;
     1130            if ( ! isset($terms[ $term->taxonomy ]) )
     1131                $terms[ $term->taxonomy ] = array();
     1132            $terms[ $term->taxonomy ][] = (int)$term->term_id;
    11141133        }
    11151134        foreach ($terms as $taxonomy => $termlist)
     
    11171136
    11181137        $metadata = array('specs','images','settings','meta');
    1119         foreach ($metadata as $metaset) {
     1138        foreach ( $metadata as $metaset ) {
    11201139            if ( ! is_array($this->$metaset) ) continue;
    1121             foreach ($this->$metaset as $metaobjects) {
     1140            foreach ( $this->$metaset as $metaobjects ) {
    11221141                if ( ! is_array($metaobjects) ) $metaobjects = array($metaobjects);
    11231142                foreach ( $metaobjects as $meta ) {
     
    11301149            }
    11311150        }
     1151
     1152        // Duplicate summary (primarily for summary settings data)
     1153        $Summary = new ProductSummary($original);
     1154        $Summary->product = $this->id;
     1155        $Summary->save();
    11321156
    11331157        // Re-summarize product pricing
  • shopp/trunk/core/model/Promotion.php

    r831831 r865739  
    132132
    133133        $table = ShoppDatabaseObject::tablename(ShoppPrice::$table);
     134        echo "SELECT id,product,discounts,FIND_IN_SET($this->id,discounts) AS offset FROM $table WHERE id IN ('" . join(',', $pricetags) . "')";
    134135        $discounted = sDB::query("SELECT id,product,discounts,FIND_IN_SET($this->id,discounts) AS offset FROM $table WHERE id IN ('" . join(',', $pricetags) . "')", 'array');
    135136
     
    139140            $promos = explode(',', $pricetag->discounts);
    140141            array_splice($promos, ($pricetag->offset - 1), 1); // Remove the located promotion ID from the discounts list
     142            echo "UPDATE $table SET discounts='" . join(',', $promos) . "' WHERE id=$pricetag->id";
    141143            sDB::query("UPDATE $table SET discounts='" . join(',', $promos) . "' WHERE id=$pricetag->id");
    142144        }
     
    144146        // Force resum on products next load
    145147        $summary = ShoppDatabaseObject::tablename('summary');
    146         sDB::query("UPDATE $summary SET modified='" . ProductSummary::$_updates . "' WHERE product IN (" . join(',', $products). ")");
     148        echo "UPDATE $summary SET modified='" . ProductSummary::RECALCULATE . "' WHERE product IN (" . join(',', $products). ")";
     149        sDB::query("UPDATE $summary SET modified='" . ProductSummary::RECALCULATE . "' WHERE product IN (" . join(',', $products). ")");
    147150    }
    148151
  • shopp/trunk/core/model/Purchase.php

    r831831 r865739  
    253253     **/
    254254    public function isrefunded () {
    255         if (empty($this->events)) $this->load_events();
    256         return ($this->refunded == $this->captured);
     255        if ( empty($this->events) ) $this->load_events();
     256        return ( $this->refunded == $this->captured );
    257257    }
    258258
     
    266266     **/
    267267    public function isvoid () {
    268         if (empty($this->events)) $this->load_events();
     268        if ( empty($this->events) ) $this->load_events();
    269269        return ($this->voided > 0 && $this->voided >= $this->invoiced);
    270270    }
     
    285285
    286286    public function capturable () {
    287         if (!$this->authorized) return 0.0;
     287        if ( ! $this->authorized ) return 0.0;
    288288        return ($this->authorized - (float)$this->captured);
    289289    }
     
    471471                sprintf(__('Order #%s: %s', 'Shopp'), $Purchase->id, $Event->label()), // Subject
    472472                "email-merchant-$Event->name.php")      // Template
    473         ));
     473        ), $Event);
    474474
    475475        // Event-specific hook for event specific email messages
    476         $messages = apply_filters('shopp_' . $Event->name . '_order_event_emails', $messages);
     476        $messages = apply_filters('shopp_' . $Event->name . '_order_event_emails', $messages, $Event);
    477477
    478478        foreach ( $messages as $name => $message ) {
     
    820820                        case "discount":
    821821                                            $meta_table = ShoppDatabaseObject::tablename(ShoppMetaObject::$table);
    822                                             $joins[ $meta_table ] = "INNER JOIN $meta_table AS discounts ON discounts.parent = o.id";
    823                                             $search[] = "discounts.value LIKE '%$keyword%' AND discounts.context='purchase' AND discounts.name='discounts'"; break;
     822                                            $joins[ $meta_table ] = "INNER JOIN $meta_table AS discounts ON discounts.parent = o.id AND discounts.name='discounts' AND discounts.context='purchase'";
     823                                            $search[] = "discounts.value LIKE '%$keyword%'"; break;
    824824                    }
    825825                }
     
    838838        $offset = ($this->set * $this->limit);
    839839
    840         $c = 0; $columns = array(); $purchasedcols = false;
     840        $c = 0; $columns = array(); $purchasedcols = false; $discountcols = false;
    841841        foreach ( $this->selected as $column ) {
    842842            $columns[] = "$column AS col".$c++;
    843843            if ( false !== strpos($column, 'p.') ) $purchasedcols = true;
     844            if ( false !== strpos($column, 'discounts') ) $discountcols = true;
    844845        }
    845846        if ( $purchasedcols ) $FROM = "FROM $purchasedtable AS p INNER JOIN $purchasetable AS o ON o.id=p.purchase";
    846847        else $FROM = "FROM $purchasetable AS o";
     848
     849        if ( $discountcols ) {
     850            $meta_table = ShoppDatabaseObject::tablename(ShoppMetaObject::$table);
     851            $joins[ $meta_table ] = "INNER JOIN $meta_table AS discounts ON discounts.parent = o.id AND discounts.name='discounts' AND discounts.context='purchase'";
     852        }
    847853
    848854        $joins = join(' ', $joins);
  • shopp/trunk/core/model/Settings.php

    r831831 r865739  
    3535     * the unavailable flag is set.
    3636     *
    37      * @author Jonathan Davis
    3837     * @since 1.0
    3938     * @version 1.1
     
    6362     * Update the availability status of the settings database table
    6463     *
    65      * @author Jonathan Davis
    6664     * @since 1.1
    6765     *
     
    7573     * Load settings from the database
    7674     *
    77      * @author Jonathan Davis
    7875     * @since 1.0
    7976     *
     
    108105     * Add a new setting to the registry and store it in the database
    109106     *
    110      * @author Jonathan Davis
    111107     * @since 1.0
    112108     *
     
    131127     * Updates the setting in the registry and the database
    132128     *
    133      * @author Jonathan Davis
    134129     * @since 1.0
    135130     *
     
    161156     * Save a setting to the database
    162157     *
    163      * @author Jonathan Davis
    164158     * @since 1.0
    165159     *
     
    181175     * Save a setting to the database if it does not already exist
    182176     *
    183      * @author Jonathan Davis
    184177     * @since 1.1
    185178     *
     
    195188     * Remove a setting from the registry and the database
    196189     *
    197      * @author Jonathan Davis
    198190     * @since 1.0
    199191     *
     
    221213     * loading it directly from the database.
    222214     *
    223      * @author Jonathan Davis
    224215     * @since 1.0
    225216     *
     
    252243     * Restores a serialized value to a runtime object/structure
    253244     *
    254      * @author Jonathan Davis
    255245     * @since 1.0
    256246     *
     
    261251        if ( ! is_string($value) ) return $value;
    262252        // Return unserialized, if serialized value
    263         if ( is_serialized($value) ) {
    264             $restored = unserialize($value);
    265             if ( empty($restored) ) $restored = unserialize( stripslashes($value) );
     253        if ( sDB::serialized($value) ) {
     254            $restored = @unserialize($value);
     255            if ( empty($restored) ) $restored = @unserialize( stripslashes($value) );
    266256            if ( false !== $restored ) return $restored;
    267257        }
     
    272262     * Provides a blank setting object template
    273263     *
    274      * @author Jonathan Davis
    275264     * @since 1.0
    276265     *
     
    294283     * Automatically collect and save settings from a POST form
    295284     *
    296      * @author Jonathan Davis
    297285     * @since 1.0
    298286     *
     
    301289    public function saveform () {
    302290        if ( empty($_POST['settings']) || ! is_array($_POST['settings']) ) return false;
    303         foreach ( $_POST['settings'] as $setting => $value )
    304             $this->save($setting, stripslashes_deep($value));
     291
     292        $settings = stripslashes_deep($_POST['settings']);
     293        $settings = Shopp::trim_deep($settings);
     294
     295        foreach ( $settings as $setting => $value )
     296            $this->save($setting, $value);
     297
    305298    }
    306299
     
    311304     * also checks the legacy
    312305     *
    313      * @author Jonathan Davis
    314306     * @since 1.3
    315307     *
     
    328320        // Try again using the legacy table
    329321        if ( false === $version && false === $legacy ) $version = self::dbversion('legacy');
     322        elseif ( false !== $legacy ) { // No version in the legacy settings table, possible 1.0 install?
     323            // Look in the old settings table for the old Shopp version setting
     324            $shopp_version = sDB::query("SELECT value FROM $table WHERE name='version' ORDER BY id DESC LIMIT 1", 'object', 'col');
     325            // Use (int) 1 to indicate Shopp 1.0 installed and avoid the install process
     326            if ( version_compare($shopp_version, '1.1', '<') ) $version = 1;
     327        }
    330328
    331329        if ( false === $version ) ShoppSettings()->registry['db_version'] = null;
     
    335333    }
    336334
     335
    337336} // END class Settings
  • shopp/trunk/core/model/Shiprates.php

    r831831 r865739  
    257257
    258258        foreach ( $this->shippable as $id => $free ) {
    259             if ( $free ) continue;
    260259
    261260            $CartItem = shopp_cart_item($id);
     
    263262
    264263            $Item = new ShoppShippableItem( $CartItem );
     264            $this->item($Item);
    265265            do_action_ref_array('shopp_calculate_item_shipping', array($id, &$Item));
    266266        }
     
    472472        if ( ! $this->shippable ) return false;
    473473
    474         $this->id = $Item->fingerprint();
    475         $this->quantity = $Item->quantity;
    476         $this->fees = $Item->shipfee;
    477         $this->weight = $Item->weight;
    478         $this->length = $Item->length;
    479         $this->width = $Item->width;
    480         $this->height = $Item->height;
    481         $this->shipsfree = $Item->freeshipping;
     474        $this->id        = $Item->fingerprint();
     475        $this->quantity  =& $Item->quantity;
     476        $this->fees      =& $Item->shipfee;
     477        $this->weight    =& $Item->weight;
     478        $this->length    =& $Item->length;
     479        $this->width     =& $Item->width;
     480        $this->height    =& $Item->height;
     481        $this->shipsfree =& $Item->freeshipping;
    482482    }
    483483
  • shopp/trunk/core/model/Tax.php

    r831831 r865739  
    218218     * @author Jonathan Davis
    219219     * @since 1.3
     220     * @deprecated Use ShoppTax->location()
    220221     *
    221222     * @param BillingAddress $Billing The billing address
     
    225226     **/
    226227    public function address ( BillingAddress $Billing, ShippingAddress $Shipping = null, $shipped = false ) {
    227 
    228228        $Address = $Billing;
    229229        if ( $shipped && null !== $Shipping || shopp_setting_enabled('tax_destination') ) // @todo add setting for "Apply tax to the shipping address"
     
    238238
    239239        $this->address = array_merge(apply_filters('shopp_taxable_address', compact('country','zone','locale')));
     240
     241        return $this->address;
     242    }
     243
     244
     245
     246    /**
     247     * Sets the taxable location (address) for matching tax rates
     248     *
     249     * @author Jonathan Davis
     250     * @since 1.3.2
     251     *
     252     * @param string $country The country code
     253     * @param string $state The state name or code
     254     * @param string $locale (optional) The locale name
     255     * @return array An associative array containing the country, zone and locale
     256     **/
     257    public function location ( string $country = null, string $state = null, string $locale = null ) {
     258
     259        $address = apply_filters('shopp_taxable_address', array(
     260            'country' => $country,
     261            'zone' => $state,
     262            'locale' => $locale
     263        ));
     264
     265        $this->address = array_merge($this->address, array_filter($address));
    240266
    241267        return $this->address;
  • shopp/trunk/core/ui/behaviors/catalog.js

    r821385 r865739  
    395395
    396396        if (input.hasClass('email') && !input.val().match( // RFC822 & RFC5322 Email validation
    397              new RegExp(/^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+)*\.(aero|arpa|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro|travel|mobi|[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i)))
     397             new RegExp(/^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+)*\.([a-z][a-z0-9]+)|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i)))
    398398             error = new Array($cv.email,field);
    399399
  • shopp/trunk/core/ui/behaviors/priceline.js

    r821385 r865739  
    501501    _.Shipped = function (data) {
    502502        _.price(data.price,data.tax);
    503         _.saleprice(data.sale,data.saleprice);
     503        _.saleprice(data._sale,data.saleprice);
    504504        _.shipping(data.shipping,data.dimensions,data.shipfee);
    505505        if (!tmp) _.inventory(data.inventory,data.stock,data.sku);
  • shopp/trunk/core/ui/behaviors/priceline.min.js

    r821385 r865739  
    44 * Licensed under the GPLv3 {@see license.txt}
    55 */
    6 function Pricelines(){var $=jqnc(),_=this;_.idx=0;_.row=new Object();_.variations=new Array();_.addons=new Array();_.linked=new Array();_.add=function(options,data,target,attachment){if(!data){data={context:"product"}}var key,p,targetkey,index;if(data.context=="variation"){key=xorkey(options);p=new Priceline(_.idx,options,data,target,attachment);_.row[key]=p;if(attachment){targetkey=parseInt(target.optionkey.val(),10);index=$.inArray(targetkey,_.variations);if(index!=-1){if(attachment=="before"){_.variations.splice(index,0,xorkey(p.options))}else{_.variations.splice(index+1,0,xorkey(p.options))}}}else{_.variations.push(xorkey(p.options))}}if(data.context=="addon"){p=new Priceline(_.idx,options,data,target,attachment);_.row[options]=p}if(data.context=="product"){p=new Priceline(0,options,data,target,attachment);_.row[0]=p}$("#prices").val(_.idx++)};_.exists=function(key){if(_.row[key]){return true}return false};_.remove=function(row){var index=$.inArray(row,_.variations);if(index!=-1){_.variations.splice(index,1)}_.row[row].row.remove();delete _.row[row]};_.reorderVariation=function(key,options){var variation=_.row[key],index=$.inArray(key,_.variations);variation.row.appendTo("#variations-pricing");variation.setOptions(options);if(index==-1){return}_.variations.splice(index,1);_.variations.push(xorkey(variation.options))};_.reorderAddon=function(id,pricegroup){var addon=_.row[id];addon.row.appendTo(pricegroup)};_.updateVariationsUI=function(type){var i,key,row,option;for(i in _.variations){key=_.variations[i];if(!Pricelines.row[key]){delete _.variations[i];continue}row=Pricelines.row[key];row.updateTabIndex(i);if(type&&type=="tabs"){continue}row.unlinkInputs();for(option in _.linked){if($.inArray(option,_.row[key].options)!=-1){if(!_.linked[option][key]){_.linked[option].push(key)}_.row[key].linkInputs(option)}}}};_.linkVariations=function(option){if(!option){return}for(var key in _.row){if($.inArray(option.toString(),_.row[key].options)!=-1){if(!_.linked[option]){_.linked[option]=new Array()}_.linked[option].push(key);_.row[key].linkInputs(option)}}};_.unlinkVariations=function(option){if(!option){return}if(!_.linked[option]){return}for(var row in _.linked[option]){if(_.row[_.linked[option][row]]){_.row[_.linked[option][row]].unlinkInputs(option)}}_.linked.splice(option,1)};_.unlinkAll=function(){for(var key in _.row){_.row[key].unlinkInputs()}_.linked.splice(0,1)};_.updateVariationLinks=function(){if(!_.linked){return}var key,option;for(key in _.row){_.row[key].unlinkInputs()}for(option in _.linked){_.linked[option]=false;_.linkVariations(option)}};_.allLinked=function(){if(_.linked[0]){return true}return false};_.linkAll=function(){_.unlinkAll();_.linked=new Array();_.linked[0]=new Array();for(var key in _.row){if(key==0){continue}_.linked[0].push(key);_.row[key].linkInputs(0)}}}function Priceline(id,options,data,target,attachment){var $=jqnc(),_=this,tmp=template,controller=Pricelines,typeOptions="",i,fn,heading,labelText,myid,context,optionids,sortorder,optionkey,type,dataCell,pricingTable,headingsRow,inputsRow;_.id=id;_.options=options;_.data=data;_.label=false;_.links=new Array();_.inputs=new Array();_.lasttype=false;i=_.id;fn="price["+i+"]";_.row=$('<div id="row-'+i+'" class="priceline" />');if(attachment=="after"){_.row.insertAfter(target)}else{if(attachment=="before"){_.row.insertBefore(target)}else{_.row.appendTo(target)}}heading=$('<div class="pricing-label" />').appendTo(_.row);labelText=$('<label for="label-'+i+'" />').appendTo(heading);_.label=$('<input type="hidden" name="price['+i+'][label]" id="label-'+i+'" />').appendTo(heading);_.label.change(function(){labelText.text($(this).val())});if(!data.id){data.id=""}if(!data.product){data.product=product}if(!data.donation){data.donation={"var":false,min:false}}if(!data.dimensions){data.dimensions={weight:0,height:0,width:0,length:0}}$('<input type="hidden" name="'+fn+'[id]" id="priceid-'+i+'" value="'+data.id+'" /><input type="hidden" name="'+fn+'[product]" id="product-'+i+'" value="'+data.product+'" /><input type="hidden" name="'+fn+'[context]" id="context-'+i+'"/><input type="hidden" name="'+fn+'[optionkey]" id="optionkey-'+i+'" class="optionkey" /><input type="hidden" name="'+fn+'[options]" id="options-'+i+'" value="" /><input type="hidden" name="sortorder[]" id="sortorder-'+i+'" value="'+i+'" />').appendTo(heading);myid=$("#priceid-"+i);context=$("#context-"+i);optionids=$("#options-"+i);sortorder=$("#sortorder-"+i);optionkey=$("#optionkey-"+i);_.row.optionkey=optionkey;$(priceTypes).each(function(t,option){if("addon"==data.context&&"Subscription"==option.label){return}typeOptions+='<option value="'+option.value+'">'+option.label+"</option>"});type=$('<select name="price['+i+'][type]" id="type-'+i+'"></select>').html(typeOptions).appendTo(heading);if(data&&data.label){_.label.val(htmlentities(data.label)).change();type.val(data.type)}dataCell=$('<div class="pricing-ui clear" />').appendTo(_.row);pricingTable=$("<table/>").addClass("pricing-table").appendTo(dataCell);headingsRow=$("<tr/>").appendTo(pricingTable);inputsRow=$("<tr/>").appendTo(pricingTable);_.price=function(price,tax){var hd,ui;hd=$('<th><label for="price-'+i+'">'+PRICE_LABEL+"</label></th>").appendTo(headingsRow);ui=$('<td><input type="text" name="'+fn+'[price]" id="price-'+i+'" value="0" size="10" class="selectall money right" /><br /><input type="hidden" name="'+fn+'[tax]" value="on" /><input type="checkbox" name="'+fn+'[tax]" id="tax-'+i+'" value="off" /><label for="tax-'+i+'"> '+NOTAX_LABEL+"</label><br /></td>").appendTo(inputsRow);_.p=$("#price-"+i).val(asMoney(new Number(price)));_.t=$("#tax-"+i).attr("checked",tax=="off"?true:false)};_.saleprice=function(toggle,saleprice){var hd,ui,dis;hd=$('<th><input type="hidden" name="'+fn+'[sale]" value="off" /><input type="checkbox" name="'+fn+'[sale]" id="sale-'+i+'" value="on" /><label for="sale-'+i+'"> '+SALE_PRICE_LABEL+"</label></th>").appendTo(headingsRow);ui=$('<td><span class="status">'+NOT_ON_SALE_TEXT+'</span><span class="ui"><input type="text" name="'+fn+'[saleprice]" id="saleprice-'+i+'" size="10" class="selectall money right" /></span></td>').appendTo(inputsRow);dis=ui.find("span.status");ui=ui.find("span.ui").hide();_.sp=$("#saleprice-"+i).val(asMoney(new Number(saleprice)));_.spt=$("#sale-"+i).attr("checked",(toggle=="on"?true:false)).toggler(dis,ui,_.sp)};_.donation=function(price,tax,variable,minimum){var hd,ui,hd2,ui2;hd=$('<th><label for="price-'+i+'"> '+AMOUNT_LABEL+"</label></th>").appendTo(headingsRow);ui=$('<td><input type="text" name="'+fn+'[price]" id="price-'+i+'" value="0" size="10" class="selectall money right" /><br /><input type="hidden" name="'+fn+'[tax]" value="on" /><input type="checkbox" name="'+fn+'[tax]" id="tax-'+i+'" value="off" /><label for="tax-'+i+'"> '+NOTAX_LABEL+"</label><br /></td>").appendTo(inputsRow);_.p=$("#price-"+i).val(asMoney(new Number(price)));_.t=$("#tax-"+i).attr("checked",tax=="on"?false:true);hd2=$("<th />").appendTo(headingsRow);ui2=$('<td width="80%"><input type="hidden" name="'+fn+'[donation][var]" value="off" /><input type="checkbox" name="'+fn+'[donation][var]" id="donation-var-'+i+'" value="on" /><label for="donation-var-'+i+'"> '+DONATIONS_VAR_LABEL+'</label><br /><input type="hidden" name="'+fn+'[donation][min]" value="off" /><input type="checkbox" name="'+fn+'[donation][min]" id="donation-min-'+i+'" value="on" /><label for="donation-min-'+i+'"> '+DONATIONS_MIN_LABEL+"</label><br /></td>").appendTo(inputsRow);_.dv=$("#donation-var-"+i).attr("checked",variable=="on"?true:false);_.dm=$("#donation-min-"+i).attr("checked",minimum=="on"?true:false)};_.shipping=function(toggle,dimensions,fee){var hd,ui,dis,inf,dc,dw,dl,dwd,dh,dv,nf=getCurrencyFormat();nf.precision="2";hd=$('<th><input type="hidden" name="'+fn+'[shipping]" value="off" /><input type="checkbox" name="'+fn+'[shipping]" id="shipping-'+i+'" value="on" /><label for="shipping-'+i+'"> '+SHIPPING_LABEL+"</label></th>").appendTo(headingsRow);ui=$('<td><span class="status">'+FREE_SHIPPING_TEXT+'</span><span class="ui"><input type="text" name="'+fn+'[dimensions][weight]" id="weight-'+i+'" size="8" class="selectall right" /><label for="weight-'+i+'" id="weight-label-'+i+'" title="'+WEIGHT_LABEL+'"> '+WEIGHT_LABEL+((weightUnit)?" ("+weightUnit+")":"")+'</label><br /><span class="dimui"></span><input type="text" name="'+fn+'[shipfee]" id="shipfee-'+i+'" size="8" class="selectall money right" /><label for="shipfee-'+i+'" title="'+SHIPFEE_XTRA+'"> '+SHIPFEE_LABEL+"</label><br /></span></td>").appendTo(inputsRow);dis=ui.find("span.status");inf=ui.find("span.ui").hide();dui=ui.find(".dimui");if(!dimensions.weight){dimensions.weight=0}_.w=$("#weight-"+i).val(formatNumber(new Number(dimensions.weight),nf,true)).bind("change.value",function(){this.value=formatNumber(isNaN(this.value)?0:this.value,nf,true)});_.fee=$("#shipfee-"+i);_.fee.val(asMoney(new Number(fee)));_.st=hd.find("#shipping-"+i).attr("checked",(toggle=="off"?false:true)).toggler(dis,inf,_.w);if(dimensionsRequired){dv=function(e,init){var value=this.value;if(init){value=new Number(value)}this.value=formatNumber(value,nf,true)};$("#weight-label-"+i).html(" "+dimensionUnit+"<sup>3</sup>/"+weightUnit);dc=$('<div class="dimensions"><div class="inline"><input type="text" name="'+fn+'[dimensions][weight]" id="dimensions-weight-'+i+'" size="4" class="selectall right weight" />'+(weightUnit?"<label>"+weightUnit+"&nbsp;</label>":"")+'<br /><label for="dimensions-weight-'+i+'" title="'+WEIGHT_LABEL+'"> '+WEIGHT_LABEL+'</label></div><div class="inline"><input type="text" name="'+fn+'[dimensions][length]" id="dimensions-length-'+i+'" size="4" class="selectall right" /><label> x </label><br /><label for="dimensions-length-'+i+'" title="'+LENGTH_LABEL+'"> '+LENGTH_LABEL+'</label></div><div class="inline"><input type="text" name="'+fn+'[dimensions][width]" id="dimensions-width-'+i+'" size="4" class="selectall right" /><label> x </label><br /><label for="dimensions-width-'+i+'" title="'+WIDTH_LABEL+'"> '+WIDTH_LABEL+'</label></div><div class="inline"><input type="text" name="'+fn+'[dimensions][height]" id="dimensions-height-'+i+'" size="4" class="selectall right" /><label>'+dimensionUnit+'</label><br /><label for="dimensions-height-'+i+'" title="'+HEIGHT_LABEL+'"> '+HEIGHT_LABEL+"</label></div></div>").hide().appendTo(dui);if(!(dimensions instanceof Object)){dimensions={weight:0,length:0,width:0,height:0}}_.dw=$("#dimensions-weight-"+i).val(dimensions.weight).bind("change.value",dv).trigger("change.value",true);_.dl=$("#dimensions-length-"+i).val(dimensions.length).bind("change.value",dv).trigger("change.value",true);_.dwd=$("#dimensions-width-"+i).val(dimensions.width).bind("change.value",dv).trigger("change.value",true);_.dh=$("#dimensions-height-"+i).val(dimensions.height).bind("change.value",dv).trigger("change.value",true);function volumeWeight(){var d=0,w=0;dc.find("input").each(function(id,dims){if($(dims).hasClass("weight")){w=asNumber(dims.value)}else{if(d==0){d=asNumber(dims.value)}else{d*=asNumber(dims.value)}}});if(!isNaN(d/w)){_.w.val((d/w)).trigger("change.value")}}function toggleDimensions(){_.w.toggleClass("extoggle");dc.toggle();_.dw.focus();volumeWeight()}_.st.change(function(){if(!$(this).attr("checked")){dc.hide()}});_.dh.blur(toggleDimensions);_.w.click(toggleDimensions).attr("readonly",true);volumeWeight()}};_.inventory=function(toggle,stock,sku){var hd,ui,dis;hd=$('<th><input type="hidden" name="'+fn+'[inventory]" value="off" /><input type="checkbox" name="'+fn+'[inventory]" id="inventory-'+i+'" value="on" /><label for="inventory-'+i+'"> '+INVENTORY_LABEL+"</label></th>").appendTo(headingsRow);ui=$('<td><span class="status">'+NOT_TRACKED_TEXT+'</span><span class="ui"><input type="text" name="'+fn+'[stocked]" id="stock-'+i+'" size="8" class="selectall right" /><label for="stock-'+i+'"> '+IN_STOCK_LABEL+'</label><br /><input type="text" name="'+fn+'[sku]" id="sku-'+i+'" size="8" title="'+SKU_XTRA+'" class="selectall" /><label for="sku-'+i+'" title="'+SKU_LABEL_HELP+'"> '+SKU_LABEL+"</label></span></td>").appendTo(inputsRow);dis=ui.find("span.status");ui=ui.find("span.ui").hide();if(!stock){stock=0}_.stock=$("#stock-"+i).val(stock).trigger("change.value",function(){this.value=new Number(this.value)});_.sku=$("#sku-"+i).val(sku);_.it=hd.find("#inventory-"+i).attr("checked",(toggle=="on"?true:false)).toggler(dis,ui,_.stock)};_.download=function(d){var hd,ui,hd2,fc;hd=$('<th><label for="download-'+i+'">'+PRODUCT_DOWNLOAD_LABEL+"</label></th>").appendTo(headingsRow);ui=$('<td width="31%"><input type="hidden" name="'+fn+'[downloadpath]" id="download_path-'+i+'"/><input type="hidden" name="'+fn+'[downloadfile]" id="download_file-'+i+'"/><div id="file-'+i+'" class="status">'+NO_DOWNLOAD+"</div></td>").appendTo(inputsRow);hd2=$('<td rowspan="2" class="controls" width="75"><button type="button" class="button-secondary" style="white-space: nowrap;" id="file-selector-'+i+'"><small>'+SELECT_FILE_BUTTON_TEXT+"&hellip;</small></button></td>").appendTo(headingsRow);_.file=$("#file-"+i);_.selector=$("#file-selector-"+i).FileChooser(i,_.file);if(d&&d.id){fc=d.mime.replace("/"," ");_.file.attr("class","file").html('<div class="icon shoppui-file '+fc+'"></div>'+d.name+"<br /><small>"+readableFileSize(d.size)+"</small>").click(function(){window.location.href=adminurl+"admin.php?src=download&shopp_download="+d.id})}};_.recurring=function(r){var hd,ui,hd2,ui2,ints,n,cycs='<option value="0">&infin;</option>',pp,ps;for(n=1;n<31;n++){ints+='<option value="'+n+'">'+n+"</option>";if(n>1){cycs+='<option value="'+n+'">'+n+"</option>"}}$(billPeriods[0]).each(function(n,option){pp+='<option value="'+option.value+'">'+option.label+"</option>"});$(billPeriods[1]).each(function(n,option){ps+='<option value="'+option.value+'">'+option.label+"</option>"});hd2=$('<th><input type="hidden" name="'+fn+'[recurring][trial]" value="off" /><input type="checkbox" name="'+fn+'[recurring][trial]" id="trial-'+i+'" /><label for="trial-'+i+'"> '+TRIAL_LABEL+"</label></th>").appendTo(headingsRow);ui2=$('<td><span class="status">'+NOTRIAL_TEXT+'</span><span class="ui"><select name="'+fn+'[recurring][trialint]" id="trialint-'+i+'">'+ints+'</select><select name="'+fn+'[recurring][trialperiod]" id="trialperiod-'+i+'" class="period">'+pp+'</select><br /><input type="text" name="'+fn+'[recurring][trialprice]" id="trialprice-'+i+'" value="0" size="10" class="selectall money right" /><label for="trialprice-'+i+'">&nbsp;'+PRICE_LABEL+"</label></span></td>").appendTo(inputsRow);hd=$('<th><label for="billcycle-'+i+'"> '+BILLCYCLE_LABEL+"</label></th>").appendTo(headingsRow);ui=$('<td><select name="'+fn+'[recurring][interval]" id="interval-'+i+'">'+ints+'</select><select name="'+fn+'[recurring][period]" id="period-'+i+'" class="period">'+pp+'</select><br /><select name="'+fn+'[recurring][cycles]" id="cycles-'+i+'">'+cycs+'</select><label for="cycles'+i+'">&nbsp;'+TIMES_LABEL+"</label></td>").appendTo(inputsRow);dis=ui2.find("span.status");ui=ui2.find("span.ui").hide();if(!r){r={period:1,interval:"d",cycles:0,trialperiod:1,trialint:1,trialprice:0}}_.period=$("#period-"+i).val(r.period);_.interval=$("#interval-"+i).val(r.interval).change(function(){var $this=$(this),s=_.period.val();if($this.val()==1){_.period.html(ps)}else{_.period.html(pp)}_.period.val(s)}).change();_.cycles=$("#cycles-"+i).val(r.cycles);_.trialperiod=$("#trialperiod-"+i).val(r.trialperiod);_.trialint=$("#trialint-"+i).val(r.trialint).change(function(){var $this=$(this),s=_.trialperiod.val();if($this.val()==1){_.trialperiod.html(ps)}else{_.trialperiod.html(pp)}_.trialperiod.val(s)}).change();_.trialprice=$("#trialprice-"+i).val(asMoney(new Number(r.trialprice)));_.trial=hd2.find("#trial-"+i).attr("checked",(r.trial=="on"?true:false)).toggler(dis,ui,_.trialint)};_.memberlevel=function(){var hd,ui,memberships,mo;memberships=["Basic","Silver","Gold","Platinum"];$(memberships).each(function(n,option){mo+='<option value="'+option+'">'+option+"</option>"});hd=$('<th><label for="membership-'+i+'"> '+MEMBERSHIP_LABEL+"</label></th>").appendTo(headingsRow);ui=$('<td><select name="'+fn+'[membership]" id="membership-'+i+'" class="membership">'+mo+"</select></td>").appendTo(inputsRow)};$.fn.toggler=function(s,ui,f){this.bind("change.value",function(){if(this.checked){s.hide();ui.show()}else{s.show();ui.hide()}if($.ua.msie){$(this).blur()}}).click(function(){if($.ua.msie){$(this).trigger("change.value")}if(this.checked){f.focus().select()}}).trigger("change.value");return $(this)};_.Shipped=function(data){_.price(data.price,data.tax);_.saleprice(data.sale,data.saleprice);_.shipping(data.shipping,data.dimensions,data.shipfee);if(!tmp){_.inventory(data.inventory,data.stock,data.sku)}};_.Virtual=function(data){_.price(data.price,data.tax);_.saleprice(data.sale,data.saleprice);if(!tmp){_.inventory(data.inventory,data.stock,data.sku)}};_.Download=function(data){_.price(data.price,data.tax);_.saleprice(data.sale,data.saleprice);if(!tmp){_.download(data.download)}};_.Donation=function(data){_.donation(data.price,data.tax,data.donation["var"],data.donation.min)};_.Subscription=function(data){_.price(data.price,data.tax);_.saleprice(data.sale,data.saleprice);_.recurring(data.recurring)};_.Membership=function(data){_.price(data.price,data.tax);_.saleprice(data.sale,data.saleprice);_.recurring();if(!tmp){_.memberlevel()}};type.bind("change.value",function(){headingsRow.empty();inputsRow.empty();var ui=type.val();if(ui=="Shipped"){_.Shipped(data)}if(ui=="Virtual"){_.Virtual(data)}if(ui=="Download"){_.Download(data)}if(ui=="Donation"){_.Donation(data)}if(ui=="Subscription"){_.Subscription(data)}if(ui=="Membership"){_.Membership(data)}inputsRow.find("input.money").bind("change.value",function(){this.value=asMoney(this.value)}).trigger("change.value");quickSelects(inputsRow)}).trigger("change.value");_.disable=function(){_.lasttype=(type.val())?type.val():false;type.val("N/A").trigger("change.value")};_.enable=function(){if(_.lasttype){type.val(_.lasttype).trigger("change.value")}};if(data&&data.context){context.val(data.context)}else{context.val("product")}_.setOptions=function(options){var update=false;if(options){if(options!=_.options){update=true}_.options=options}if(context.val()=="variation"){optionkey.val(xorkey(_.options))}if(update){_.updateLabel()}};_.updateKey=function(){optionkey.val(xorkey(_.options))};_.updateLabel=function(){var type=context.val(),string="",ids="";if(_.options){if(type=="variation"){$(_.options).each(function(index,id){if(string==""){string=$(productOptions[id]).val()}else{string+=", "+$(productOptions[id]).val()}if(ids==""){ids=id}else{ids+=","+id}})}if(type=="addon"){string=$(productAddons[_.options]).val();ids=_.options}}if(string==""){string=DEFAULT_PRICELINE_LABEL}_.label.val(htmlentities(string)).change();optionids.val(ids)};_.updateTabIndex=function(row){row=new Number(row);$.each(_.inputs,function(i,input){$(input).attr("tabindex",((row+1)*100)+i)})};_.linkInputs=function(option){_.links.push(option);$.each(_.inputs,function(i,input){if(!input){return}var type="change.linkedinputs",elem=$(input);if(elem.attr("type")=="checkbox"){type="click.linkedinputs"}$(input).bind(type,function(){var value=$(this).val(),checked=$(this).attr("checked");$.each(_.links,function(l,option){$.each(controller.linked[option],function(id,key){if(key==xorkey(_.options)){return}if(!controller.row[key]){return}if(elem.attr("type")=="checkbox"){$(controller.row[key].inputs[i]).attr("checked",checked)}else{$(controller.row[key].inputs[i]).val(value)}$(controller.row[key].inputs[i]).trigger("change.value")})})})})};_.unlinkInputs=function(option){if(option!==false){index=$.inArray(option,_.links);_.links.splice(index,1)}$.each(_.inputs,function(i,input){if(!input){return}var type="blur.linkedinputs";if($(input).attr("type")=="checkbox"){type="click.linkedinputs"}$(input).unbind(type)})};if(type.val()!="N/A"){_.inputs=new Array(type,_.p,_.t,_.spt,_.sp,_.dv,_.dm,_.st,_.w,_.dw,_.dl,_.dwd,_.dh,_.fee,_.it,_.stock,_.sku,_.period,_.interval,_.cycles,_.trialperiod,_.trialint,_.trialprice,_.trial)}_.updateKey();_.updateLabel()};
     6function Pricelines(){var $=jqnc(),_=this;_.idx=0;_.row=new Object();_.variations=new Array();_.addons=new Array();_.linked=new Array();_.add=function(options,data,target,attachment){if(!data){data={context:"product"}}var key,p,targetkey,index;if(data.context=="variation"){key=xorkey(options);p=new Priceline(_.idx,options,data,target,attachment);_.row[key]=p;if(attachment){targetkey=parseInt(target.optionkey.val(),10);index=$.inArray(targetkey,_.variations);if(index!=-1){if(attachment=="before"){_.variations.splice(index,0,xorkey(p.options))}else{_.variations.splice(index+1,0,xorkey(p.options))}}}else{_.variations.push(xorkey(p.options))}}if(data.context=="addon"){p=new Priceline(_.idx,options,data,target,attachment);_.row[options]=p}if(data.context=="product"){p=new Priceline(0,options,data,target,attachment);_.row[0]=p}$("#prices").val(_.idx++)};_.exists=function(key){if(_.row[key]){return true}return false};_.remove=function(row){var index=$.inArray(row,_.variations);if(index!=-1){_.variations.splice(index,1)}_.row[row].row.remove();delete _.row[row]};_.reorderVariation=function(key,options){var variation=_.row[key],index=$.inArray(key,_.variations);variation.row.appendTo("#variations-pricing");variation.setOptions(options);if(index==-1){return}_.variations.splice(index,1);_.variations.push(xorkey(variation.options))};_.reorderAddon=function(id,pricegroup){var addon=_.row[id];addon.row.appendTo(pricegroup)};_.updateVariationsUI=function(type){var i,key,row,option;for(i in _.variations){key=_.variations[i];if(!Pricelines.row[key]){delete _.variations[i];continue}row=Pricelines.row[key];row.updateTabIndex(i);if(type&&type=="tabs"){continue}row.unlinkInputs();for(option in _.linked){if($.inArray(option,_.row[key].options)!=-1){if(!_.linked[option][key]){_.linked[option].push(key)}_.row[key].linkInputs(option)}}}};_.linkVariations=function(option){if(!option){return}for(var key in _.row){if($.inArray(option.toString(),_.row[key].options)!=-1){if(!_.linked[option]){_.linked[option]=new Array()}_.linked[option].push(key);_.row[key].linkInputs(option)}}};_.unlinkVariations=function(option){if(!option){return}if(!_.linked[option]){return}for(var row in _.linked[option]){if(_.row[_.linked[option][row]]){_.row[_.linked[option][row]].unlinkInputs(option)}}_.linked.splice(option,1)};_.unlinkAll=function(){for(var key in _.row){_.row[key].unlinkInputs()}_.linked.splice(0,1)};_.updateVariationLinks=function(){if(!_.linked){return}var key,option;for(key in _.row){_.row[key].unlinkInputs()}for(option in _.linked){_.linked[option]=false;_.linkVariations(option)}};_.allLinked=function(){if(_.linked[0]){return true}return false};_.linkAll=function(){_.unlinkAll();_.linked=new Array();_.linked[0]=new Array();for(var key in _.row){if(key==0){continue}_.linked[0].push(key);_.row[key].linkInputs(0)}}}function Priceline(id,options,data,target,attachment){var $=jqnc(),_=this,tmp=template,controller=Pricelines,typeOptions="",i,fn,heading,labelText,myid,context,optionids,sortorder,optionkey,type,dataCell,pricingTable,headingsRow,inputsRow;_.id=id;_.options=options;_.data=data;_.label=false;_.links=new Array();_.inputs=new Array();_.lasttype=false;i=_.id;fn="price["+i+"]";_.row=$('<div id="row-'+i+'" class="priceline" />');if(attachment=="after"){_.row.insertAfter(target)}else{if(attachment=="before"){_.row.insertBefore(target)}else{_.row.appendTo(target)}}heading=$('<div class="pricing-label" />').appendTo(_.row);labelText=$('<label for="label-'+i+'" />').appendTo(heading);_.label=$('<input type="hidden" name="price['+i+'][label]" id="label-'+i+'" />').appendTo(heading);_.label.change(function(){labelText.text($(this).val())});if(!data.id){data.id=""}if(!data.product){data.product=product}if(!data.donation){data.donation={"var":false,min:false}}if(!data.dimensions){data.dimensions={weight:0,height:0,width:0,length:0}}$('<input type="hidden" name="'+fn+'[id]" id="priceid-'+i+'" value="'+data.id+'" /><input type="hidden" name="'+fn+'[product]" id="product-'+i+'" value="'+data.product+'" /><input type="hidden" name="'+fn+'[context]" id="context-'+i+'"/><input type="hidden" name="'+fn+'[optionkey]" id="optionkey-'+i+'" class="optionkey" /><input type="hidden" name="'+fn+'[options]" id="options-'+i+'" value="" /><input type="hidden" name="sortorder[]" id="sortorder-'+i+'" value="'+i+'" />').appendTo(heading);myid=$("#priceid-"+i);context=$("#context-"+i);optionids=$("#options-"+i);sortorder=$("#sortorder-"+i);optionkey=$("#optionkey-"+i);_.row.optionkey=optionkey;$(priceTypes).each(function(t,option){if("addon"==data.context&&"Subscription"==option.label){return}typeOptions+='<option value="'+option.value+'">'+option.label+"</option>"});type=$('<select name="price['+i+'][type]" id="type-'+i+'"></select>').html(typeOptions).appendTo(heading);if(data&&data.label){_.label.val(htmlentities(data.label)).change();type.val(data.type)}dataCell=$('<div class="pricing-ui clear" />').appendTo(_.row);pricingTable=$("<table/>").addClass("pricing-table").appendTo(dataCell);headingsRow=$("<tr/>").appendTo(pricingTable);inputsRow=$("<tr/>").appendTo(pricingTable);_.price=function(price,tax){var hd,ui;hd=$('<th><label for="price-'+i+'">'+PRICE_LABEL+"</label></th>").appendTo(headingsRow);ui=$('<td><input type="text" name="'+fn+'[price]" id="price-'+i+'" value="0" size="10" class="selectall money right" /><br /><input type="hidden" name="'+fn+'[tax]" value="on" /><input type="checkbox" name="'+fn+'[tax]" id="tax-'+i+'" value="off" /><label for="tax-'+i+'"> '+NOTAX_LABEL+"</label><br /></td>").appendTo(inputsRow);_.p=$("#price-"+i).val(asMoney(new Number(price)));_.t=$("#tax-"+i).attr("checked",tax=="off"?true:false)};_.saleprice=function(toggle,saleprice){var hd,ui,dis;hd=$('<th><input type="hidden" name="'+fn+'[sale]" value="off" /><input type="checkbox" name="'+fn+'[sale]" id="sale-'+i+'" value="on" /><label for="sale-'+i+'"> '+SALE_PRICE_LABEL+"</label></th>").appendTo(headingsRow);ui=$('<td><span class="status">'+NOT_ON_SALE_TEXT+'</span><span class="ui"><input type="text" name="'+fn+'[saleprice]" id="saleprice-'+i+'" size="10" class="selectall money right" /></span></td>').appendTo(inputsRow);dis=ui.find("span.status");ui=ui.find("span.ui").hide();_.sp=$("#saleprice-"+i).val(asMoney(new Number(saleprice)));_.spt=$("#sale-"+i).attr("checked",(toggle=="on"?true:false)).toggler(dis,ui,_.sp)};_.donation=function(price,tax,variable,minimum){var hd,ui,hd2,ui2;hd=$('<th><label for="price-'+i+'"> '+AMOUNT_LABEL+"</label></th>").appendTo(headingsRow);ui=$('<td><input type="text" name="'+fn+'[price]" id="price-'+i+'" value="0" size="10" class="selectall money right" /><br /><input type="hidden" name="'+fn+'[tax]" value="on" /><input type="checkbox" name="'+fn+'[tax]" id="tax-'+i+'" value="off" /><label for="tax-'+i+'"> '+NOTAX_LABEL+"</label><br /></td>").appendTo(inputsRow);_.p=$("#price-"+i).val(asMoney(new Number(price)));_.t=$("#tax-"+i).attr("checked",tax=="on"?false:true);hd2=$("<th />").appendTo(headingsRow);ui2=$('<td width="80%"><input type="hidden" name="'+fn+'[donation][var]" value="off" /><input type="checkbox" name="'+fn+'[donation][var]" id="donation-var-'+i+'" value="on" /><label for="donation-var-'+i+'"> '+DONATIONS_VAR_LABEL+'</label><br /><input type="hidden" name="'+fn+'[donation][min]" value="off" /><input type="checkbox" name="'+fn+'[donation][min]" id="donation-min-'+i+'" value="on" /><label for="donation-min-'+i+'"> '+DONATIONS_MIN_LABEL+"</label><br /></td>").appendTo(inputsRow);_.dv=$("#donation-var-"+i).attr("checked",variable=="on"?true:false);_.dm=$("#donation-min-"+i).attr("checked",minimum=="on"?true:false)};_.shipping=function(toggle,dimensions,fee){var hd,ui,dis,inf,dc,dw,dl,dwd,dh,dv,nf=getCurrencyFormat();nf.precision="2";hd=$('<th><input type="hidden" name="'+fn+'[shipping]" value="off" /><input type="checkbox" name="'+fn+'[shipping]" id="shipping-'+i+'" value="on" /><label for="shipping-'+i+'"> '+SHIPPING_LABEL+"</label></th>").appendTo(headingsRow);ui=$('<td><span class="status">'+FREE_SHIPPING_TEXT+'</span><span class="ui"><input type="text" name="'+fn+'[dimensions][weight]" id="weight-'+i+'" size="8" class="selectall right" /><label for="weight-'+i+'" id="weight-label-'+i+'" title="'+WEIGHT_LABEL+'"> '+WEIGHT_LABEL+((weightUnit)?" ("+weightUnit+")":"")+'</label><br /><span class="dimui"></span><input type="text" name="'+fn+'[shipfee]" id="shipfee-'+i+'" size="8" class="selectall money right" /><label for="shipfee-'+i+'" title="'+SHIPFEE_XTRA+'"> '+SHIPFEE_LABEL+"</label><br /></span></td>").appendTo(inputsRow);dis=ui.find("span.status");inf=ui.find("span.ui").hide();dui=ui.find(".dimui");if(!dimensions.weight){dimensions.weight=0}_.w=$("#weight-"+i).val(formatNumber(new Number(dimensions.weight),nf,true)).bind("change.value",function(){this.value=formatNumber(isNaN(this.value)?0:this.value,nf,true)});_.fee=$("#shipfee-"+i);_.fee.val(asMoney(new Number(fee)));_.st=hd.find("#shipping-"+i).attr("checked",(toggle=="off"?false:true)).toggler(dis,inf,_.w);if(dimensionsRequired){dv=function(e,init){var value=this.value;if(init){value=new Number(value)}this.value=formatNumber(value,nf,true)};$("#weight-label-"+i).html(" "+dimensionUnit+"<sup>3</sup>/"+weightUnit);dc=$('<div class="dimensions"><div class="inline"><input type="text" name="'+fn+'[dimensions][weight]" id="dimensions-weight-'+i+'" size="4" class="selectall right weight" />'+(weightUnit?"<label>"+weightUnit+"&nbsp;</label>":"")+'<br /><label for="dimensions-weight-'+i+'" title="'+WEIGHT_LABEL+'"> '+WEIGHT_LABEL+'</label></div><div class="inline"><input type="text" name="'+fn+'[dimensions][length]" id="dimensions-length-'+i+'" size="4" class="selectall right" /><label> x </label><br /><label for="dimensions-length-'+i+'" title="'+LENGTH_LABEL+'"> '+LENGTH_LABEL+'</label></div><div class="inline"><input type="text" name="'+fn+'[dimensions][width]" id="dimensions-width-'+i+'" size="4" class="selectall right" /><label> x </label><br /><label for="dimensions-width-'+i+'" title="'+WIDTH_LABEL+'"> '+WIDTH_LABEL+'</label></div><div class="inline"><input type="text" name="'+fn+'[dimensions][height]" id="dimensions-height-'+i+'" size="4" class="selectall right" /><label>'+dimensionUnit+'</label><br /><label for="dimensions-height-'+i+'" title="'+HEIGHT_LABEL+'"> '+HEIGHT_LABEL+"</label></div></div>").hide().appendTo(dui);if(!(dimensions instanceof Object)){dimensions={weight:0,length:0,width:0,height:0}}_.dw=$("#dimensions-weight-"+i).val(dimensions.weight).bind("change.value",dv).trigger("change.value",true);_.dl=$("#dimensions-length-"+i).val(dimensions.length).bind("change.value",dv).trigger("change.value",true);_.dwd=$("#dimensions-width-"+i).val(dimensions.width).bind("change.value",dv).trigger("change.value",true);_.dh=$("#dimensions-height-"+i).val(dimensions.height).bind("change.value",dv).trigger("change.value",true);function volumeWeight(){var d=0,w=0;dc.find("input").each(function(id,dims){if($(dims).hasClass("weight")){w=asNumber(dims.value)}else{if(d==0){d=asNumber(dims.value)}else{d*=asNumber(dims.value)}}});if(!isNaN(d/w)){_.w.val((d/w)).trigger("change.value")}}function toggleDimensions(){_.w.toggleClass("extoggle");dc.toggle();_.dw.focus();volumeWeight()}_.st.change(function(){if(!$(this).attr("checked")){dc.hide()}});_.dh.blur(toggleDimensions);_.w.click(toggleDimensions).attr("readonly",true);volumeWeight()}};_.inventory=function(toggle,stock,sku){var hd,ui,dis;hd=$('<th><input type="hidden" name="'+fn+'[inventory]" value="off" /><input type="checkbox" name="'+fn+'[inventory]" id="inventory-'+i+'" value="on" /><label for="inventory-'+i+'"> '+INVENTORY_LABEL+"</label></th>").appendTo(headingsRow);ui=$('<td><span class="status">'+NOT_TRACKED_TEXT+'</span><span class="ui"><input type="text" name="'+fn+'[stocked]" id="stock-'+i+'" size="8" class="selectall right" /><label for="stock-'+i+'"> '+IN_STOCK_LABEL+'</label><br /><input type="text" name="'+fn+'[sku]" id="sku-'+i+'" size="8" title="'+SKU_XTRA+'" class="selectall" /><label for="sku-'+i+'" title="'+SKU_LABEL_HELP+'"> '+SKU_LABEL+"</label></span></td>").appendTo(inputsRow);dis=ui.find("span.status");ui=ui.find("span.ui").hide();if(!stock){stock=0}_.stock=$("#stock-"+i).val(stock).trigger("change.value",function(){this.value=new Number(this.value)});_.sku=$("#sku-"+i).val(sku);_.it=hd.find("#inventory-"+i).attr("checked",(toggle=="on"?true:false)).toggler(dis,ui,_.stock)};_.download=function(d){var hd,ui,hd2,fc;hd=$('<th><label for="download-'+i+'">'+PRODUCT_DOWNLOAD_LABEL+"</label></th>").appendTo(headingsRow);ui=$('<td width="31%"><input type="hidden" name="'+fn+'[downloadpath]" id="download_path-'+i+'"/><input type="hidden" name="'+fn+'[downloadfile]" id="download_file-'+i+'"/><div id="file-'+i+'" class="status">'+NO_DOWNLOAD+"</div></td>").appendTo(inputsRow);hd2=$('<td rowspan="2" class="controls" width="75"><button type="button" class="button-secondary" style="white-space: nowrap;" id="file-selector-'+i+'"><small>'+SELECT_FILE_BUTTON_TEXT+"&hellip;</small></button></td>").appendTo(headingsRow);_.file=$("#file-"+i);_.selector=$("#file-selector-"+i).FileChooser(i,_.file);if(d&&d.id){fc=d.mime.replace("/"," ");_.file.attr("class","file").html('<div class="icon shoppui-file '+fc+'"></div>'+d.name+"<br /><small>"+readableFileSize(d.size)+"</small>").click(function(){window.location.href=adminurl+"admin.php?src=download&shopp_download="+d.id})}};_.recurring=function(r){var hd,ui,hd2,ui2,ints,n,cycs='<option value="0">&infin;</option>',pp,ps;for(n=1;n<31;n++){ints+='<option value="'+n+'">'+n+"</option>";if(n>1){cycs+='<option value="'+n+'">'+n+"</option>"}}$(billPeriods[0]).each(function(n,option){pp+='<option value="'+option.value+'">'+option.label+"</option>"});$(billPeriods[1]).each(function(n,option){ps+='<option value="'+option.value+'">'+option.label+"</option>"});hd2=$('<th><input type="hidden" name="'+fn+'[recurring][trial]" value="off" /><input type="checkbox" name="'+fn+'[recurring][trial]" id="trial-'+i+'" /><label for="trial-'+i+'"> '+TRIAL_LABEL+"</label></th>").appendTo(headingsRow);ui2=$('<td><span class="status">'+NOTRIAL_TEXT+'</span><span class="ui"><select name="'+fn+'[recurring][trialint]" id="trialint-'+i+'">'+ints+'</select><select name="'+fn+'[recurring][trialperiod]" id="trialperiod-'+i+'" class="period">'+pp+'</select><br /><input type="text" name="'+fn+'[recurring][trialprice]" id="trialprice-'+i+'" value="0" size="10" class="selectall money right" /><label for="trialprice-'+i+'">&nbsp;'+PRICE_LABEL+"</label></span></td>").appendTo(inputsRow);hd=$('<th><label for="billcycle-'+i+'"> '+BILLCYCLE_LABEL+"</label></th>").appendTo(headingsRow);ui=$('<td><select name="'+fn+'[recurring][interval]" id="interval-'+i+'">'+ints+'</select><select name="'+fn+'[recurring][period]" id="period-'+i+'" class="period">'+pp+'</select><br /><select name="'+fn+'[recurring][cycles]" id="cycles-'+i+'">'+cycs+'</select><label for="cycles'+i+'">&nbsp;'+TIMES_LABEL+"</label></td>").appendTo(inputsRow);dis=ui2.find("span.status");ui=ui2.find("span.ui").hide();if(!r){r={period:1,interval:"d",cycles:0,trialperiod:1,trialint:1,trialprice:0}}_.period=$("#period-"+i).val(r.period);_.interval=$("#interval-"+i).val(r.interval).change(function(){var $this=$(this),s=_.period.val();if($this.val()==1){_.period.html(ps)}else{_.period.html(pp)}_.period.val(s)}).change();_.cycles=$("#cycles-"+i).val(r.cycles);_.trialperiod=$("#trialperiod-"+i).val(r.trialperiod);_.trialint=$("#trialint-"+i).val(r.trialint).change(function(){var $this=$(this),s=_.trialperiod.val();if($this.val()==1){_.trialperiod.html(ps)}else{_.trialperiod.html(pp)}_.trialperiod.val(s)}).change();_.trialprice=$("#trialprice-"+i).val(asMoney(new Number(r.trialprice)));_.trial=hd2.find("#trial-"+i).attr("checked",(r.trial=="on"?true:false)).toggler(dis,ui,_.trialint)};_.memberlevel=function(){var hd,ui,memberships,mo;memberships=["Basic","Silver","Gold","Platinum"];$(memberships).each(function(n,option){mo+='<option value="'+option+'">'+option+"</option>"});hd=$('<th><label for="membership-'+i+'"> '+MEMBERSHIP_LABEL+"</label></th>").appendTo(headingsRow);ui=$('<td><select name="'+fn+'[membership]" id="membership-'+i+'" class="membership">'+mo+"</select></td>").appendTo(inputsRow)};$.fn.toggler=function(s,ui,f){this.bind("change.value",function(){if(this.checked){s.hide();ui.show()}else{s.show();ui.hide()}if($.ua.msie){$(this).blur()}}).click(function(){if($.ua.msie){$(this).trigger("change.value")}if(this.checked){f.focus().select()}}).trigger("change.value");return $(this)};_.Shipped=function(data){_.price(data.price,data.tax);_.saleprice(data._sale,data.saleprice);_.shipping(data.shipping,data.dimensions,data.shipfee);if(!tmp){_.inventory(data.inventory,data.stock,data.sku)}};_.Virtual=function(data){_.price(data.price,data.tax);_.saleprice(data.sale,data.saleprice);if(!tmp){_.inventory(data.inventory,data.stock,data.sku)}};_.Download=function(data){_.price(data.price,data.tax);_.saleprice(data.sale,data.saleprice);if(!tmp){_.download(data.download)}};_.Donation=function(data){_.donation(data.price,data.tax,data.donation["var"],data.donation.min)};_.Subscription=function(data){_.price(data.price,data.tax);_.saleprice(data.sale,data.saleprice);_.recurring(data.recurring)};_.Membership=function(data){_.price(data.price,data.tax);_.saleprice(data.sale,data.saleprice);_.recurring();if(!tmp){_.memberlevel()}};type.bind("change.value",function(){headingsRow.empty();inputsRow.empty();var ui=type.val();if(ui=="Shipped"){_.Shipped(data)}if(ui=="Virtual"){_.Virtual(data)}if(ui=="Download"){_.Download(data)}if(ui=="Donation"){_.Donation(data)}if(ui=="Subscription"){_.Subscription(data)}if(ui=="Membership"){_.Membership(data)}inputsRow.find("input.money").bind("change.value",function(){this.value=asMoney(this.value)}).trigger("change.value");quickSelects(inputsRow)}).trigger("change.value");_.disable=function(){_.lasttype=(type.val())?type.val():false;type.val("N/A").trigger("change.value")};_.enable=function(){if(_.lasttype){type.val(_.lasttype).trigger("change.value")}};if(data&&data.context){context.val(data.context)}else{context.val("product")}_.setOptions=function(options){var update=false;if(options){if(options!=_.options){update=true}_.options=options}if(context.val()=="variation"){optionkey.val(xorkey(_.options))}if(update){_.updateLabel()}};_.updateKey=function(){optionkey.val(xorkey(_.options))};_.updateLabel=function(){var type=context.val(),string="",ids="";if(_.options){if(type=="variation"){$(_.options).each(function(index,id){if(string==""){string=$(productOptions[id]).val()}else{string+=", "+$(productOptions[id]).val()}if(ids==""){ids=id}else{ids+=","+id}})}if(type=="addon"){string=$(productAddons[_.options]).val();ids=_.options}}if(string==""){string=DEFAULT_PRICELINE_LABEL}_.label.val(htmlentities(string)).change();optionids.val(ids)};_.updateTabIndex=function(row){row=new Number(row);$.each(_.inputs,function(i,input){$(input).attr("tabindex",((row+1)*100)+i)})};_.linkInputs=function(option){_.links.push(option);$.each(_.inputs,function(i,input){if(!input){return}var type="change.linkedinputs",elem=$(input);if(elem.attr("type")=="checkbox"){type="click.linkedinputs"}$(input).bind(type,function(){var value=$(this).val(),checked=$(this).attr("checked");$.each(_.links,function(l,option){$.each(controller.linked[option],function(id,key){if(key==xorkey(_.options)){return}if(!controller.row[key]){return}if(elem.attr("type")=="checkbox"){$(controller.row[key].inputs[i]).attr("checked",checked)}else{$(controller.row[key].inputs[i]).val(value)}$(controller.row[key].inputs[i]).trigger("change.value")})})})})};_.unlinkInputs=function(option){if(option!==false){index=$.inArray(option,_.links);_.links.splice(index,1)}$.each(_.inputs,function(i,input){if(!input){return}var type="blur.linkedinputs";if($(input).attr("type")=="checkbox"){type="click.linkedinputs"}$(input).unbind(type)})};if(type.val()!="N/A"){_.inputs=new Array(type,_.p,_.t,_.spt,_.sp,_.dv,_.dm,_.st,_.w,_.dw,_.dl,_.dwd,_.dh,_.fee,_.it,_.stock,_.sku,_.period,_.interval,_.cycles,_.trialperiod,_.trialint,_.trialprice,_.trial)}_.updateKey();_.updateLabel()};
  • shopp/trunk/core/ui/categories/category.js

    r821385 r865739  
    5252    }).change();
    5353
    54     if (details) for (s in details) addDetail(details[s]);
     54    if (details) $.each(details, function(i, r) { addDetail(r); });
    5555    $('#addPriceLevel').click(function() { addPriceLevel(); });
    5656    $('#addDetail').click(function() { addDetail(); });
     
    7272    }).change();
    7373
    74     if (priceranges) for (key in priceranges) addPriceLevel(priceranges[key]);
     74    if (priceranges) $.each(priceranges, function(i, r) { addPriceLevel(r); });
    7575
    7676    if (!category) $('#title').focus();
     
    124124
    125125        // Load up existing options
    126         if (data && data.options) {
    127             for (var i in data.options) menu.addOption(data.options[i]);
    128         }
    129 
    130 
     126        if (data && data.options) $.each(data.options, function (i,r) { menu.addOption(r); });
    131127        $(menu.itemsElement).sortable({'axis':'y','items':'li.option','scroll':false});
    132128
  • shopp/trunk/core/ui/categories/category.min.js

    r821385 r865739  
    1 var Pricelines=new Pricelines(),productOptions=new Array(),optionMenus=new Array(),detailsidx=1,variationsidx=1,optionsidx=1,pricingidx=1,pricelevelsidx=1,fileUploader=false,changes=false,saving=false,flashUploader=false,template=true;jQuery(document).ready(function(){var $=jqnc(),editslug=new SlugEditor(category,"category"),imageUploads=new ImageUploads($("#image-category-id").val(),"category");postboxes.add_postbox_toggles("shopp_page_shopp-categories");$(".if-js-closed").removeClass("if-js-closed").addClass("closed");$(".postbox a.help").click(function(){$(this).colorbox({iframe:true,open:true,innerWidth:768,innerHeight:480,scrolling:false});return false});updateWorkflow();$("#category").submit(function(){this.action=this.action.substr(0,this.action.indexOf("?"))+"?"+$.param(request);return true});$("#templates, #details-template, #details-facetedmenu, #variations-template, #variations-pricing, #price-ranges").hide();$("#spectemplates-setting").change(function(){if(this.checked){$("#templates, #details-template").show()}else{$("#details-template").hide()}if(!$("#spectemplates-setting").attr("checked")&&!$("#variations-setting").attr("checked")){$("#templates").hide()}}).change();$("#faceted-setting").change(function(){if(this.checked){$("#details-menu").removeClass("options").addClass("menu");$("#details-facetedmenu, #price-ranges").show()}else{$("#details-menu").removeClass("menu").addClass("options");$("#details-facetedmenu, #price-ranges").hide()}}).change();if(details){for(s in details){addDetail(details[s])}}$("#addPriceLevel").click(function(){addPriceLevel()});$("#addDetail").click(function(){addDetail()});$("#variations-setting").bind("toggleui",function(){if(this.checked){$("#templates, #variations-template, #variations-pricing").show()}else{$("#variations-template, #variations-pricing").hide()}if(!$("#spectemplates-setting").attr("checked")&&!$("#variations-setting").attr("checked")){$("#templates").hide()}}).click(function(){$(this).trigger("toggleui")}).trigger("toggleui");if(options){loadVariations(!(options.v)?options:options.v,prices)}$("#addVariationMenu").click(function(){addVariationOptionsMenu()});$("#pricerange-facetedmenu").change(function(){if($(this).val()=="custom"){$("#pricerange-menu, #addPriceLevel").show()}else{$("#pricerange-menu, #addPriceLevel").hide()}}).change();if(priceranges){for(key in priceranges){addPriceLevel(priceranges[key])}}if(!category){$("#title").focus()}function addPriceLevel(data){var menus=$("#pricerange-menu");var id=pricelevelsidx++;var menu=new NestedMenu(id,menus,"priceranges","",data,false,{axis:"y",scroll:false});$(menu.label).change(function(){this.value=asMoney(this.value)}).change()}function addDetail(data){var menus=$("#details-menu"),entries=$("#details-list"),addOptionButton=$("#addDetailOption"),id=detailsidx,menu=new NestedMenu(id,menus,"specs",NEW_DETAIL_DEFAULT,data,{target:entries,type:"list"});menu.items=new Array();menu.addOption=function(data){var option=new NestedMenuOption(menu.index,menu.itemsElement,menu.dataname,NEW_OPTION_DEFAULT,data,true);menu.items.push(option)};var facetedSetting=$('<li class="setting"></li>').appendTo(menu.itemsElement);var facetedMenu=$('<select name="specs['+menu.index+'][facetedmenu]"></select>').appendTo(facetedSetting);$('<option value="disabled">'+FACETED_DISABLED+"</option>").appendTo(facetedMenu);$('<option value="auto">'+FACETED_AUTO+"</option>").appendTo(facetedMenu);$('<option value="ranges">'+FACETED_RANGES+"</option>").appendTo(facetedMenu);$('<option value="custom">'+FACETED_CUSTOM+"</option>").appendTo(facetedMenu);if(data&&data.facetedmenu){facetedMenu.val(data.facetedmenu)}facetedMenu.change(function(){if($(this).val()=="disabled"||$(this).val()=="auto"){$(addOptionButton).hide();$(menu.itemsElement).find("li.option").hide()}else{$(addOptionButton).show();$(menu.itemsElement).find("li.option").show()}}).change();if(data&&data.options){for(var i in data.options){menu.addOption(data.options[i])}}$(menu.itemsElement).sortable({axis:"y",items:"li.option",scroll:false});menu.element.unbind("click",menu.click);menu.element.click(function(){menu.selected();$(addOptionButton).unbind("click").click(menu.addOption);$(facetedMenu).change()});detailsidx++}function updateWorkflow(){$("#workflow").change(function(){setting=$(this).val();request.page=adminpage;request.id=category;if(!request.id){request.id="new"}if(setting=="new"){request.id="new";request.next=setting}if(setting=="close"){delete request.id}if(setting=="previous"){$.each(worklist,function(i,entry){if(entry.id!=category){return}if(worklist[i-1]){request.next=worklist[i-1].id}else{delete request.id}})}if(setting=="next"){$.each(worklist,function(i,entry){if(entry.id!=category){return}if(worklist[i+1]){request.next=worklist[i+1].id}else{delete request.id}})}}).change()}});
     1var Pricelines=new Pricelines(),productOptions=new Array(),optionMenus=new Array(),detailsidx=1,variationsidx=1,optionsidx=1,pricingidx=1,pricelevelsidx=1,fileUploader=false,changes=false,saving=false,flashUploader=false,template=true;jQuery(document).ready(function(){var $=jqnc(),editslug=new SlugEditor(category,"category"),imageUploads=new ImageUploads($("#image-category-id").val(),"category");postboxes.add_postbox_toggles("shopp_page_shopp-categories");$(".if-js-closed").removeClass("if-js-closed").addClass("closed");$(".postbox a.help").click(function(){$(this).colorbox({iframe:true,open:true,innerWidth:768,innerHeight:480,scrolling:false});return false});updateWorkflow();$("#category").submit(function(){this.action=this.action.substr(0,this.action.indexOf("?"))+"?"+$.param(request);return true});$("#templates, #details-template, #details-facetedmenu, #variations-template, #variations-pricing, #price-ranges").hide();$("#spectemplates-setting").change(function(){if(this.checked){$("#templates, #details-template").show()}else{$("#details-template").hide()}if(!$("#spectemplates-setting").attr("checked")&&!$("#variations-setting").attr("checked")){$("#templates").hide()}}).change();$("#faceted-setting").change(function(){if(this.checked){$("#details-menu").removeClass("options").addClass("menu");$("#details-facetedmenu, #price-ranges").show()}else{$("#details-menu").removeClass("menu").addClass("options");$("#details-facetedmenu, #price-ranges").hide()}}).change();if(details){$.each(details,function(i,r){addDetail(r)})}$("#addPriceLevel").click(function(){addPriceLevel()});$("#addDetail").click(function(){addDetail()});$("#variations-setting").bind("toggleui",function(){if(this.checked){$("#templates, #variations-template, #variations-pricing").show()}else{$("#variations-template, #variations-pricing").hide()}if(!$("#spectemplates-setting").attr("checked")&&!$("#variations-setting").attr("checked")){$("#templates").hide()}}).click(function(){$(this).trigger("toggleui")}).trigger("toggleui");if(options){loadVariations(!(options.v)?options:options.v,prices)}$("#addVariationMenu").click(function(){addVariationOptionsMenu()});$("#pricerange-facetedmenu").change(function(){if($(this).val()=="custom"){$("#pricerange-menu, #addPriceLevel").show()}else{$("#pricerange-menu, #addPriceLevel").hide()}}).change();if(priceranges){$.each(priceranges,function(i,r){addPriceLevel(r)})}if(!category){$("#title").focus()}function addPriceLevel(data){var menus=$("#pricerange-menu");var id=pricelevelsidx++;var menu=new NestedMenu(id,menus,"priceranges","",data,false,{axis:"y",scroll:false});$(menu.label).change(function(){this.value=asMoney(this.value)}).change()}function addDetail(data){var menus=$("#details-menu"),entries=$("#details-list"),addOptionButton=$("#addDetailOption"),id=detailsidx,menu=new NestedMenu(id,menus,"specs",NEW_DETAIL_DEFAULT,data,{target:entries,type:"list"});menu.items=new Array();menu.addOption=function(data){var option=new NestedMenuOption(menu.index,menu.itemsElement,menu.dataname,NEW_OPTION_DEFAULT,data,true);menu.items.push(option)};var facetedSetting=$('<li class="setting"></li>').appendTo(menu.itemsElement);var facetedMenu=$('<select name="specs['+menu.index+'][facetedmenu]"></select>').appendTo(facetedSetting);$('<option value="disabled">'+FACETED_DISABLED+"</option>").appendTo(facetedMenu);$('<option value="auto">'+FACETED_AUTO+"</option>").appendTo(facetedMenu);$('<option value="ranges">'+FACETED_RANGES+"</option>").appendTo(facetedMenu);$('<option value="custom">'+FACETED_CUSTOM+"</option>").appendTo(facetedMenu);if(data&&data.facetedmenu){facetedMenu.val(data.facetedmenu)}facetedMenu.change(function(){if($(this).val()=="disabled"||$(this).val()=="auto"){$(addOptionButton).hide();$(menu.itemsElement).find("li.option").hide()}else{$(addOptionButton).show();$(menu.itemsElement).find("li.option").show()}}).change();if(data&&data.options){$.each(data.options,function(i,r){menu.addOption(r)})}$(menu.itemsElement).sortable({axis:"y",items:"li.option",scroll:false});menu.element.unbind("click",menu.click);menu.element.click(function(){menu.selected();$(addOptionButton).unbind("click").click(menu.addOption);$(facetedMenu).change()});detailsidx++}function updateWorkflow(){$("#workflow").change(function(){setting=$(this).val();request.page=adminpage;request.id=category;if(!request.id){request.id="new"}if(setting=="new"){request.id="new";request.next=setting}if(setting=="close"){delete request.id}if(setting=="previous"){$.each(worklist,function(i,entry){if(entry.id!=category){return}if(worklist[i-1]){request.next=worklist[i-1].id}else{delete request.id}})}if(setting=="next"){$.each(worklist,function(i,entry){if(entry.id!=category){return}if(worklist[i+1]){request.next=worklist[i+1].id}else{delete request.id}})}}).change()}});
  • shopp/trunk/core/ui/orders/order.php

    r831831 r865739  
    2121                        <?php endif; ?>
    2222
    23                         <?php if ($Purchase->ispaid()): ?>
     23                        <?php if ( $Purchase->ispaid() && ! $Purchase->isvoid() ): ?>
    2424                        <div class="stamp paid"><div class="type"><?php _e('Paid','Shopp'); ?></div><div class="ing">&nbsp;</div></div>
    2525                        <?php elseif ($Purchase->isvoid()): ?>
     
    114114                        <ul class="promos">
    115115                        <?php foreach ( $Purchase->discounts as $id => $Discount ): ?>
    116                             <li><small><a href="<?php echo esc_url( add_query_arg(array('page' => $this->Admin->pagename('discounts'), 'id' => $id), admin_url('admin.php'))); ?>"><?php echo esc_html($Discount->name); ?></a></small></li>
     116                            <li><small><a href="<?php echo esc_url( add_query_arg(array('page' => $this->Admin->pagename('discounts'), 'id' => $id), admin_url('admin.php'))); ?>"><?php echo esc_html($Discount->name); ?></a><?php if ( isset($Discount->code) ) echo " - " . esc_html($Discount->code); ?></small></li>
    117117                        <?php endforeach; ?>
    118118                        </ul>
     
    187187                                                    $Product->load_data( array('images') );
    188188                                                    $Image = reset($Product->images);
     189                                                    $image_id = apply_filters('shopp_order_item_image_id', $Image->id, $Item, $Product);
    189190
    190191                                                    if ( ! empty($Image) ) { ?>
    191                                                         <img src="?siid=<?php echo $Image->id ?>&amp;<?php echo $Image->resizing(38, 0, 1) ?>" width="38" height="38" class="alignleft" />
     192                                                        <img src="?siid=<?php echo $image_id ?>&amp;<?php echo $Image->resizing(38, 0, 1) ?>" width="38" height="38" class="alignleft" />
    192193                                                    <?php
    193194                                                    }
     
    251252                                        ?>
    252253                                            <td class="<?php echo esc_attr(join(' ',$classes)); ?>">
    253                                             <?php do_action( 'shopp_manage_order_'.$column.'_column_data', $column, $Product ); ?>
     254                                            <?php do_action( 'shopp_manage_order_' . sanitize_key($column) .'_column_data', $column, $Product, $Item, $Purchase ); ?>
    254255                                            </td>
    255256                                        <?php
  • shopp/trunk/core/ui/orders/orders.php

    r831831 r865739  
    3636            <button type="submit" id="update-button" name="update" value="order" class="button-secondary"><?php _e('Update','Shopp'); ?></button>
    3737        </div>
    38        
     38
    3939        <div class="alignleft actions filtering">
    4040                <select name="range" id="range">
     
    6363        <tbody id="orders-table" class="list orders">
    6464        <?php
     65            $columns = get_column_headers(ShoppAdmin()->screen());
    6566            $hidden = get_hidden_columns(ShoppAdmin()->screen());
    6667
     
    9495            do_action_ref_array('shopp_order_row_css',array(&$classes,&$Order));
    9596            $even = !$even;
    96             ?>
     97        ?>
    9798        <tr class="<?php echo join(' ',$classes); ?>">
    98             <th scope='row' class='check-column'><input type='checkbox' name='selected[]' value='<?php echo $Order->id; ?>' /></th>
    99             <td class="order column-order<?php echo in_array('order',$hidden)?' hidden':''; ?>"><a class='row-title' href='<?php echo esc_url($viewurl); ?>' title='<?php printf(__('View Order #%d','Shopp'),$Order->id); ?>'><?php printf(__('Order #%d','Shopp'),$Order->id); ?></a></td>
    100             <td class="name column-name"><a href="<?php echo esc_url($customerurl); ?>"><?php echo esc_html($customer); ?></a><?php echo !empty($Order->company)?"<br />".esc_html($Order->company):""; ?></td>
    101             <td class="destination column-destination<?php echo in_array('destination',$hidden)?' hidden':''; ?>"><?php echo esc_html($location); ?></td>
    102             <td class="txn column-txn<?php echo in_array('txn',$hidden)?' hidden':''; ?>"><?php echo $Order->txnid; ?><br /><?php echo esc_html($gateway); ?></td>
    103             <td class="date column-date<?php echo in_array('date',$hidden)?' hidden':''; ?>"><?php echo date("Y/m/d",mktimestamp($Order->created)); ?><br />
    104                 <strong><?php echo $statusLabels[$Order->status]; ?></strong></td>
    105             <td class="total column-total<?php echo in_array('total',$hidden)?' hidden':''; ?>"><?php echo money($Order->total); ?><br /><span class="status"><?php echo $txnstatus; ?></span></td>
     99        <?php
     100            foreach ( $columns as $column => $column_title ) {
     101                $classes = array($column,"column-$column");
     102                if ( in_array($column, $hidden) ) $classes[] = 'hidden';
     103
     104                switch ( $column ) {
     105                    case 'cb':
     106                    ?>
     107                        <th scope='row' class='check-column'><input type='checkbox' name='selected[]' value='<?php echo esc_attr($Order->id); ?>' /></th>
     108                    <?php
     109                    break;
     110
     111                    case 'order':
     112                    ?>
     113                        <td class="<?php echo esc_attr(join(' ', $classes)); ?>">
     114                            <a class='row-title' href='<?php echo esc_url($viewurl); ?>' title='<?php printf(__('View Order #%d','Shopp'),$Order->id); ?>'><?php printf(__('Order #%d','Shopp'),$Order->id); ?></a>
     115                        </td>
     116                    <?php
     117                    break;
     118
     119                    case 'name':
     120                    ?>
     121                        <td class="<?php echo esc_attr(join(' ', $classes)); ?>">
     122                            <a href="<?php echo esc_url($customerurl); ?>"><?php echo esc_html($customer); ?></a><?php echo !empty($Order->company)?"<br />".esc_html($Order->company):""; ?>
     123                        </td>
     124                    <?php
     125                    break;
     126
     127                    case 'destination':
     128                    ?>
     129                        <td class="<?php echo esc_attr(join(' ', $classes)); ?>">
     130                            <?php echo esc_html($location); ?>
     131                        </td>
     132                    <?php
     133                    break;
     134
     135                    case 'txn':
     136                    ?>
     137                        <td class="<?php echo esc_attr(join(' ', $classes)); ?>">
     138                            <?php echo $Order->txnid; ?><br /><?php echo esc_html($gateway); ?>
     139                        </td>
     140                    <?php
     141                    break;
     142
     143                    case 'date':
     144                    ?>
     145                        <td class="<?php echo esc_attr(join(' ', $classes)); ?>">
     146                            <?php echo date("Y/m/d", mktimestamp($Order->created)); ?><br />
     147                            <strong><?php echo $statusLabels[$Order->status]; ?></strong>
     148                        </td>
     149                    <?php
     150                    break;
     151
     152                    case 'total':
     153                    ?>
     154                        <td class="<?php echo esc_attr(join(' ', $classes)); ?>">
     155                            <?php echo money($Order->total); ?><br /><span class="status"><?php echo $txnstatus; ?></span>
     156                        </td>
     157                    <?php
     158                    break;
     159
     160                    default:
     161                    ?>
     162                        <td class="<?php echo esc_attr(join(' ', $classes)); ?>">
     163                            <?php do_action( 'shopp_manage_orders_custom_column', $column, $Order ); ?>
     164                            <?php do_action( 'shopp_manage_orders_' . santitize_key($column) . '_column', $column, $Order ); ?>
     165                        </td>
     166                    <?php
     167                    break;
     168
     169
     170                } // end switch ( $column )
     171
     172            } // end foreach ( $columns…)
     173        ?>
    106174        </tr>
    107175        <?php endforeach; ?>
  • shopp/trunk/core/ui/reports/products.php

    r831831 r865739  
    4242        $query = "SELECT CONCAT($id) AS id,
    4343                            CONCAT(p.post_title,' ',pr.label) AS product,
    44                             COUNT(DISTINCT o.id) AS sold,
     44                            SUM(o.quantity) AS sold,
    4545                            COUNT(DISTINCT o.purchase) AS orders,
    4646                            SUM(o.total) AS grossed
  • shopp/trunk/core/ui/reports/reports.php

    r821385 r865739  
    2121        </div>
    2222
    23         <?php $ListTable->pagination('top'); ?>
     23        <?php $ListTable->pagination( 'top' ); ?>
    2424
    2525        <div class="clear"></div>
  • shopp/trunk/core/ui/settings/setup.php

    r821385 r865739  
    7676            <tr>
    7777                <th scope="row" valign="top"><label for="maintenance-toggle"><?php _e('Maintenance Mode','Shopp'); ?></label></th>
    78                 <td><input type="hidden" name="settings[maintenance]" value="off" /><input type="checkbox" name="settings[maintenance]" value="on" id="maintenance-toggle"<?php if ( shopp_setting_enabled('maintenance') ) echo ' checked="checked"'?> /><label for="maintenance-toggle"> <?php _e('Enable mainteance mode','Shopp'); ?></label><br />
     78                <td><input type="hidden" name="settings[maintenance]" value="off" /><input type="checkbox" name="settings[maintenance]" value="on" id="maintenance-toggle"<?php if ( shopp_setting_enabled('maintenance') ) echo ' checked="checked"'?> /><label for="maintenance-toggle"> <?php _e('Enable maintenance mode','Shopp'); ?></label><br />
    7979                <?php _e('All storefront pages will display a maintenance mode message.','Shopp'); ?></td>
    8080            </tr>
  • shopp/trunk/core/ui/styles/admin.css

    r831831 r865739  
    312312#lightbox div.progress { margin:40px 0 0 10px; }
    313313
    314 .shopp .notice {-webkit-border-radius:3px;border-radius:3px;border:1px solid #e6db55;background-color:#ffffe0;margin: 5px 0 15px;padding: 0 0.6em; }
    315 .shopp .notice p { margin:1em 0; }
     314#shopp-activation-nag { width:100%; box-sizing:border-box;}
     315#shopp-activation-nag .dismiss { color:#ccc; margin-right:8px; }
     316#shopp-activation-nag .dismiss:hover { color:#aaa; }
     317#shopp-activation-nag .button { float: left; margin: 8px 20px 0 0; button }
     318#shopp-activation-nag big { display:block; }
    316319
    317320#wpcontent .form-table select.scrolling { height:auto; }
     
    507510
    508511.orders.list .status { font-weight: bold; }
     512.orders.list .refunded .order,
     513.orders.list .refunded .total,
    509514.orders.list .voided .order,
    510515.orders.list .voided .total { text-decoration: line-through; }
  • shopp/trunk/core/ui/styles/dashboard.css

    r821385 r865739  
    2929#dashboard_shopp_inventory span.lowstock.warning { background: #C60; }
    3030#dashboard_shopp_inventory span.lowstock.backorder { background: #C00; }
     31
     32.orders.list .refunded td { text-decoration: line-through; }
  • shopp/trunk/gateways/PayPal/PayPalStandard.php

    r831831 r865739  
    66 * @copyright Ingenesis Limited, May 2009
    77 * @package shopp
    8  * @version 1.3
     8 * @version 1.3.2
    99 * @since 1.2
    1010 **/
     
    5353        // add_filter('shopp_themeapi_cart_paypal', array($this, 'sendcart'), 10, 2); // provides shopp('cart.paypal') checkout button
    5454        add_filter( 'shopp_checkout_submit_button', array($this, 'submit'), 10, 3 ); // replace submit button with paypal image
     55
     56        // Prevent inclusive taxes from adding extra taxes to the order
     57        add_filter( 'shopp_gateway_tax_amount', array($this, 'notaxinclusive' ) );
    5558
    5659        // request handlers
     
    347350        $title = Shopp::__( 'Sending order to PayPal&hellip;' );
    348351        $message = '<form id="' . $id . '" action="' . $this->url() . '" method="POST">' .
    349             $this->form( $Purchase ) .
    350             '<h1>' . $title . '</h1>' .
    351             '<noscript>' .
    352             '<p>' . Shopp::__( 'Click the &quot;Checkout with PayPal&quot; button below to submit your order to PayPal for payment processing:' ) . '</p>' .
    353             '<p>' . join( '', $this->submit() ) . '</p>' .
    354             '</noscript>' .
    355             '</form>' .
    356             '<script type="text/javascript">document.getElementById("' . $id . '").submit();</script></body></html>';
     352                    $this->form( $Purchase ) .
     353                    '<h1>' . $title . '</h1>' .
     354                    '<noscript>' .
     355                    '<p>' . Shopp::__( 'Click the &quot;Checkout with PayPal&quot; button below to submit your order to PayPal for payment processing:' ) . '</p>' .
     356                    '<p>' . join( '', $this->submit() ) . '</p>' .
     357                    '</noscript>' .
     358                    '</form>' .
     359                    '<script type="text/javascript">document.getElementById("' . $id . '").submit();</script></body></html>';
    357360
    358361        wp_die( $message, $title, array( 'response' => 200 ) );
     
    502505                $_['item_number_'.$id]      = $id;
    503506                $_['item_name_'.$id]        = apply_filters('paypal_freeorder_handling_label',
    504                     __('Shipping & Handling','Shopp'));
     507                                                            __('Shipping & Handling','Shopp'));
    505508                $_['amount_'.$id]           = $this->amount( max((float)$this->amount('shipping'), 0.01) );
    506509                $_['quantity_'.$id]         = 1;
     
    534537                $this->sale($Purchase);
    535538            elseif ( 'capture' === $event ) {
    536                 if ( ! $Purchase->capturable() ) return false; // Already captured
     539
     540                if ( ! $Purchase->capturable() )
     541                    return ShoppOrder()->success(); // Already captured
     542
    537543                if ( 'voided' === $Purchase->txnstatus )
    538544                    ShoppOrder()->invoice($Purchase); // Reinvoice for cancel-reversals
     
    695701     **/
    696702    public function pdt () {
    697 
    698         if ( empty(ShoppOrder()->inprogress) ) {
    699             shopp_debug('PDT processing could not load in-progress order from session.');
    700             return Shopp::redirect( Shopp::url( false, 'thanks', false ) ); // Send back customer to thanks page and hope IPN takes care of it properly
    701         }
     703        $Order = ShoppOrder();
    702704
    703705        if ( ! $this->pdtvalid() ) return;
     
    708710        $event = $Message->event();
    709711
    710         if ( ShoppOrder()->inprogress != $id ) {
    711             shopp_debug('PDT processing order did not match the inprogress order.');
    712             return Shopp::redirect( Shopp::url( false, 'thanks', false ) );
    713         }
    714 
    715712        $Purchase = new ShoppPurchase($id);
    716713
    717         if ( ! isset($Purchase->id) || empty($Purchase->id) ) {
     714        if ( empty($Purchase->id) ) {
    718715            shopp_debug('PDT processing could not load the in progress order from the database.');
    719716            return Shopp::redirect( Shopp::url(false, 'thanks', false ) );
    720717        }
    721718
     719        $Order->inprogress = $Purchase->id;
    722720        $this->process($event, $Purchase);
    723721        Shopp::redirect( Shopp::url( false, 'thanks', false ) );
     
    933931
    934932        $this->labels(); // Initialize labels
    935 
    936933    }
    937934
  • shopp/trunk/license.txt

    r821385 r865739  
    1 Table of Contents
     1# Table of Contents
    22=====================================================================
    33Section I   : Credits and Copyrights
    44Section II  : GNU General Public License
    55Section III : BSD License (see Credits)
    6 Section IV  : THE EMOGRIFIER LICENSE
    7 
    8 Section I   : Credits
    9 =====================================================================
     6Section IV : THE EMOGRIFIER LICENSE
     7=====================================================================
     8## Section I: Credits
     9
    1010Code contributors include (but not limited to):
    1111Jonathan Davis - Ingenesis Limited
     
    1313Marc Neuhaus
    1414Timothy Hatcher
    15 Tom Wu (see BSD License in Section III for license to this author's work)
    16 Pelago, pelagodesign.com (see THE EMOGRIFIER LICENSE, Section IV)
     15Barry Hughes
     16Clifton Griffin
     17Ben Andersen
     18Lorenzo Caum
     19Tom Wu (see Section III: BSD License)
     20Pelago, pelagodesign.com (see Section IV: THE EMOGRIFIER LICENSE)
    1721
    1822Copyrights:
     
    2226source code files.
    2327
    24 Section II  : GNU General Public License
    25 =====================================================================
     28=====================================================================
     29
     30## Section II: GNU General Public License
     31
    2632                    GNU GENERAL PUBLIC LICENSE
    2733                       Version 3, 29 June 2007
     
    702708=====================================================================
    703709
    704 Section III : BSD License (see Credits)
    705 =====================================================================
    706 * Permission is hereby granted, free of charge, to any person obtaining
    707 * a copy of this software and associated documentation files (the
    708 * "Software"), to deal in the Software without restriction, including
    709 * without limitation the rights to use, copy, modify, merge, publish,
    710 * distribute, sublicense, and/or sell copies of the Software, and to
    711 * permit persons to whom the Software is furnished to do so, subject to
    712 * the following conditions:
    713 *
    714 * The above copyright notice and this permission notice shall be
    715 * included in all copies or substantial portions of the Software.
    716 *
    717 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
    718 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
    719 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
    720 *
    721 * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
    722 * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
    723 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
    724 * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
    725 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    726 *
    727 * In addition, the following condition applies:
    728 *
    729 * All redistributions must retain an intact copy of this copyright notice
    730 * and disclaimer.
    731 *
     710## Section III: BSD License
     711
     712(see Section I: Credits)
     713
     714Permission is hereby granted, free of charge, to any person obtaining
     715a copy of this software and associated documentation files (the
     716"Software"), to deal in the Software without restriction, including
     717without limitation the rights to use, copy, modify, merge, publish,
     718distribute, sublicense, and/or sell copies of the Software, and to
     719permit persons to whom the Software is furnished to do so, subject to
     720the following conditions:
     721
     722The above copyright notice and this permission notice shall be
     723included in all copies or substantial portions of the Software.
     724
     725THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
     726EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
     727WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
     728
     729IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
     730INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
     731RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
     732THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
     733OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     734
     735In addition, the following condition applies:
     736
     737All redistributions must retain an intact copy of this copyright notice
     738and disclaimer.
     739
    732740Address all questions regarding this license to:
    733741
     
    739747=====================================================================
    740748
    741 Section IV : THE EMOGRIFIER LICENSE (see Credits)
    742 =====================================================================
     749## Section IV: THE EMOGRIFIER LICENSE
     750
     751(see Section I: Credits)
    743752
    744753Emogrifier is provided under the terms of the MIT license:
  • shopp/trunk/readme.txt

    r821385 r865739  
    2121Run sales, add new products, update inventory, ship orders. It’s all there, and it’s easy. Shopp consistently wins accolades for it’s WordPress-native administration tools giving it the most natural management experience you’ll find in a WordPress e-commerce solution. Get in, do your thing and get on with your real life.
    2222
    23 E-commerce software shouldn’t tell you how to manage your business. You should be able tell your e-commerce software how you manage your business. Shopp lets you do just that. Create your own order processing labels and easily move orders through each step of the order fullfillment workflow either automatically or take control of it yourself.
     23E-commerce software shouldn’t tell you how to manage your business. You should be able tell your e-commerce software how you manage your business. Shopp lets you do just that. Create your own order processing labels and easily move orders through each step of the order fulfillment workflow either automatically or take control of it yourself.
    2424
    2525Export orders to accounting systems like Intuit® QuickBooks®, or to spreadsheets like Microsoft® Excel®. Shopp remembers your last export date so its easy to do ongoing exports on a regular basis.
     
    2727= Safe & Sound =
    2828
    29 Your website will be in good -hands- bytes running on Shopp. Our comprehensive security builds on the solid toolkit of WordPress to let the good guys in and keep bad guys out. We use all the tricks of the trade plus a few ideas of our own to protect your store and your customers.
     29Your website will be in good hands (or bytes) running on Shopp. Our comprehensive security builds on the solid toolkit of WordPress to let the good guys in and keep bad guys out. We use all the tricks of the trade plus a few ideas of our own to protect your store and your customers.
    3030
    3131Shopp is so secure it passes PCI vulnerability scans every day and we can prove it! [See the McAfee SECURE seal on our website](https://www.mcafeesecure.com/RatingVerify?ref=shopplugin.net).
     
    86866. Click on any of the Shopp menus to start setting up your new Shopp store!
    8787
    88 At a minimum you should setup a **Base of Operations** location to setup Shopp to work for your location. For details see our [Getting Started Guide](https://shopplugin.comt/docs/getting-started/)
     88At a minimum you should setup a **Base of Operations** location to setup Shopp to work for your location. For details see our [Getting Started Guide](https://shopplugin.com/docs/getting-started/)
    8989
    9090== Changelog ==
    9191
    92 = 1.0 =
     92= 1.3 =
    9393
    94 * Initial release
    95 * Content templates
    96 * Shortcodes
    97 * Widgets
    98 * Product Variants editor
    99 * PayPal Standard
    100 
    101 = 1.1 =
    102 
    103 * Product addons
    104 * Storefront Search Engine
    105 * Tax system improvements
    106 * Image & Script Servers
    107 * Scriptabe email templates
    108 * Cart Item discounts
    109 * Passes PCI scans
    110 * Offline Payments
    111 * 2Checkout and Google Checkout included
    112 * New Theme API tags
     94* Added reports with charting and exports
     95* Relabeled Promotions to Discounts
     96* Added icon font and other vector art
     97* Implemented PHP smart loading
     98* Improved checkout experience and templates
     99* Added schema.org support
     100* Refactored classes for better encapsulation and tidier interfaces
     101* Added direct URL support to storage engines
     102* Introduced support for `wp-content/shopp-addons/` directory
     103* Improved tax and discount calculations
     104* Added compound tax support
     105* Implemented totals register system
     106* Improved session handling
     107* Fixed slow queries
     108* Improved order management
     109* Redesigned unit tests
     110* implemented continuous integration developement
    113111
    114112= 1.2 =
     
    132130* Implemented unit tests
    133131
    134 = 1.3 =
     132= 1.1 =
    135133
    136 * Added reports with charting and exports
    137 * Relabeled Promotions to Discounts
    138 * Added icon font and other vector art
    139 * Implemented PHP smart loading
    140 * Improved checkout experience and templates
    141 * Added schema.org support
    142 * Refactored classes for better encapsulation and tidier interfaces
    143 * Added direct URL support to storage engines
    144 * Introduced support for `wp-content/shopp-addons/` directory
    145 * Improved tax and discount calculations
    146 * Added compound tax support
    147 * Implemented totals register system
    148 * Improved session handling
    149 * Fixed slow queries
    150 * Improved order management
    151 * Redesigned unit tests
    152 * implemented continuous integration developement
     134* Product addons
     135* Storefront Search Engine
     136* Tax system improvements
     137* Image & Script Servers
     138* Scriptabe email templates
     139* Cart Item discounts
     140* Passes PCI scans
     141* Offline Payments
     142* 2Checkout and Google Checkout included
     143* New Theme API tags
     144
     145= 1.0 =
     146
     147* Initial release
     148* Content templates
     149* Shortcodes
     150* Widgets
     151* Product Variants editor
     152* PayPal Standard
     153
    153154
    154155== Frequently Asked Questions ==
     
    160161= Where can I see examples of Shopp in action? =
    161162
    162 The [Showcase](https://shopplugin.com/showcase) has lots of live storefronts running Shopp.
     163The [Showcase](https://shopplugin.net/blog/category/showcase/) has lots of live storefronts running Shopp.
    163164
    164165= Do you have a product importer? =
  • shopp/trunk/services/image.php

    r831831 r865739  
    246246
    247247        // Output the image
    248         ob_clean(); // try to catch errant data in buffer
     248        @ob_clean(); // try to catch errant data in buffer
    249249        $this->Image->output($headers);
    250250    }
  • shopp/trunk/storage/core/FSStorage.php

    r821385 r865739  
    4242        parent::__construct();
    4343        $this->name = __('File system','Shopp');
    44         $this->webroot = apply_filters('shopp_fsstorage_webroot', trim(ABSPATH, '/'));
     44        $this->webroot = apply_filters('shopp_fsstorage_webroot', $_SERVER['DOCUMENT_ROOT']);
    4545    }
    4646
     
    281281     * @return mixed bool | string
    282282     */
    283     public function direct( $uri ) {
     283    public function direct ( $uri ) {
    284284        if ( defined('SHOPP_DIRECT_ASSET_URLS') && ! SHOPP_DIRECT_ASSET_URLS ) return false;
    285285        $path = $this->finddirect($uri);
     
    298298     * @return string | bool
    299299     */
    300     protected function finddirect($uri) {
    301         $wpurl = get_option('siteurl');
    302         $webroot = explode('/', $this->webroot);
     300    protected function finddirect ( $uri ) {
     301        // The web server's document root
     302        $webroot = $this->webroot;
     303
     304        // The base directory of WordPress
     305        $wpdir = apply_filters( 'shopp_fsstorage_homedir', ABSPATH );
     306
     307        // The base URL (home URL) of the site
     308        $baseurl = untrailingslashit( apply_filters( 'shopp_fsstorage_homeurl', get_option( 'home' ) ) );
    303309
    304310        // Obtain the storage path or bail out early if it is not accessible/is invalid
    305311        if ( ! ( $storagepath = $this->storagepath() ) ) return false;
    306         else $storagedir = explode('/', trim($storagepath, '/') );
    307312
    308313        // Ensure the file exists
    309         if ( ! file_exists($storagepath . "/$uri" )) return false;
     314        if ( ! file_exists( $storagepath . "/$uri" ) ) return false;
    310315
    311316        // Determine if the storage path is inside the webroot (they should have the same initial set of "segments")
    312         for ($segment = 0; $segment < count($webroot); $segment++)
    313             if ($webroot[$segment] !== $storagedir[$segment]) return false;
     317        if ( 0 != strpos( $storagepath, $webroot ) ) return false;
     318
     319        // Is WordPress installed and accessed from within a subdirectory of webroot? Adjust $baseurl to remove the subdir if so
     320        if ( $webroot !== $wpdir && false !== strpos($wpdir, $webroot) ) {
     321            $path = untrailingslashit( str_replace( $webroot, '', $wpdir ) );
     322            $inpath = strrpos( $baseurl, $path );
     323            if ( $inpath ) $baseurl = substr( $baseurl, 0, $inpath );
     324        }
     325
     326        // Determine if the storage path is under the WordPress directory and if so use it as the canonical webroot
     327        if ( false !== strpos($storagepath, $wpdir) && false === strpos($storagepath, $webroot) )
     328            $webroot = $wpdir;
    314329
    315330        // Supposing the image directory isn't the WP root, append the trailing component
    316         if (count($storagedir) > count($this->webroot)) {
    317             $trailing_component = join('/', array_slice($storagedir, count($webroot)));
    318             $public_url = trailingslashit($wpurl) . $trailing_component;
    319         }
    320         else $public_url = $wpurl;
    321 
    322         return trailingslashit($public_url) . $uri;
     331        if ( $storagepath !== $webroot ) {
     332            $path = str_replace( trailingslashit( $webroot ), '', $storagepath );
     333            $url = trailingslashit( $baseurl ) . $path;
     334        } else $url = $baseurl;
     335
     336        return trailingslashit( $url ) . $uri;
    323337    }
    324338
  • shopp/trunk/templates/email-order.php

    r821385 r865739  
    1212        <?php shopp( 'purchase.receipt' ); ?>
    1313        <?php if ( shopp( 'purchase.notpaid' ) && shopp( 'checkout.get-offline-instructions' ) ) : ?>
    14             <p><?php shopp( 'checkout.offline-instructions' ); ?></p>
     14            <?php shopp( 'checkout.offline-instructions' ); ?>
    1515        <?php endif; ?>
    1616    </div>
  • shopp/trunk/templates/sidecart.php

    r821385 r865739  
    2323        </ul>
    2424    <?php else : ?>
    25         <p class="notice"><?php _e( 'Your cart is empty.', 'Shopp' ); ?></p>
     25        <p class="status notice"><?php _e( 'Your cart is empty.', 'Shopp' ); ?></p>
    2626    <?php endif; ?>
    2727</div>
  • shopp/trunk/templates/thanks.php

    r821385 r865739  
    2020
    2121        <?php if ( shopp( 'checkout.get-offline-instructions' ) ) : ?>
    22             <p><?php shopp( 'checkout.offline-instructions' ); ?></p>
     22            <?php shopp( 'checkout.offline-instructions' ); ?>
    2323        <?php endif; ?>
    2424
Note: See TracChangeset for help on using the changeset viewer.