_table = DatabaseObject::tablename('cart'); // Close out any early session calls if(session_id()) session_write_close(); $this->handlers = session_set_save_handler( array( &$this, 'open' ), // Open array( &$this, 'close' ), // Close array( &$this, 'load' ), // Read array( &$this, 'save' ), // Write array( &$this, 'unload' ), // Destroy array( &$this, 'trash' ) // Garbage Collection ); register_shutdown_function('session_write_close'); define('SHOPP_SECURE_KEY','shopp_sec_'.COOKIEHASH); $this->data = new stdClass(); // Session data $this->data->Totals = new stdClass(); // Cart totals $this->data->Totals->subtotal = 0; // Subtotal of item totals $this->data->Totals->quantity = 0; // Total quantity of all items $this->data->Totals->discount = 0; // Total discount applied $this->data->Totals->shipping = 0; // Total shipping cost $this->data->Totals->tax = 0; // Total tax cost $this->data->Totals->taxrate = 0; // Current tax rate $this->data->Totals->total = 0; // Grand total $this->data->Order = new stdClass(); // Order object $this->data->Order->data = array(); // Custom order data registry $this->data->Order->Customer = false; // Order's customer record $this->data->Order->Billing = false; // Order's billing address record $this->data->Order->Shipping = false; // Order's shipping address record $this->data->added = 0; // Recently added item index $this->data->login = false; // Customer logged in flag $this->data->secure = false; // Security flag $this->data->Errors = new ShoppErrors(); // Tracks errors $this->data->Shipping = false; // Cart has shipped items $this->data->ShippingDisabled = false; // Shipping is disabled $this->data->Estimates = false; // Order needs shipping estimates $this->data->Promotions = array(); // Promotions available (cache) $this->data->PromosApplied = array(); // Promotions applied to order $this->data->PromoCode = false; // Recent promo code attempt $this->data->PromoCodes = array(); // Promo codes applied $this->data->PromoCodeResult = false; // Result of recent promo code attempt $this->data->ShipCosts = array(); // Shipping method costs $this->data->ShippingPostcode = false; // Shipping calcs require postcode $this->data->ShippingPostcodeError = false; // Postal code invalid error $this->data->Purchase = false; // Final purchase receipt $this->data->Category = array(); // Session related category settings $this->data->Search = false; // Search processed // Total the cart once, and only if there are changes add_action('parse_request',array(&$this,'totals'),99); } /* open() * Initializing routine for the session management. */ function open ($path,$name) { $this->path = $path; if (empty($this->path)) $this->path = dirname(realpath(tempnam('','tmp_'))); $this->trash(); // Clear out any residual session information before loading new data if (empty($this->session)) $this->session = session_id(); // Grab our session id $this->ip = $_SERVER['REMOTE_ADDR']; // Save the IP address making the request return true; } /* close() * Placeholder function as we are working with a persistant * database as opposed to file handlers. */ function close () { return true; } /* load() * Gets data from the session data table and loads Member * objects into the User from the loaded data. */ function load ($id) { global $Shopp; $db = DB::get(); if (is_robot()) return true; $query = "SELECT * FROM $this->_table WHERE session='$this->session'"; // echo "$query".BR; if ($result = $db->query($query)) { if (substr($result->data,0,1) == "!") { $key = $_COOKIE[SHOPP_SECURE_KEY]; $readable = $db->query("SELECT AES_DECRYPT('". mysql_real_escape_string( base64_decode( substr($result->data,1) ) )."','$key') AS data"); $result->data = $readable->data; } $this->ip = $result->ip; $this->data = unserialize($result->data); if (empty($result->contents)) $this->contents = array(); else $this->contents = unserialize($result->contents); $this->created = mktimestamp($result->created); $this->modified = mktimestamp($result->modified); } else { $db->query("INSERT INTO $this->_table (session, ip, data, contents, created, modified) VALUES ('$this->session','$this->ip','','',now(),now())"); } if (empty($this->data->Errors)) $this->data->Errors = new ShoppErrors(); if ($Shopp->Settings->get('shipping') == "off") $this->data->ShippingDisabled = true; // Read standard session data if (file_exists("$this->path/sess_$id")) return (string) file_get_contents("$this->path/sess_$id"); return true; } /* unload() * Deletes the session data from the database, unregisters the * session and releases all the objects. */ function unload () { $db = DB::get(); if (!$db->query("DELETE FROM $this->_table WHERE session='$this->session'")) trigger_error("Could not clear session data."); unset($this->session,$this->ip,$this->data,$this->contents); return true; } /* save() * Save the session data to our session table in the database. */ function save ($id,$session) { global $Shopp; $db = DB::get(); if (!$Shopp->Settings->unavailable) { $data = $db->escape(addslashes(serialize($this->data))); $contents = $db->escape(serialize($this->contents)); if ($this->secured() && is_shopp_secure()) { if (!isset($_COOKIE[SHOPP_SECURE_KEY])) $key = $this->securekey(); else $key = isset($_COOKIE[SHOPP_SECURE_KEY])?$_COOKIE[SHOPP_SECURE_KEY]:''; if (!empty($key)) { $secure = $db->query("SELECT AES_ENCRYPT('$data','$key') AS data"); $data = "!".base64_encode($secure->data); } } $query = "UPDATE $this->_table SET ip='$this->ip',data='$data',contents='$contents',modified=now() WHERE session='$this->session'"; if (!$db->query($query)) trigger_error("Could not save session updates to the database."); } // Save standard session data for compatibility if (!empty($session)) { if ($sf = fopen("$this->path/sess_$id","w")) { $result = fwrite($sf, $session); fclose($sf); return $result; } return false; } return true; } /* trash() * Garbage collection routine for cleaning up old and expired * sessions. */ function trash () { $db = DB::get(); // 1800 seconds = 30 minutes, 3600 seconds = 1 hour if (!$db->query("DELETE LOW_PRIORITY FROM $this->_table WHERE UNIX_TIMESTAMP(NOW())-UNIX_TIMESTAMP(modified) > ".SHOPP_SESSION_TIMEOUT)) trigger_error("Could not delete cached session data."); return true; } /** * add() * Adds a product as an item to the cart */ function add ($quantity,&$Product,&$Price,$category,$data=array()) { $NewItem = new Item($Product,$Price,$category,$data); if (!$NewItem->valid()) return false; if (($item = $this->hasitem($NewItem)) !== false) { $this->contents[$item]->add($quantity); $this->added = $this->contents[$item]; $this->data->added = $item; } else { $NewItem->quantity($quantity); $this->contents[] = $NewItem; $this->data->added = count($this->contents)-1; $this->added = $this->contents[$this->data->added]; if ($NewItem->shipping && !$this->data->ShippingDisabled) $this->data->Shipping = true; } do_action_ref_array('shopp_cart_add_item',array(&$NewItem)); $this->updated(); return true; } /** * remove() * Removes an item from the cart */ function remove ($item) { array_splice($this->contents,$item,1); $this->updated(); return true; } /** * update() * Changes the quantity of an item in the cart */ function update ($item,$quantity) { if (empty($this->contents)) return false; if ($quantity == 0) return $this->remove($item); elseif (isset($this->contents[$item])) { $this->contents[$item]->quantity($quantity); if ($this->contents[$item]->quantity == 0) $this->remove($item); $this->updated(); } return true; } /** * updated() * Changes the quantity of an item in the cart */ function updated () { $this->updated = true; } /** * clear() * Empties the contents of the cart */ function clear () { $this->contents = array(); return true; } /** * reset() * Resets the entire session */ function reset () { $this->unload(); $this->session = session_regenerate_id(); } /** * change() * Changes an item to a different product/price variation */ function change ($item,&$Product,$pricing) { // Don't change anything if everything is the same if ($this->contents[$item]->product == $Product->id && $this->contents[$item]->price == $pricing) return true; // If the updated product and price variation match // add the updated quantity of this item to the other item // and remove this one foreach ($this->contents as $id => $thisitem) { if ($thisitem->product == $Product->id && $thisitem->price == $pricing) { $this->update($id,$thisitem->quantity+$this->contents[$item]->quantity); $this->remove($item); $this->updated(); return true; } } // No existing item, so change this one $qty = $this->contents[$item]->quantity; $category = $this->contents[$item]->category; $this->contents[$item] = new Item($Product,$pricing,$category); $this->contents[$item]->quantity($qty); $this->updated(); if ($this->contents[$item]->shipping && !$this->data->ShippingDisabled) $this->data->Shipping = true; return true; } /** * hasitem() * Determines if a specified item is already in this cart */ function hasitem($NewItem) { $i = 0; foreach ($this->contents as $Item) { if ($Item->product == $NewItem->product && $Item->price == $NewItem->price && (empty($NewItem->data) || (serialize($Item->data) == serialize($NewItem->data)))) return $i; $i++; } return false; } /** * shipzone() * Sets the shipping address location * for calculating shipping estimates */ function shipzone ($data) { if (!isset($this->data->Order->Shipping)) $this->data->Order->Shipping = new Shipping(); $this->data->Order->Shipping->updates($data); // Update state if postcode changes for tax updates if (isset($data['postcode'])) $this->data->Order->Shipping->postarea(); if (!isset($this->data->Order->Billing)) $this->data->Order->Billing = new Billing(); $this->data->Order->Billing->updates($data); if (isset($data['region'])) { $this->data->Order->Shipping->region = $data['region']; $this->data->Order->Billing->region = $data['region']; } if (!empty($data)) $this->updated(); } /** * shipping() * Calulates shipping costs based on the contents * of the cart and the currently available shipping * location set with shipzone() */ function shipping () { if (!$this->data->Order->Shipping) return false; if ($this->freeshipping) return 0; global $Shopp; $ShipCosts = &$this->data->ShipCosts; $Shipping = $this->data->Order->Shipping; $base = $Shopp->Settings->get('base_operations'); $handling = $Shopp->Settings->get('order_shipfee'); $methods = $Shopp->Settings->get('shipping_rates'); if (!is_array($methods)) return 0; if (empty($Shipping->country)) $Shipping->country = $base['country']; if (!$this->retotal) { $fees = 0; // Calculate any product-specific shipping fee markups $shipflag = false; foreach ($this->contents as $Item) { if ($Item->shipping) $shipflag = true; if ($Item->shipfee > 0) $fees += ($Item->quantity * $Item->shipfee); } if ($shipflag) $this->data->Shipping = true; else { $this->data->Shipping = false; return 0; } // Add order handling fee if ($handling > 0) $fees += $handling; $estimate = false; foreach ($methods as $id => $option) { if (isset($option['postcode-required'])) { $this->data->ShippingPostcode = true; if (empty($Shipping->postcode)) { $this->data->ShippingPostcodeError = true; new ShoppError(__('A postal code for calculating shipping estimates and taxes is required before you can proceed to checkout.','Shopp','cart_required_postcode',SHOPP_ERR)); return null; } else $this->data->ShippingPostcodeError = false; } else { $this->data->ShippingPostcode = false; $this->data->ShippingPostcodeError = false; } if ($Shipping->country == $base['country']) { // Use country/domestic region if (isset($option[$base['country']])) $column = $base['country']; // Use the country rate else $column = $Shipping->postarea(); // Try to get domestic regional rate } else if (isset($option[$Shipping->region])) { // Global region rate $column = $Shipping->region; } else { // Worldwide shipping rate, last rate entry end($option); $column = key($option); } list($ShipCalcClass,$process) = explode("::",$option['method']); if (isset($Shopp->ShipCalcs->modules[$ShipCalcClass])) $estimated = apply_filters('shopp_shipping_estimate', $Shopp->ShipCalcs->modules[$ShipCalcClass]->calculate( $this, $fees, $option, $column)); if ($estimated === false) continue; // Skip the cost estimates if (!$estimate || $estimated['cost'] < $estimate['cost']) $estimate = $estimated; // Get lowest estimate } // end foreach ($methods) } // end if (!$this->retotal) if (!isset($ShipCosts[$this->data->Order->Shipping->method])) $this->data->Order->Shipping->method = false; if (!empty($this->data->Order->Shipping->method)) return $ShipCosts[$this->data->Order->Shipping->method]['cost']; $this->data->Order->Shipping->method = $estimate['name']; return $estimate['cost']; } /** * promotions() * Matches, calculates and applies promotion discounts */ function promotions () { global $Shopp; $db = DB::get(); $limit = $Shopp->Settings->get('promo_limit'); // Load promotions if they've not yet been loaded if (empty($this->data->Promotions)) { $promo_table = DatabaseObject::tablename(Promotion::$table); // Add date-based lookup too $this->data->Promotions = $db->query("SELECT * FROM $promo_table WHERE scope='Order' AND ((status='enabled' AND UNIX_TIMESTAMP(starts) > 0 AND UNIX_TIMESTAMP(starts) < UNIX_TIMESTAMP() AND UNIX_TIMESTAMP(ends) > UNIX_TIMESTAMP()) OR status='enabled')",AS_ARRAY); } $PromoCodeFound = false; $PromoCodeExists = false; $PromoLimit = false; $this->data->PromosApplied = array(); foreach ($this->data->Promotions as &$promo) { if (!is_array($promo->rules)) $promo->rules = unserialize($promo->rules); // Add quantity rule automatically for buy x get y promos if ($promo->type == "Buy X Get Y Free") { $promo->search = "all"; if ($promo->rules[count($promo->rules)-1]['property'] != "Item quantity") { $qtyrule = array( 'property' => 'Item quantity', 'logic' => "Is greater than", 'value' => $promo->buyqty); $promo->rules[] = $qtyrule; } } $items = array(); $match = false; $rulematches = 0; foreach ($promo->rules as $rule) { $rulematch = false; switch($rule['property']) { case "Item name": foreach ($this->contents as $id => &$Item) { if (Promotion::match_rule($Item->name,$rule['logic'],$rule['value'], $rule['property'])) { $items[$id] = &$Item; $rulematch = true; } } break; case "Item quantity": foreach ($this->contents as $id => &$Item) { if (Promotion::match_rule(number_format($Item->quantity,0),$rule['logic'],$rule['value'], $rule['property'])) { $items[$id] = &$Item; $rulematch = true; } } break; case "Item amount": foreach ($this->contents as $id => &$Item) { if (Promotion::match_rule(number_format($Item->total,2),$rule['logic'],$rule['value'], $rule['property'])) { $items[$id] = &$Item; $rulematch = true; } } break; case "Total quantity": if (Promotion::match_rule(number_format($this->data->Totals->quantity,0),$rule['logic'],$rule['value'], $rule['property'])) { $rulematch = true; } break; case "Shipping amount": if (Promotion::match_rule(number_format($this->data->Totals->shipping,2),$rule['logic'],$rule['value'], $rule['property'])) { $rulematch = true; } break; case "Subtotal amount": if (Promotion::match_rule(number_format($this->data->Totals->subtotal,2),$rule['logic'],$rule['value'], $rule['property'])) { $rulematch = true; } break; case "Promo code": // Match previously applied codes if (is_array($this->data->PromoCodes) && in_array($rule['value'],$this->data->PromoCodes)) { $rulematch = true; break; } // Match a new code if (!empty($this->data->PromoCode)) { if (Promotion::match_rule($this->data->PromoCode,$rule['logic'],$rule['value'], $rule['property'])) { if (is_array($this->data->PromoCodes) && !in_array($this->data->PromoCode, $this->data->PromoCodes)) { $this->data->PromoCodes[] = $rule['value']; $PromoCodeFound = $rule['value']; } else $PromoCodeExists = true; $this->data->PromoCode = false; $rulematch = true; } } break; } if ($rulematch && $promo->search == "all") $rulematches++; if ($rulematch && $promo->search == "any") { $match = true; break; // One matched, no need to match any more } } // end foreach ($promo->rules) if ($promo->search == "all" && $rulematches == count($promo->rules)) $match = true; // Everything matches up, apply the promotion if ($match && !$PromoLimit) { // echo "Matched $promo->name".BR; if (!empty($items)) { $freeshipping = 0; // Apply promo calculation to specific cart items foreach ($items as $item) { switch ($promo->type) { case "Percentage Off": $this->data->Totals->discount += $item->total*($promo->discount/100); break; case "Amount Off": $this->data->Totals->discount += $promo->discount; break; case "Buy X Get Y Free": $this->data->Totals->discount += floor($item->quantity / ($promo->buyqty + $promo->getqty))*($item->unitprice); break; case "Free Shipping": $freeshipping++; break; } } if ($freeshipping == count($this->contents) && $promo->scope == "Order") $this->freeshipping = true; else $this->freeshipping = false; } else { // Apply promo calculation to entire order switch ($promo->type) { case "Percentage Off": $this->data->Totals->discount += $this->data->Totals->subtotal*($promo->discount/100); break; case "Amount Off": $this->data->Totals->discount += $promo->discount; break; case "Free Shipping": $this->freeshipping = true; break; } } $this->data->PromosApplied[] = $promo; if ($limit > 0 && count($this->data->PromosApplied)+1 > $limit) { $PromoLimit = true; break; } } if ($match && $promo->exclusive == "on") break; } // end foreach ($Promotions) // Promo code found, but ran into promotion limits if (!empty($this->data->PromoCode) && $PromoLimit) { $this->data->PromoCodeResult = __("No additional codes can be applied.","Shopp"); $this->data->PromoCodes = array_diff($this->data->PromoCodes,array($PromoCodeFound)); $this->data->PromoCode = false; } // Promo code not found if (!empty($this->data->PromoCode) && !$PromoCodeFound && !$PromoCodeExists) { $this->data->PromoCodeResult = $this->data->PromoCode.' '.__("is not a valid code.","Shopp"); $this->data->PromoCodes = array_diff($this->data->PromoCodes,array($this->data->PromoCode)); $this->data->PromoCode = false; } } /** * taxrate() * Determines the taxrate based on the currently * available shipping information set by shipzone() */ function taxrate () { global $Shopp; if ($Shopp->Settings->get('taxes') == "off") return false; $taxrates = $Shopp->Settings->get('taxrates'); $base = $Shopp->Settings->get('base_operations'); if (!is_array($taxrates)) return false; if (!empty($this->data->Order->Shipping->country)) $country = $this->data->Order->Shipping->country; elseif (!empty($this->data->Order->Billing->country)) $country = $this->data->Order->Billing->country; else $country = $base['country']; if (!empty($this->data->Order->Shipping->state)) $zone = $this->data->Order->Shipping->state; elseif (!empty($this->data->Order->Billing->state)) $zone = $this->data->Order->Billing->state; else $zone = $base['zone']; $global = false; foreach ($taxrates as $setting) { // Grab the global setting if found if ($setting['country'] == "*") { $global = $setting; continue; } if (isset($setting['zone'])) { if ($country == $setting['country'] && $zone == $setting['zone']) return apply_filters('shopp_cart_taxrate',$setting['rate']/100); } elseif ($country == $setting['country']) { return apply_filters('shopp_cart_taxrate',$setting['rate']/100); } } if ($global) return apply_filters('shopp_cart_taxrate',$global['rate']/100); } /** * totals() * Calculates subtotal, shipping, tax and * order total amounts */ function totals () { global $Shopp; if (!$this->retotal && !$this->updated) return true; $shippingTaxed = ($Shopp->Settings->get('tax_shipping') == "on"); $Totals =& $this->data->Totals; $Totals->quantity = 0; $Totals->subtotal = 0; $Totals->discount = 0; $Totals->shipping = 0; $Totals->taxed = 0; $Totals->tax = 0; $Totals->total = 0; $Totals->taxrate = $this->taxrate(); $freeshipping = true; // Assume free shipping for the cart unless proven wrong foreach ($this->contents as $key => $Item) { // Add the item to the shipped list if ($Item->shipping && !$Item->freeshipping) $this->shipped[$key] = $Item; // Item does not have free shipping, // so the cart shouldn't have free shipping if (!$Item->freeshipping) $freeshipping = false; $Totals->quantity += $Item->quantity; $Totals->subtotal += $Item->total; // Tabulate the taxable total to be calculated after discounts if ($Item->taxable && $Totals->taxrate > 0) $Totals->taxed += $Item->total; } $this->freeshipping = $freeshipping; if ($this->data->ShippingDisabled) $this->freeshipping = false; // Calculate discounts $this->promotions(); $discount = ($Totals->discount > $Totals->subtotal)?$Totals->subtotal:$Totals->discount; // Calculate shipping if (!$this->data->ShippingDisabled && $this->data->Shipping && !$this->freeshipping) $Totals->shipping = $this->shipping(); // Calculate taxes if ($discount > $Totals->taxed) $Totals->taxed = 0; else $Totals->taxed -= $discount; if($shippingTaxed) $Totals->taxed += $Totals->shipping; $Totals->tax = round($Totals->taxed*$Totals->taxrate,2); // Calculate final totals $Totals->total = round($Totals->subtotal - round($discount,2) + $Totals->shipping + $Totals->tax,2); do_action_ref_array('shopp_cart_retotal',array(&$Totals)); } /** * logins () * Handle login processing */ function logins () { global $Shopp; if (!$this->data->Order->Customer) { $this->data->Order->Customer = new Customer(); $this->data->Order->Billing = new Billing(); $this->data->Order->Shipping = new Shipping(); } $authentication = $Shopp->Settings->get('account_system'); if (isset($_GET['acct']) && isset($this->data->Order->Customer) && $_GET['acct'] == "logout") { if ($authentication == "wordpress" && $this->data->login) add_action('shopp_logout','wp_clear_auth_cookie'); return $this->logout(); } switch ($authentication) { case "wordpress": if ($this->data->login) add_action('wp_logout',array(&$this,'logout')); // See if the wordpress user is already logged in $user = wp_get_current_user(); if (!empty($user->ID) && !$this->data->login) { if ($Account = new Customer($user->ID,'wpuser')) { $this->loggedin($Account); $this->data->Order->Customer->wpuser = $user->ID; break; } } if (empty($_POST['process-login'])) return false; if (!empty($_POST['account-login'])) { if (strpos($_POST['account-login'],'@') !== false) $mode = "email"; else $mode = "loginname"; $loginname = $_POST['account-login']; } else { new ShoppError(__('You must provide a valid login name or email address to proceed.'), 'missing_account', SHOPP_AUTH_ERR); } if ($loginname) { $this->auth($loginname,$_POST['password-login'],$mode); } break; case "shopp": if (!isset($_POST['process-login'])) return false; if ($_POST['process-login'] != "true") return false; $mode = "loginname"; if (!empty($_POST['account-login']) && strpos($_POST['account-login'],'@') !== false) $mode = "email"; $this->auth($_POST['account-login'],$_POST['password-login'],$mode); break; } } /** * auth () * Authorize login credentials */ function auth ($id,$password,$type='email') { global $Shopp; $db = DB::get(); $authentication = $Shopp->Settings->get('account_system'); switch($authentication) { case "shopp": $Account = new Customer($id,'email'); if (empty($Account)) { new ShoppError(__("No customer account was found with that email.","Shopp"),'invalid_account',SHOPP_AUTH_ERR); return false; } if (!wp_check_password($password,$Account->password)) { new ShoppError(__("The password is incorrect.","Shopp"),'invalid_password',SHOPP_AUTH_ERR); return false; } break; case "wordpress": if($type == 'email'){ $user = get_user_by_email($id); if ($user) $loginname = $user->user_login; else { new ShoppError(__("No customer account was found with that email.","Shopp"),'invalid_account',SHOPP_AUTH_ERR); return false; } } else $loginname = $id; $user = wp_authenticate($loginname,$password); if (!is_wp_error($user)) { wp_set_auth_cookie($user->ID, false, $Shopp->secure); do_action('wp_login', $loginname); if ($Account = new Customer($user->ID,'wpuser')) { $this->loggedin($Account); $this->data->Order->Customer->wpuser = $user->ID; add_action('wp_logout',array(&$this,'logout')); } return true; } else { // WordPress User Authentication failed $_e = $user->get_error_code(); if($_e == 'invalid_username') new ShoppError(__("No customer account was found with that login.","Shopp"),'invalid_account',SHOPP_AUTH_ERR); else if($_e == 'incorrect_password') new ShoppError(__("The password is incorrect.","Shopp"),'invalid_password',SHOPP_AUTH_ERR); else new ShoppError(__('Unknown login error: ').$_e,false,SHOPP_AUTH_ERR); return false; } break; default: return false; } $this->loggedin($Account); } /** * loggedin() * Initialize login data */ function loggedin ($Account) { $this->data->login = true; $this->data->Order->Customer = $Account; unset($this->data->Order->Customer->password); $this->data->Order->Billing = new Billing($Account->id,'customer'); $this->data->Order->Billing->card = ""; $this->data->Order->Billing->cardexpires = ""; $this->data->Order->Billing->cardholder = ""; $this->data->Order->Billing->cardtype = ""; $this->data->Order->Shipping = new Shipping($Account->id,'customer'); if (empty($this->data->Order->Shipping->id)) $this->data->Order->Shipping->copydata($this->data->Order->Billing); do_action_ref_array('shopp_login',array(&$Account)); } /** * logout() * Clear the session account data */ function logout () { do_action('shopp_logout'); $this->data->login = false; $this->data->Order->wpuser = false; $this->data->Order->Customer->id = false; $this->data->Order->Billing->id = false; $this->data->Order->Billing->customer = false; $this->data->Order->Shipping->id = false; $this->data->Order->Shipping->customer = false; session_commit(); } /** * secured() * Check or set the security setting for the cart */ function secured ($setting=null) { if (is_null($setting)) return $this->data->secure; $this->data->secure = ($setting); if (SHOPP_DEBUG) { if ($this->data->secure) new ShoppError('Switching the cart to secure mode.',false,SHOPP_DEBUG_ERR); else new ShoppError('Switching the cart to unsecure mode.',false,SHOPP_DEBUG_ERR); } return $this->data->secure; } /** * securekey() * Generate the security key */ function securekey () { global $Shopp; require_once(ABSPATH . WPINC . '/pluggable.php'); if (!is_shopp_secure()) return false; $expiration = time()+SHOPP_SESSION_TIMEOUT; if (defined('SECRET_AUTH_KEY') && SECRET_AUTH_KEY != '') $key = SECRET_AUTH_KEY; else $key = md5(serialize($this->data).time()); $content = hash_hmac('sha256', $this->session . '|' . $expiration, $key); if ( version_compare(phpversion(), '5.2.0', 'ge') ) setcookie(SHOPP_SECURE_KEY,$content,0,'/','',true,true); else setcookie(SHOPP_SECURE_KEY,$content,0,'/','',true); return $content; } /** * request() * Processes cart requests and updates the cart * accordingly */ function request () { global $Shopp; do_action('shopp_cart_request'); if (isset($_REQUEST['checkout'])) shopp_redirect($Shopp->link('checkout',true)); if (isset($_REQUEST['shopping'])) shopp_redirect($Shopp->link('catalog')); if (isset($_REQUEST['shipping'])) { $countries = $Shopp->Settings->get('countries'); $regions = $Shopp->Settings->get('regions'); $_REQUEST['shipping']['region'] = $regions[$countries[$_REQUEST['shipping']['country']]['region']]; if (!empty($_REQUEST['shipping']['postcode'])) // Protect input field from XSS $_REQUEST['shipping']['postcode'] = attribute_escape($_REQUEST['shipping']['postcode']); unset($countries,$regions); $this->shipzone($_REQUEST['shipping']); } else if (!isset($this->data->Order->Shipping->country)) { $base = $Shopp->Settings->get('base_operations'); $_REQUEST['shipping']['country'] = $base['country']; $this->shipzone($_REQUEST['shipping']); } if (!empty($_REQUEST['promocode'])) { $this->data->PromoCodeResult = ""; if (!in_array($_REQUEST['promocode'],$this->data->PromoCodes)) { $this->data->PromoCode = attribute_escape($_REQUEST['promocode']); // Protect from XSS $this->updated(); } else $this->data->PromoCodeResult = __("That code has already been applied.","Shopp"); } if (isset($_REQUEST['remove'])) $_REQUEST['cart'] = "remove"; if (isset($_REQUEST['update'])) $_REQUEST['cart'] = "update"; if (isset($_REQUEST['empty'])) $_REQUEST['cart'] = "empty"; if (!isset($_REQUEST['quantity'])) $_REQUEST['quantity'] = 1; switch($_REQUEST['cart']) { case "add": if (isset($_REQUEST['product'])) { $quantity = (empty($product['quantity']) && $product['quantity'] !== 0)?1:$product['quantity']; // Add 1 by default $Product = new Product($_REQUEST['product']); $pricing = false; if (!empty($_REQUEST['options']) && !empty($_REQUEST['options'][0])) $pricing = $_REQUEST['options']; else $pricing = $_REQUEST['price']; $category = false; if (!empty($_REQUEST['category'])) $category = $_REQUEST['category']; if (isset($_REQUEST['data'])) $data = $_REQUEST['data']; else $data = array(); if (isset($_REQUEST['item'])) $result = $this->change($_REQUEST['item'],$Product,$pricing); else $result = $this->add($quantity,$Product,$pricing,$category,$data); } if (isset($_REQUEST['products']) && is_array($_REQUEST['products'])) { foreach ($_REQUEST['products'] as $id => $product) { $quantity = (empty($product['quantity']) && $product['quantity'] !== 0)?1:$product['quantity']; // Add 1 by default $Product = new Product($product['product']); $pricing = false; if (!empty($product['options']) && !empty($product['options'][0])) $pricing = $product['options']; elseif (isset($product['price'])) $pricing = $product['price']; $category = false; if (!empty($product['category'])) $category = $product['category']; if (!empty($product['data'])) $data = $product['data']; else $data = array(); if (!empty($Product->id)) { if (isset($product['item'])) $result = $this->change($product['item'],$Product,$pricing); else $result = $this->add($quantity,$Product,$pricing,$category,$data); } } } break; case "remove": if (!empty($this->contents)) $this->remove(current($_REQUEST['remove'])); break; case "empty": $this->clear(); break; default: if (isset($_REQUEST['item']) && isset($_REQUEST['quantity'])) { $this->update($_REQUEST['item'],$_REQUEST['quantity']); } elseif (!empty($_REQUEST['items'])) { foreach ($_REQUEST['items'] as $id => $item) { if (isset($item['quantity'])) { $item['quantity'] = ceil(preg_replace('/[^\d\.]+/','',$item['quantity'])); if (!empty($item['quantity'])) $this->update($id,$item['quantity']); if (isset($_REQUEST['remove'][$id])) $this->remove($_REQUEST['remove'][$id]); } if (isset($item['product']) && isset($item['price']) && $item['product'] == $this->contents[$id]->product && $item['price'] != $this->contents[$id]->price) { $Product = new Product($item['product']); $this->change($id,$Product,$item['price']); } } } } do_action('shopp_cart_updated',$this); } /** * ajax() * Handles AJAX-based cart request responses */ function ajax () { global $Shopp; if ($_REQUEST['response'] == "html") { echo $this->tag('sidecart'); exit(); } $AjaxCart = new StdClass(); $AjaxCart->url = $Shopp->link('cart'); $AjaxCart->Totals = clone($this->data->Totals); $AjaxCart->Contents = array(); foreach($this->contents as $item) { $cartitem = clone($item); unset($cartitem->options); $AjaxCart->Contents[] = $cartitem; } if (isset($this->data->added)) $AjaxCart->Item = clone($this->contents[$this->data->added]); else $AjaxCart->Item = new Item(); unset($AjaxCart->Item->options); echo json_encode($AjaxCart); exit(); } /** * validate() * Validate checkout form order data before processing */ function validate () { global $Shopp; $authentication = $Shopp->Settings->get('account_system'); if (empty($_POST['firstname'])) return new ShoppError(__('You must provide your first name.','Shopp'),'cart_validation'); if (empty($_POST['lastname'])) return new ShoppError(__('You must provide your last name.','Shopp'),'cart_validation'); $rfc822email = '([^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+|\\x22([^\\x0d'. '\\x22\\x5c\\x80-\\xff]|\\x5c[\\x00-\\x7f])*\\x22)(\\x2e([^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e'. '\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+|\\x22([^\\x0d\\x22\\x5c\\x80-\\xff]|\\x5c[\\x00-\\x7f])*'. '\\x22))*\\x40([^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+'. '|\\x5b([^\\x0d\\x5b-\\x5d\\x80-\\xff]|\\x5c[\\x00-\\x7f])*\\x5d)(\\x2e([^\\x00-\\x20\\x22\\x28'. '\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+|\\x5b([^\\x0d\\x5b-\\x5d\\x80-\\xff]'. '|\\x5c[\\x00-\\x7f])*\\x5d))*'; if(!preg_match("!^$rfc822email$!", $_POST['email'])) return new ShoppError(__('You must provide a valid e-mail address.','Shopp'),'cart_validation'); if ($authentication == "wordpress" && !$this->data->login) { require_once(ABSPATH."/wp-includes/registration.php"); // Validate possible wp account names for availability if(isset($_POST['login'])){ if(username_exists($_POST['login'])) return new ShoppError(__('The login name you provided is not available. Try logging in if you have previously created an account.'), 'cart_validation'); } else { // need to find a usuable login list($handle,$domain) = explode("@",$_POST['email']); if(!username_exists($handle)) $_POST['login'] = $handle; $handle = $_POST['firstname'].substr($_POST['lastname'],0,1); if(!isset($_POST['login']) && !username_exists($handle)) $_POST['login'] = $handle; $handle = substr($_POST['firstname'],0,1).$_POST['lastname']; if(!isset($_POST['login']) && !username_exists($handle)) $_POST['login'] = $handle; $handle .= rand(1000,9999); if(!isset($_POST['login']) && !username_exists($handle)) $_POST['login'] = $handle; if(!isset($_POST['login'])) return new ShoppError(__('A login is not available for creation with the information you provided. Please try a different email address or name, or try logging in if you previously created an account.'),'cart_validation'); } if(SHOPP_DEBUG) new ShoppError('Login set to '. $_POST['login'] . ' for WordPress account creation.',false,SHOPP_DEBUG_ERR); $ExistingCustomer = new Customer($_POST['email'],'email'); if (email_exists($_POST['email']) || !empty($ExistingCustomer->id)) return new ShoppError(__('The email address you entered is already in use. Try logging in if you previously created an account, or enter another email address to create your new account.','Shopp'),'cart_validation'); } elseif ($authentication == "shopp" && !$this->data->login) { $ExistingCustomer = new Customer($_POST['email'],'email'); if (!empty($ExistingCustomer->id)) return new ShoppError(__('The email address you entered is already in use. Try logging in if you previously created an account, or enter another email address to create a new account.','Shopp'),'cart_validation'); } // Validate WP account if (isset($_POST['login']) && empty($_POST['login'])) return new ShoppError(__('You must enter a login name for your account.','Shopp'),'cart_validation'); if (isset($_POST['login'])) { require_once(ABSPATH."/wp-includes/registration.php"); if (username_exists($_POST['login'])) return new ShoppError(__('The login name you provided is already in use. Try logging in if you previously created that account, or enter another login name for your new account.','Shopp'),'cart_validation'); } if (isset($_POST['password'])) { if (empty($_POST['password']) || empty($_POST['confirm-password'])) return new ShoppError(__('You must provide a password for your account and confirm it to ensure correct spelling.','Shopp'),'cart_validation'); if ($_POST['password'] != $_POST['confirm-password']) { $_POST['password'] = ""; $_POST['confirm-password'] = ""; return new ShoppError(__('The passwords you entered do not match. Please re-enter your passwords.','Shopp'),'cart_validation'); } } if (empty($_POST['billing']['address']) || strlen($_POST['billing']['address']) < 4) return new ShoppError(__('You must enter a valid street address for your billing information.','Shopp'),'cart_validation'); if (empty($_POST['billing']['postcode'])) return new ShoppError(__('You must enter a valid postal code for your billing information.','Shopp'),'cart_validation'); if (empty($_POST['billing']['country'])) return new ShoppError(__('You must select a country for your billing information.','Shopp'),'cart_validation'); // Skip validating billing details for free purchases // and remote checkout systems if ((int)$this->data->Totals->total == 0 || !empty($_GET['shopp_xco'])) return apply_filters('shopp_validate_checkout',true); if (empty($_POST['billing']['card'])) return new ShoppError(__('You did not provide a credit card number.','Shopp'),'cart_validation'); if (empty($_POST['billing']['cardtype'])) return new ShoppError(__('You did not select a credit card type.','Shopp'),'cart_validation'); // credit card validation switch(strtolower($_POST['billing']['cardtype'])) { case "american express": case "amex": $pattern = '/^3[4,7]\d{13}$/'; break; case "diner's club": case "diners club": $pattern = '/^3[0,6,8]\d{12}$/'; break; case "discover": $pattern = '/^6011-?\d{4}-?\d{4}-?\d{4}$/'; break; case "mastercard": $pattern = '/^5[1-5]\d{2}-?\d{4}-?\d{4}-?\d{4}$/'; break; case "visa": $pattern = '/^4\d{3}-?\d{4}-?\d{4}-?\d{4}$/'; break; default: $pattern = false; } if ($pattern && !preg_match($pattern,$_POST['billing']['card'])) return new ShoppError(__('The credit card number you provided is invalid.','Shopp'),'cart_validation'); // credit card checksum validation $cs = 0; $cc = str_replace("-","",$_POST['billing']['card']); $code = strrev(str_replace("-","",$_POST['billing']['card'])); for ($i = 0; $i < strlen($code); $i++) { $d = intval($code[$i]); if ($i & 1) $d *= 2; $cs += $d % 10; if ($d > 9) $cs += 1; } if ($cs % 10 != 0) return new ShoppError(__('The credit card number you provided is not valid.','Shopp'),'cart_validation'); if (empty($_POST['billing']['cardexpires-mm'])) return new ShoppError(__('You did not enter the month the credit card expires.','Shopp'),'cart_validation'); if (empty($_POST['billing']['cardexpires-yy'])) return new ShoppError(__('You did not enter the year the credit card expires.','Shopp'),'cart_validation'); if (!empty($_POST['billing']['cardexpires-mm']) && !empty($_POST['billing']['cardexpires-yy']) && $_POST['billing']['cardexpires-mm'] < date('n') && $_POST['billing']['cardexpires-yy'] <= date('y')) return new ShoppError(__('The credit card expiration date you provided has already expired.','Shopp'),'cart_validation'); if (strlen($_POST['billing']['cardholder']) < 2) return new ShoppError(__('You did not enter the name on the credit card you provided.','Shopp'),'cart_validation'); if (strlen($_POST['billing']['cvv']) < 3) return new ShoppError(__('You did not enter a valid security ID for the card you provided. The security ID is a 3 or 4 digit number found on the back of the credit card.','Shopp'),'cart_validation'); return apply_filters('shopp_validate_checkout',true); } /** * validorder() * Validates order data during checkout processing to verify that sufficient information exists to process. */ function validorder () { $Order = $this->data->Order; $Customer = $Order->Customer; $Shipping = $this->data->Order->Shipping; if(empty($this->contents)) return false; // No items if(empty($Order)) return false; // No order data if(!$Customer) return false; // No Customer // Always require name and email if( empty($Customer->firstname) || empty($Customer->lastname)) return false; if( empty($Customer->email) ) return false; // Check for shipped items but no Shipping information if ($this->data->Shipping) { if(empty($Shipping->address)) return false; if(empty($Shipping->city)) return false; if(empty($Shipping->country)) return false; if(empty($Shipping->postcode)) return false; } return true; } /** * orderisfree() * Determines if the current order has no cost */ function orderisfree() { $status = (count($this->contents) > 0 && number_format($this->data->Totals->total,2) == 0)?true:false; return apply_filters('shopp_free_order',$status); } function tag ($property,$options=array()) { global $Shopp; $submit_attrs = array('title','value','disabled','tabindex','accesskey'); // Return strings with no options switch ($property) { case "url": return $Shopp->link('cart'); break; case "hasitems": return (count($this->contents) > 0); break; case "totalitems": return $this->data->Totals->quantity; break; case "items": if (!$this->looping) { reset($this->contents); $this->looping = true; } else next($this->contents); if (current($this->contents)) return true; else { $this->looping = false; reset($this->contents); return false; } case "lastitem": return $this->contents[$this->data->added]; break; case "totalpromos": return count($this->data->PromosApplied); break; case "haspromos": return (count($this->data->PromosApplied) > 0); break; case "promos": if (!$this->looping) { reset($this->data->PromosApplied); $this->looping = true; } else next($this->data->PromosApplied); if (current($this->data->PromosApplied)) return true; else { $this->looping = false; reset($this->data->PromosApplied); return false; } case "promo-name": $promo = current($this->data->PromosApplied); return $promo->name; break; case "promo-discount": $promo = current($this->data->PromosApplied); if (empty($options['label'])) $options['label'] = __('Off!','Shopp'); if (!empty($options['before'])) $string = $options['before']; switch($promo->type) { case "Free Shipping": $string .= $Shopp->Settings->get('free_shipping_text'); case "Percentage Off": $string .= percentage($promo->discount)." ".$options['label']; case "Amount Off": $string .= money($promo->discount)." ".$options['label']; case "Buy X Get Y Free": return ""; } if (!empty($options['after'])) $string = $options['after']; return $string; break; case "function": $result = ''; if (!$this->data->Errors->exist()) return $result; $errors = $this->data->Errors->get(SHOPP_COMM_ERR); foreach ((array)$errors as $error) if (!empty($error)) $result .= '

'.$error->message().'

'; $this->data->Errors->reset(); // Reset after display return $result; break; case "empty-button": if (!isset($options['value'])) $options['value'] = __('Empty Cart','Shopp'); return ''; break; case "update-button": if (!isset($options['value'])) $options['value'] = __('Update Subtotal','Shopp'); return ''; break; case "sidecart": ob_start(); include(SHOPP_TEMPLATES."/sidecart.php"); $content = ob_get_contents(); ob_end_clean(); return $content; break; case "hasdiscount": return ($this->data->Totals->discount > 0); break; case "discount": return money($this->data->Totals->discount); break; } $result = ""; switch ($property) { case "promos-available": if (empty($this->data->Promotions)) return false; // Skip if the promo limit has been reached if ($Shopp->Settings->get('promo_limit') > 0 && count($this->data->PromosApplied) >= $Shopp->Settings->get('promo_limit')) return false; return true; break; case "promo-code": // Skip if no promotions exist if (empty($this->data->Promotions)) return false; // Skip if the promo limit has been reached if ($Shopp->Settings->get('promo_limit') > 0 && count($this->data->PromosApplied) >= $Shopp->Settings->get('promo_limit')) return false; if (!isset($options['value'])) $options['value'] = __("Apply Promo Code","Shopp"); $result .= ''; return $result; case "has-shipping-methods": return (!$this->data->ShippingDisabled && count($this->data->ShipCosts) > 1 && $this->data->Shipping); break; case "needs-shipped": return $this->data->Shipping; break; case "hasshipcosts": case "has-ship-costs": return ($this->data->Totals->shipping > 0); break; case "needs-shipping-estimates": $markets = $Shopp->Settings->get('target_markets'); return ($this->data->Shipping && ($this->data->ShippingPostcode || count($markets) > 1)); break; case "shipping-estimates": if (!$this->data->Shipping) return ""; $base = $Shopp->Settings->get('base_operations'); $markets = $Shopp->Settings->get('target_markets'); if (empty($markets)) return ""; foreach ($markets as $iso => $country) $countries[$iso] = $country; if (!empty($this->data->Order->Shipping->country)) $selected = $this->data->Order->Shipping->country; else $selected = $base['country']; $result .= ''; return $result; break; } $result = ""; switch ($property) { case "subtotal": $result = $this->data->Totals->subtotal; break; case "shipping": if (!$this->data->Shipping) return ""; if (isset($options['label'])) { $options['currency'] = "false"; if ($this->data->Totals->shipping === 0) { $result = $Shopp->Settings->get('free_shipping_text'); if (empty($result)) $result = __('Free Shipping!','Shopp'); } else $result = $options['label']; } else { if ($this->data->Totals->shipping === null) return __("Enter Postal Code","Shopp"); elseif ($this->data->Totals->shipping === false) return __("Not Available","Shopp"); else $result = $this->data->Totals->shipping; } break; case "hastaxes": case "has-taxes": return ($this->data->Totals->tax > 0); break; case "tax": if ($this->data->Totals->tax > 0) { if (isset($options['label'])) { $options['currency'] = "false"; $result = $options['label']; } else $result = $this->data->Totals->tax; } else $options['currency'] = "false"; break; case "total": $result = $this->data->Totals->total; break; } if (isset($options['currency']) && !value_is_true($options['currency'])) return $result; else return ''.money($result).''; return false; } function itemtag ($property,$options=array()) { if ($this->looping) { $Item = current($this->contents); if ($Item !== false) { $id = key($this->contents); if ($property == "id") return $id; return $Item->tag($id,$property,$options); } } else return false; } /** * shippingtag() * shopp('shipping','...') * Used primarily in the summary.php template **/ function shippingtag ($property,$options=array()) { global $Shopp; $ShipCosts =& $this->data->ShipCosts; $result = ""; switch ($property) { case "hasestimates": return (count($ShipCosts) > 0); break; case "methods": if (!isset($this->sclooping)) $this->sclooping = false; if (!$this->sclooping) { reset($ShipCosts); $this->sclooping = true; } else next($ShipCosts); if (current($ShipCosts)) return true; else { $this->sclooping = false; reset($ShipCosts); return false; } break; case "method-name": return key($ShipCosts); break; case "method-cost": $method = current($ShipCosts); return money($method['cost']); break; case "method-selector": $method = current($ShipCosts); $checked = ''; if ((isset($this->data->Order->Shipping->method) && $this->data->Order->Shipping->method == $method['name']) || ($method['cost'] == $this->data->Totals->shipping)) $checked = ' checked="checked"'; $result .= ''; return $result; break; case "method-delivery": $periods = array("h"=>3600,"d"=>86400,"w"=>604800,"m"=>2592000); $method = current($ShipCosts); $estimates = explode("-",$method['delivery']); $format = get_option('date_format'); if ($estimates[0] == $estimates[1]) $estimates = array($estimates[0]); $result = ""; for ($i = 0; $i < count($estimates); $i++){ list($interval,$p) = sscanf($estimates[$i],'%d%s'); if (!empty($result)) $result .= "—"; $result .= _d($format,mktime()+($interval*$periods[$p])); } return $result; } } function checkouttag ($property,$options=array()) { global $Shopp,$wp; $gateway = $Shopp->Settings->get('payment_gateway'); $xcos = $Shopp->Settings->get('xco_gateways'); $pages = $Shopp->Settings->get('pages'); $base = $Shopp->Settings->get('base_operations'); $countries = $Shopp->Settings->get('target_markets'); $process = get_query_var('shopp_proc'); $xco = get_query_var('shopp_xco'); $select_attrs = array('title','required','class','disabled','required','size','tabindex','accesskey'); $submit_attrs = array('title','class','value','disabled','tabindex','accesskey'); if (!isset($options['mode'])) $options['mode'] = "input"; switch ($property) { case "url": $ssl = true; // Test Mode will not require encrypted checkout if (strpos($gateway,"TestMode.php") !== false || isset($_GET['shopp_xco']) || $this->orderisfree() || SHOPP_NOSSL) $ssl = false; $link = $Shopp->link('checkout',$ssl); // Pass any arguments along $args = $_GET; if (isset($args['page_id'])) unset($args['page_id']); $link = esc_url(add_query_arg($args,$link)); if ($process == "confirm-order") $link = apply_filters('shopp_confirm_url',$link); else $link = apply_filters('shopp_checkout_url',$link); return $link; break; case "function": if (!isset($options['shipcalc'])) $options['shipcalc'] = ''; $regions = $Shopp->Settings->get('zones'); $base = $Shopp->Settings->get('base_operations'); $output = ''."\n"; if (!empty($options['value'])) $value = $options['value']; else $value = "process"; $output .= '
'; if ($value == "confirmed") $output = apply_filters('shopp_confirm_form',$output); else $output = apply_filters('shopp_checkout_form',$output); return $output; break; case "error": $result = ""; if (!$this->data->Errors->exist(SHOPP_COMM_ERR)) return false; $errors = $this->data->Errors->get(SHOPP_COMM_ERR); foreach ((array)$errors as $error) if (!empty($error)) $result .= $error->message(); return $result; // if (isset($options['show']) && $options['show'] == "code") return $this->data->OrderError->code; // return $this->data->OrderError->message; break; case "cart-summary": ob_start(); include(SHOPP_TEMPLATES."/summary.php"); $content = ob_get_contents(); ob_end_clean(); return $content; break; case "loggedin": return $this->data->login; break; case "notloggedin": return (!$this->data->login && $Shopp->Settings->get('account_system') != "none"); break; case "email-login": // Deprecating case "loginname-login": // Deprecating case "account-login": if (!empty($_POST['account-login'])) $options['value'] = $_POST['account-login']; return ''; break; case "password-login": if (!empty($_POST['password-login'])) $options['value'] = $_POST['password-login']; return ''; break; case "submit-login": // Deprecating case "login-button": $string = ''; $string .= ''; return $string; break; case "firstname": if ($options['mode'] == "value") return $this->data->Order->Customer->firstname; if (!empty($this->data->Order->Customer->firstname)) $options['value'] = $this->data->Order->Customer->firstname; return ''; break; case "lastname": if ($options['mode'] == "value") return $this->data->Order->Customer->lastname; if (!empty($this->data->Order->Customer->lastname)) $options['value'] = $this->data->Order->Customer->lastname; return ''; break; case "email": if ($options['mode'] == "value") return $this->data->Order->Customer->email; if (!empty($this->data->Order->Customer->email)) $options['value'] = $this->data->Order->Customer->email; return ''; break; case "loginname": if ($options['mode'] == "value") return $this->data->Order->Customer->login; if (!empty($this->data->Order->Customer->login)) $options['value'] = $this->data->Order->Customer->login; return ''; break; case "password": if ($options['mode'] == "value") return $this->data->Order->Customer->password; if (!empty($this->data->Order->Customer->password)) $options['value'] = $this->data->Order->Customer->password; return ''; break; case "confirm-password": if (!empty($this->data->Order->Customer->confirm_password)) $options['value'] = $this->data->Order->Customer->confirm_password; return ''; break; case "phone": if ($options['mode'] == "value") return $this->data->Order->Customer->phone; if (!empty($this->data->Order->Customer->phone)) $options['value'] = $this->data->Order->Customer->phone; return ''; break; case "organization": case "company": if ($options['mode'] == "value") return $this->data->Order->Customer->company; if (!empty($this->data->Order->Customer->company)) $options['value'] = $this->data->Order->Customer->company; return ''; break; case "customer-info": $allowed_types = array("text","password","hidden","checkbox","radio"); if (empty($options['type'])) $options['type'] = "hidden"; if (isset($options['name']) && $options['mode'] == "value") return $this->data->Order->Customer->info[$options['name']]; if (isset($options['name']) && in_array($options['type'],$allowed_types)) { if (isset($this->data->Order->Customer->info[$options['name']])) $options['value'] = $this->data->Order->Customer->info[$options['name']]; return ''; } break; // SHIPPING TAGS case "shipping": return $this->data->Shipping; case "shipping-address": if ($options['mode'] == "value") return $this->data->Order->Shipping->address; if (!empty($this->data->Order->Shipping->address)) $options['value'] = $this->data->Order->Shipping->address; return ''; break; case "shipping-xaddress": if ($options['mode'] == "value") return $this->data->Order->Shipping->xaddress; if (!empty($this->data->Order->Shipping->xaddress)) $options['value'] = $this->data->Order->Shipping->xaddress; return ''; break; case "shipping-city": if ($options['mode'] == "value") return $this->data->Order->Shipping->city; if (!empty($this->data->Order->Shipping->city)) $options['value'] = $this->data->Order->Shipping->city; return ''; break; case "shipping-province": case "shipping-state": if ($options['mode'] == "value") return $this->data->Order->Shipping->state; if (!isset($options['selected'])) $options['selected'] = false; if (!empty($this->data->Order->Shipping->state)) { $options['selected'] = $this->data->Order->Shipping->state; $options['value'] = $this->data->Order->Shipping->state; } $country = $base['country']; if (!empty($this->data->Order->Shipping->country)) $country = $this->data->Order->Shipping->country; if (!array_key_exists($country,$countries)) $country = key($countries); if (empty($options['type'])) $options['type'] = "menu"; $regions = $Shopp->Settings->get('zones'); $states = $regions[$country]; if (is_array($states) && $options['type'] == "menu") { $label = (!empty($options['label']))?$options['label']:''; $output = ''; } else $output .= ''; return $output; break; case "shipping-postcode": if ($options['mode'] == "value") return $this->data->Order->Shipping->postcode; if (!empty($this->data->Order->Shipping->postcode)) $options['value'] = $this->data->Order->Shipping->postcode; return ''; break; case "shipping-country": if ($options['mode'] == "value") return $this->data->Order->Shipping->country; if (!empty($this->data->Order->Shipping->country)) $options['selected'] = $this->data->Order->Shipping->country; else if (empty($options['selected'])) $options['selected'] = $base['country']; $output = ''; return $output; break; case "same-shipping-address": $label = __("Same shipping address","Shopp"); if (isset($options['label'])) $label = $options['label']; $checked = ' checked="checked"'; if (isset($options['checked']) && !value_is_true($options['checked'])) $checked = ''; $output = ''; return $output; break; // BILLING TAGS case "billing-required": if ($this->data->Totals->total == 0) return false; if (isset($_GET['shopp_xco'])) { $xco = join(DIRECTORY_SEPARATOR,array($Shopp->path,'gateways',$_GET['shopp_xco'].".php")); if (file_exists($xco)) { $meta = $Shopp->Flow->scan_gateway_meta($xco); $PaymentSettings = $Shopp->Settings->get($meta->tags['class']); return ($PaymentSettings['billing-required'] != "off"); } } return ($this->data->Totals->total > 0); break; case "billing-address": if ($options['mode'] == "value") return $this->data->Order->Billing->address; if (!empty($this->data->Order->Billing->address)) $options['value'] = $this->data->Order->Billing->address; return ''; break; case "billing-xaddress": if ($options['mode'] == "value") return $this->data->Order->Billing->xaddress; if (!empty($this->data->Order->Billing->xaddress)) $options['value'] = $this->data->Order->Billing->xaddress; return ''; break; case "billing-city": if (!empty($this->data->Order->Billing->city)) $options['value'] = $this->data->Order->Billing->city; return ''; break; case "billing-province": case "billing-state": if ($options['mode'] == "value") return $this->data->Order->Billing->state; if (!isset($options['selected'])) $options['selected'] = false; if (!empty($this->data->Order->Billing->state)) { $options['selected'] = $this->data->Order->Billing->state; $options['value'] = $this->data->Order->Billing->state; } if (empty($options['type'])) $options['type'] = "menu"; $country = $base['country']; if (!empty($this->data->Order->Billing->country)) $country = $this->data->Order->Billing->country; if (!array_key_exists($country,$countries)) $country = key($countries); $regions = $Shopp->Settings->get('zones'); $states = $regions[$country]; if (is_array($states) && $options['type'] == "menu") { $label = (!empty($options['label']))?$options['label']:''; $output = ''; } else $output .= ''; return $output; break; case "billing-postcode": if ($options['mode'] == "value") return $this->data->Order->Billing->postcode; if (!empty($this->data->Order->Billing->postcode)) $options['value'] = $this->data->Order->Billing->postcode; return ''; break; case "billing-country": if ($options['mode'] == "value") return $this->data->Order->Billing->country; if (!empty($this->data->Order->Billing->country)) $options['selected'] = $this->data->Order->Billing->country; else if (empty($options['selected'])) $options['selected'] = $base['country']; $output = ''; return $output; break; case "billing-card": if ($options['mode'] == "value") return str_repeat('X',strlen($this->data->Order->Billing->card)-4) .substr($this->data->Order->Billing->card,-4); if (!empty($this->data->Order->Billing->card)) { $options['value'] = $this->data->Order->Billing->card; $this->data->Order->Billing->card = ""; } return ''; break; case "billing-cardexpires-mm": if ($options['mode'] == "value") return date("m",$this->data->Order->Billing->cardexpires); if (!empty($this->data->Order->Billing->cardexpires)) $options['value'] = date("m",$this->data->Order->Billing->cardexpires); return ''; break; case "billing-cardexpires-yy": if ($options['mode'] == "value") return date("y",$this->data->Order->Billing->cardexpires); if (!empty($this->data->Order->Billing->cardexpires)) $options['value'] = date("y",$this->data->Order->Billing->cardexpires); return ''; break; case "billing-cardtype": if ($options['mode'] == "value") return $this->data->Order->Billing->cardtype; if (!isset($options['selected'])) $options['selected'] = false; if (!empty($this->data->Order->Billing->cardtype)) $options['selected'] = $this->data->Order->Billing->cardtype; $cards = $Shopp->Settings->get('gateway_cardtypes'); $label = (!empty($options['label']))?$options['label']:''; $output = ''; return $output; break; case "billing-cardholder": if ($options['mode'] == "value") return $this->data->Order->Billing->cardholder; if (!empty($this->data->Order->Billing->cardholder)) $options['value'] = $this->data->Order->Billing->cardholder; return ''; break; case "billing-cvv": if (!empty($this->data->Order->Billing->cardholder)) $options['value'] = $_POST['billing']['cvv']; return ''; break; case "billing-xco": if (isset($_GET['shopp_xco'])) { if ($this->data->Totals->total == 0) return false; $xco = join(DIRECTORY_SEPARATOR,array($Shopp->path,'gateways',$_GET['shopp_xco'].".php")); if (file_exists($xco)) { $meta = $Shopp->Flow->scan_gateway_meta($xco); $ProcessorClass = $meta->tags['class']; include_once($xco); $Payment = new $ProcessorClass(); if (method_exists($Payment,'billing')) return $Payment->billing($options); } } break; case "has-data": case "hasdata": return (is_array($this->data->Order->data) && count($this->data->Order->data) > 0); break; case "order-data": case "orderdata": if (isset($options['name']) && $options['mode'] == "value") return $this->data->Order->data[$options['name']]; $allowed_types = array("text","hidden",'password','checkbox','radio','textarea'); $value_override = array("text","hidden","password","textarea"); if (empty($options['type'])) $options['type'] = "hidden"; if (isset($options['name']) && in_array($options['type'],$allowed_types)) { if (!isset($options['title'])) $options['title'] = $options['name']; if (in_array($options['type'],$value_override) && isset($this->data->Order->data[$options['name']])) $options['value'] = $this->data->Order->data[$options['name']]; if (!isset($options['cols'])) $options['cols'] = "30"; if (!isset($options['rows'])) $options['rows'] = "3"; if ($options['type'] == "textarea") return ''; return ''; } // Looping for data value output if (!$this->dataloop) { reset($this->data->Order->data); $this->dataloop = true; } else next($this->data->Order->data); if (current($this->data->Order->data) !== false) return true; else { $this->dataloop = false; return false; } break; case "data": if (!is_array($this->data->Order->data)) return false; $data = current($this->data->Order->data); $name = key($this->data->Order->data); if (isset($options['name'])) return $name; return $data; break; case "submit": if (!isset($options['value'])) $options['value'] = __('Submit Order','Shopp'); return ''; break; case "confirm-button": if (!isset($options['value'])) $options['value'] = __('Confirm Order','Shopp'); return ''; break; case "local-payment": return (!empty($gateway)); break; case "xco-buttons": if (!is_array($xcos)) return false; $buttons = ""; foreach ($xcos as $xco) { $xco = str_replace('/',DIRECTORY_SEPARATOR,$xco); $xcopath = join(DIRECTORY_SEPARATOR,array($Shopp->path,'gateways',$xco)); if (!file_exists($xcopath)) continue; $meta = $Shopp->Flow->scan_gateway_meta($xcopath); $ProcessorClass = $meta->tags['class']; if (!empty($ProcessorClass)) { $PaymentSettings = $Shopp->Settings->get($ProcessorClass); if ($PaymentSettings['enabled'] == "on") { include_once($xcopath); $Payment = new $ProcessorClass(); $buttons .= $Payment->tag('button',$options); } } } return $buttons; break; } } } // end Cart class ?>