PageRenderTime 38ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/blog/wp-content/plugins/shopp/core/Flow.php

https://github.com/kennethreitz-archive/wordpress-skeleton
PHP | 3010 lines | 2752 code | 198 blank | 60 comment | 225 complexity | 788fee78bad25f76bd4558933fed0612 MD5 | raw file
  1. <?php
  2. /**
  3. * Flow handlers
  4. * Main flow handling for all request processing/handling
  5. *
  6. * @author Jonathan Davis
  7. * @version 1.0
  8. * @copyright Ingenesis Limited, 2 April, 2008
  9. * @package Shopp
  10. **/
  11. class Flow {
  12. var $Admin;
  13. var $Settings;
  14. var $basepath;
  15. var $baseuri;
  16. var $secureuri;
  17. function Flow (&$Core) {
  18. global $wp_version;
  19. $this->Settings = $Core->Settings;
  20. $this->Cart = $Core->Cart;
  21. $langpath = array(PLUGINDIR,$Core->directory,'lang');
  22. load_plugin_textdomain('Shopp',join(DIRECTORY_SEPARATOR,$langpath));
  23. $this->basepath = dirname(dirname(__FILE__));
  24. $this->uri = ((!empty($_SERVER['HTTPS']))?"https://":"http://").
  25. $_SERVER['SERVER_NAME'].str_replace("?".$_SERVER['QUERY_STRING'],"",$_SERVER['REQUEST_URI']);
  26. $this->secureuri = 'https://'.$_SERVER['SERVER_NAME'].$this->uri;
  27. $this->Admin = new stdClass();
  28. $this->Admin->orders = $Core->directory."-orders";
  29. $this->Admin->customers = $Core->directory."-customers";
  30. $this->Admin->editcustomer = $Core->directory."-customers-edit";
  31. $this->Admin->categories = $Core->directory."-categories";
  32. $this->Admin->editcategory = $Core->directory."-categories-edit";
  33. $this->Admin->products = $Core->directory."-products";
  34. $this->Admin->editproduct = $Core->directory."-products-edit";
  35. $this->Admin->promotions = $Core->directory."-promotions";
  36. $this->Admin->editpromo = $Core->directory."-promotions-edit";
  37. $this->Admin->settings = array(
  38. 'settings' => array($Core->directory."-settings",__('General','Shopp')),
  39. 'checkout' => array($Core->directory."-settings-checkout",__('Checkout','Shopp')),
  40. 'payments' => array($Core->directory."-settings-payments",__('Payments','Shopp')),
  41. 'shipping' => array($Core->directory."-settings-shipping",__('Shipping','Shopp')),
  42. 'taxes' => array($Core->directory."-settings-taxes",__('Taxes','Shopp')),
  43. 'presentation' => array($Core->directory."-settings-presentation",__('Presentation','Shopp')),
  44. 'system' => array($Core->directory."-settings-system",__('System','Shopp')),
  45. 'update' => array($Core->directory."-settings-update",__('Update','Shopp'))
  46. );
  47. $this->Admin->help = $Core->directory."-help";
  48. $this->Admin->welcome = $Core->directory."-welcome";
  49. $this->Admin->default = $this->Admin->orders;
  50. $this->Pages = $Core->Settings->get('pages');
  51. if (empty($this->Pages)) {
  52. $this->Pages = array();
  53. $this->Pages['catalog'] = array('name'=>'shop','title'=>'Shop','content'=>'[catalog]');
  54. $this->Pages['cart'] = array('name'=>'cart','title'=>'Cart','content'=>'[cart]');
  55. $this->Pages['checkout'] = array('name'=>'checkout','title'=>'Checkout','content'=>'[checkout]');
  56. $this->Pages['account'] = array('name'=>'account','title'=>'Your Orders','content'=>'[account]');
  57. }
  58. $this->Docs = array(
  59. 'orders' => 'Managing Orders',
  60. 'customers' => 'Managing Customers',
  61. 'promotions' => 'Running Sales & Promotions',
  62. 'editpromos' => 'Running Sales & Promotions',
  63. 'products' => 'Editing a Product',
  64. 'editproducts' => 'Editing a Product',
  65. 'categories' => 'Editing a Category',
  66. 'editcategory' => 'Editing a Category',
  67. 'settings' => 'General Settings',
  68. 'checkout' => 'Checkout Settings',
  69. 'payments' => 'Payments Settings',
  70. 'shipping' => 'Shipping Settings',
  71. 'taxes' => 'Taxes Settings',
  72. 'presentation' => 'Presentation Settings',
  73. 'system' => 'System Settings',
  74. 'update' => 'Update Settings'
  75. );
  76. $this->coremods = array("GoogleCheckout.php", "PayPalExpress.php",
  77. "TestMode.php", "FlatRates.php", "ItemQuantity.php",
  78. "OrderAmount.php", "OrderWeight.php");
  79. if (!defined('BR')) define('BR','<br />');
  80. // Overrideable macros
  81. if (!defined('SHOPP_USERLEVEL')) define('SHOPP_USERLEVEL',8);
  82. if (!defined('SHOPP_NOSSL')) define('SHOPP_NOSSL',false);
  83. if (!defined('SHOPP_PREPAYMENT_DOWNLOADS')) define('SHOPP_PREPAYMENT_DOWNLOADS',false);
  84. if (!defined('SHOPP_SESSION_TIMEOUT')) define('SHOPP_SESSION_TIMEOUT',7200);
  85. if (!defined('SHOPP_QUERY_DEBUG')) define('SHOPP_QUERY_DEBUG',false);
  86. define("SHOPP_WP27",(!version_compare($wp_version,"2.7","<")));
  87. define("SHOPP_DEBUG",($Core->Settings->get('error_logging') == 2048));
  88. define("SHOPP_PATH",$this->basepath);
  89. define("SHOPP_ADMINPATH",SHOPP_PATH."/core/ui");
  90. define("SHOPP_PLUGINURI",$Core->uri);
  91. define("SHOPP_DBSCHEMA",SHOPP_PATH."/core/model/schema.sql");
  92. define("SHOPP_TEMPLATES",($Core->Settings->get('theme_templates') != "off" &&
  93. is_dir($Core->Settings->get('theme_templates')))?
  94. $Core->Settings->get('theme_templates'):
  95. SHOPP_PATH.DIRECTORY_SEPARATOR."templates");
  96. define("SHOPP_TEMPLATES_URI",($Core->Settings->get('theme_templates') != "off" &&
  97. is_dir($Core->Settings->get('theme_templates')))?
  98. get_bloginfo('stylesheet_directory')."/shopp":
  99. $Core->uri."/templates");
  100. define("SHOPP_GATEWAYS",SHOPP_PATH.DIRECTORY_SEPARATOR."gateways".DIRECTORY_SEPARATOR);
  101. define("SHOPP_PERMALINKS",(get_option('permalink_structure') == "")?false:true);
  102. define("SHOPP_LOOKUP",(strpos($_SERVER['REQUEST_URI'],"images/") !== false ||
  103. strpos($_SERVER['REQUEST_URI'],"lookup=") !== false)?true:false);
  104. $this->uploadErrors = array(
  105. UPLOAD_ERR_INI_SIZE => __('The uploaded file exceeds the upload_max_filesize directive in PHP\'s configuration file','Shopp'),
  106. UPLOAD_ERR_FORM_SIZE => __('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.','Shopp'),
  107. UPLOAD_ERR_PARTIAL => __('The uploaded file was only partially uploaded.','Shopp'),
  108. UPLOAD_ERR_NO_FILE => __('No file was uploaded.','Shopp'),
  109. UPLOAD_ERR_NO_TMP_DIR => __('The server\'s temporary folder is missing.','Shopp'),
  110. UPLOAD_ERR_CANT_WRITE => __('Failed to write the file to disk.','Shopp'),
  111. UPLOAD_ERR_EXTENSION => __('File upload stopped by extension.','Shopp'),
  112. );
  113. }
  114. function admin () {
  115. global $Shopp;
  116. $db =& DB::get();
  117. if (!defined('WP_ADMIN') || !isset($_GET['page'])) return;
  118. $Admin = $Shopp->Flow->Admin;
  119. $adminurl = $Shopp->wpadminurl."admin.php";
  120. $defaults = array(
  121. 'page' => false,
  122. 'deleting' => false,
  123. 'delete' => false,
  124. 'id' => false,
  125. 'save' => false,
  126. 'duplicate' => false,
  127. 'next' => false
  128. );
  129. $args = array_merge($defaults,$_REQUEST);
  130. extract($args,EXTR_SKIP);
  131. if (strstr($page,$Admin->categories)) {
  132. if ($page == "shopp-categories"
  133. && !empty($deleting)
  134. && !empty($delete)
  135. && is_array($delete)) {
  136. foreach($delete as $deletion) {
  137. $Category = new Category($deletion);
  138. $db->query("UPDATE $Category->_table SET parent=0 WHERE parent=$Category->id");
  139. $Category->delete();
  140. }
  141. $redirect = esc_url(add_query_arg(array_merge($_GET,array('delete[]'=>null,'deleting'=>null)),$adminurl));
  142. shopp_redirect($redirect);
  143. }
  144. if ($id && $id != "new")
  145. $Shopp->Category = new Category($id);
  146. else $Shopp->Category = new Category();
  147. if ($save) {
  148. $this->save_category($Shopp->Category);
  149. $this->Notice = '<strong>'.stripslashes($Shopp->Category->name).'</strong> '.__('has been saved.','Shopp');
  150. if ($next) {
  151. if ($next != "new")
  152. $Shopp->Category = new Category($next);
  153. else $Shopp->Category = new Category();
  154. } else {
  155. if (empty($id)) $id = $Shopp->Category->id;
  156. $Shopp->Category = new Category($id);
  157. }
  158. }
  159. } // end $Admin->categories
  160. if (strstr($page,$Admin->products)) {
  161. if ($page == "shopp-products"
  162. && !empty($deleting)
  163. && !empty($delete)
  164. && is_array($delete)) {
  165. foreach($delete as $deletion) {
  166. $Product = new Product($deletion);
  167. $Product->delete();
  168. }
  169. $redirect = esc_url(add_query_arg(array_merge($_GET,array('delete'=>null,'deleting'=>null)),$adminurl));
  170. shopp_redirect($redirect);
  171. exit();
  172. }
  173. if ($duplicate) {
  174. $Product = new Product();
  175. $Product->load($duplicate);
  176. $Product->duplicate();
  177. shopp_redirect(add_query_arg('page',$Admin->products,$adminurl));
  178. }
  179. if ($id && $id != "new") {
  180. $Shopp->Product = new Product($id);
  181. $Shopp->Product->load_data(array('prices','specs','categories','tags'));
  182. } else {
  183. $Shopp->Product = new Product();
  184. $Shopp->Product->published = "on";
  185. }
  186. if ($save) {
  187. $this->save_product($Shopp->Product);
  188. $this->Notice = '<strong>'.stripslashes($Shopp->Product->name).'</strong> '.__('has been saved.','Shopp');
  189. if ($next) {
  190. if ($next == "new") {
  191. $Shopp->Product = new Product();
  192. $Shopp->Product->published = "on";
  193. } else {
  194. $Shopp->Product = new Product($next);
  195. $Shopp->Product->load_data(array('prices','specs','categories','tags'));
  196. }
  197. } else {
  198. if (empty($id)) $id = $Shopp->Product->id;
  199. $Shopp->Product = new Product($id);
  200. $Shopp->Product->load_data(array('prices','specs','categories','tags'));
  201. }
  202. }
  203. } // end $Admin->products
  204. }
  205. function helpdoc ($menu,$page) {
  206. if (!isset($this->Docs[$menu])) return;
  207. $url = SHOPP_DOCS.str_replace("+","_",urlencode($this->Docs[$menu]));
  208. $link = htmlspecialchars($this->Docs[$menu]);
  209. $content = '<a href="'.$url.'" target="_blank">'.$link.'</a>';
  210. if ($menu == "orders" || $menu == "customers") {
  211. ob_start();
  212. include("{$this->basepath}/core/ui/help/$menu.php");
  213. $help = ob_get_contents();
  214. ob_end_clean();
  215. $content .= $help;
  216. }
  217. add_contextual_help($page,$content);
  218. }
  219. /**
  220. * Catalog flow handlers
  221. **/
  222. function catalog () {
  223. global $Shopp;
  224. if (SHOPP_DEBUG) new ShoppError('Displaying catalog page request: '.$_SERVER['REQUEST_URI'],'shopp_catalog',SHOPP_DEBUG_ERR);
  225. ob_start();
  226. switch ($Shopp->Catalog->type) {
  227. case "product":
  228. if (file_exists(SHOPP_TEMPLATES."/product-{$Shopp->Product->id}.php"))
  229. include(SHOPP_TEMPLATES."/product-{$Shopp->Product->id}.php");
  230. else include(SHOPP_TEMPLATES."/product.php"); break;
  231. case "category":
  232. if (isset($Shopp->Category->smart) &&
  233. file_exists(SHOPP_TEMPLATES."/category-{$Shopp->Category->slug}.php"))
  234. include(SHOPP_TEMPLATES."/category-{$Shopp->Category->slug}.php");
  235. elseif (isset($Shopp->Category->id) &&
  236. file_exists(SHOPP_TEMPLATES."/category-{$Shopp->Category->id}.php"))
  237. include(SHOPP_TEMPLATES."/category-{$Shopp->Category->id}.php");
  238. else include(SHOPP_TEMPLATES."/category.php"); break;
  239. default: include(SHOPP_TEMPLATES."/catalog.php"); break;
  240. }
  241. $content = ob_get_contents();
  242. ob_end_clean();
  243. $classes = $Shopp->Catalog->type;
  244. if (!isset($_COOKIE['shopp_catalog_view'])) {
  245. // No cookie preference exists, use shopp default setting
  246. $view = $Shopp->Settings->get('default_catalog_view');
  247. if ($view == "list") $classes .= " list";
  248. if ($view == "grid") $classes .= " grid";
  249. } else {
  250. if ($_COOKIE['shopp_catalog_view'] == "list") $classes .= " list";
  251. if ($_COOKIE['shopp_catalog_view'] == "grid") $classes .= " grid";
  252. }
  253. return apply_filters('shopp_catalog','<div id="shopp" class="'.$classes.'">'.$content.'<div class="clear"></div></div>');
  254. }
  255. /**
  256. * Shopping Cart flow handlers
  257. **/
  258. function cart ($attrs=array()) {
  259. $Cart = $this->Cart;
  260. ob_start();
  261. include(SHOPP_TEMPLATES."/cart.php");
  262. $content = ob_get_contents();
  263. ob_end_clean();
  264. return apply_filters('shopp_cart_template','<div id="shopp">'.$content.'</div>');
  265. }
  266. function shipping_estimate ($attrs) {
  267. $Cart = $this->Cart;
  268. ob_start();
  269. include(SHOPP_TEMPLATES."/shipping.php");
  270. $content = ob_get_contents();
  271. ob_end_clean();
  272. return $content;
  273. }
  274. /**
  275. * Checkout flow handlers
  276. **/
  277. function checkout () {
  278. global $Shopp;
  279. $Cart = $Shopp->Cart;
  280. $process = get_query_var('shopp_proc');
  281. $xco = get_query_var('shopp_xco');
  282. if (!empty($xco)) {
  283. $Shopp->gateway($xco);
  284. $Shopp->Gateway->actions();
  285. }
  286. switch ($process) {
  287. case "confirm-order": $content = $this->order_confirmation(); break;
  288. case "receipt": $content = $this->order_receipt(); break;
  289. default:
  290. ob_start();
  291. if ($Cart->data->Errors->exist(SHOPP_COMM_ERR)) {
  292. include(SHOPP_TEMPLATES."/errors.php");
  293. $Cart->data->Errors->reset();
  294. }
  295. if (!empty($xco)) {
  296. if (!empty($Shopp->Gateway)) {
  297. if ($Shopp->Gateway->checkout) include(SHOPP_TEMPLATES."/checkout.php");
  298. else {
  299. if ($Cart->data->Errors->exist(SHOPP_COMM_ERR))
  300. include(SHOPP_TEMPLATES."/errors.php");
  301. include(SHOPP_TEMPLATES."/summary.php");
  302. echo $Shopp->Gateway->tag('button');
  303. }
  304. } else include(SHOPP_TEMPLATES."/summary.php");
  305. } else include(SHOPP_TEMPLATES."/checkout.php");
  306. $content = ob_get_contents();
  307. ob_end_clean();
  308. unset($Cart->data->OrderError);
  309. }
  310. // Wrap with #shopp if not already wrapped
  311. if (strpos($content,'<div id="shopp">') === false)
  312. $content = '<div id="shopp">'.$content.'</div>';
  313. return apply_filters('shopp_checkout',$content);
  314. }
  315. function checkout_order_summary () {
  316. global $Shopp;
  317. $Cart = $Shopp->Cart;
  318. ob_start();
  319. include(SHOPP_TEMPLATES."/summary.php");
  320. $content = ob_get_contents();
  321. ob_end_clean();
  322. return apply_filters('shopp_order_summary',$content);
  323. }
  324. function secure_page_links ($linklist) {
  325. global $Shopp;
  326. $gateway = $Shopp->Settings->get('payment_gateway');
  327. if (strpos($gateway,"TestMode.php") !== false) return $linklist;
  328. $hrefs = array(
  329. 'checkout' => $Shopp->link('checkout'),
  330. 'account' => $Shopp->link('account')
  331. );
  332. if (empty($gateway)) return str_replace($hrefs['checkout'],$Shopp->link('cart'),$linklist);
  333. foreach ($hrefs as $href) {
  334. $secure_href = str_replace("http://","https://",$href);
  335. $linklist = str_replace($href,$secure_href,$linklist);
  336. }
  337. return $linklist;
  338. }
  339. /**
  340. * order()
  341. * Processes orders by passing transaction information to the active
  342. * payment gateway */
  343. function order ($gateway = false) {
  344. global $Shopp;
  345. $Cart = $Shopp->Cart;
  346. $db = DB::get();
  347. do_action('shopp_order_preprocessing');
  348. $Order = $Shopp->Cart->data->Order;
  349. $Order->Totals = $Shopp->Cart->data->Totals;
  350. $Order->Items = $Shopp->Cart->contents;
  351. $Order->Cart = $Shopp->Cart->session;
  352. if ($Shopp->Gateway && !$Cart->orderisfree()) {
  353. // Use an external checkout payment gateway
  354. if (SHOPP_DEBUG) new ShoppError('Processing order through a remote-payment gateway service.',false,SHOPP_DEBUG_ERR);
  355. $Purchase = $Shopp->Gateway->process();
  356. if (!$Purchase) {
  357. if (SHOPP_DEBUG) new ShoppError('The remote-payment gateway encountered an error.',false,SHOPP_DEBUG_ERR);
  358. $Shopp->Gateway->error();
  359. return false;
  360. }
  361. if (SHOPP_DEBUG) new ShoppError('Transaction successfully processed by remote-payment gateway service.',false,SHOPP_DEBUG_ERR);
  362. } else {
  363. // Use local payment gateway set in payment settings
  364. $gateway = $Shopp->Settings->get('payment_gateway');
  365. // Process a transaction if the order has a cost (is not free)
  366. if (!$Cart->orderisfree()) {
  367. if (!$Shopp->gateway($gateway)) return false;
  368. // Process the transaction through the payment gateway
  369. if (SHOPP_DEBUG) new ShoppError('Processing order through local-payment gateway service.',false,SHOPP_DEBUG_ERR);
  370. $processed = $Shopp->Gateway->process();
  371. // exit();
  372. // There was a problem processing the transaction,
  373. // grab the error response from the gateway so we can report it
  374. if (!$processed) {
  375. if (SHOPP_DEBUG) new ShoppError('The local-payment gateway encountered an error.',false,SHOPP_DEBUG_ERR);
  376. $Shopp->Gateway->error();
  377. return false;
  378. }
  379. $gatewaymeta = $this->scan_gateway_meta(SHOPP_GATEWAYS.$gateway);
  380. $gatewayname = $gatewaymeta->name;
  381. $transactionid = $Shopp->Gateway->transactionid();
  382. if (SHOPP_DEBUG) new ShoppError('Transaction '.$transactionid.' successfully processed by local-payment gateway service '.$gatewayname.'.',false,SHOPP_DEBUG_ERR);
  383. } else {
  384. if(!$Cart->validorder()){
  385. new ShoppError(__('There is not enough customer information to process the order.','Shopp'),'invalid_order',SHOPP_TRXN_ERR);
  386. return false;
  387. }
  388. $gatewayname = __('N/A','Shopp');
  389. $transactionid = __('(Free Order)','Shopp');
  390. }
  391. $authentication = $Shopp->Settings->get('account_system');
  392. // Transaction successful, save the order
  393. if ($authentication == "wordpress") {
  394. // Check if they've logged in
  395. // If the shopper is already logged-in, save their updated customer info
  396. if ($Shopp->Cart->data->login) {
  397. if (SHOPP_DEBUG) new ShoppError('Customer logged in, linking Shopp customer account to existing WordPress account.',false,SHOPP_DEBUG_ERR);
  398. get_currentuserinfo();
  399. global $user_ID;
  400. $Order->Customer->wpuser = $user_ID;
  401. }
  402. // Create WordPress account (if necessary)
  403. if (!$Order->Customer->wpuser) {
  404. if (SHOPP_DEBUG) new ShoppError('Creating a new WordPress account for this customer.',false,SHOPP_DEBUG_ERR);
  405. if(!$Order->Customer->new_wpuser()) new ShoppError(__('Account creation failed on order for customer id:' . $Order->Customer->id, "Shopp"), false,SHOPP_TRXN_ERR);
  406. }
  407. }
  408. // Create a WP-compatible password hash to go in the db
  409. if (empty($Order->Customer->id))
  410. $Order->Customer->password = wp_hash_password($Order->Customer->password);
  411. $Order->Customer->save();
  412. $Order->Billing->customer = $Order->Customer->id;
  413. $Order->Billing->card = substr($Order->Billing->card,-4);
  414. $Order->Billing->save();
  415. // Card data is truncated, switch the cart to normal mode
  416. if ($Shopp->Cart->secured() && is_shopp_secure())
  417. $Shopp->Cart->secured(false);
  418. if (!empty($Order->Shipping->address)) {
  419. $Order->Shipping->customer = $Order->Customer->id;
  420. $Order->Shipping->save();
  421. }
  422. $Promos = array();
  423. foreach ($Shopp->Cart->data->PromosApplied as $promo)
  424. $Promos[$promo->id] = $promo->name;
  425. if ($Shopp->Cart->orderisfree()) $orderisfree = true;
  426. else $orderisfree = false;
  427. $Purchase = new Purchase();
  428. $Purchase->customer = $Order->Customer->id;
  429. $Purchase->billing = $Order->Billing->id;
  430. $Purchase->shipping = $Order->Shipping->id;
  431. $Purchase->copydata($Order->Customer);
  432. $Purchase->copydata($Order->Billing);
  433. $Purchase->copydata($Order->Shipping,'ship');
  434. $Purchase->copydata($Shopp->Cart->data->Totals);
  435. $Purchase->data = $Order->data;
  436. $Purchase->promos = $Promos;
  437. $Purchase->freight = $Shopp->Cart->data->Totals->shipping;
  438. $Purchase->gateway = $gatewayname;
  439. $Purchase->transactionid = $transactionid;
  440. $Purchase->transtatus = "CHARGED";
  441. $Purchase->ip = $Shopp->Cart->ip;
  442. $Purchase->save();
  443. // echo "<pre>"; print_r($Purchase); echo "</pre>";
  444. foreach($Shopp->Cart->contents as $Item) {
  445. $Purchased = new Purchased();
  446. $Purchased->copydata($Item);
  447. $Purchased->purchase = $Purchase->id;
  448. if (!empty($Purchased->download)) $Purchased->keygen();
  449. $Purchased->save();
  450. if ($Item->inventory) $Item->unstock();
  451. }
  452. if (SHOPP_DEBUG) new ShoppError('Purchase '.$Purchase->id.' was successfully saved to the database.',false,SHOPP_DEBUG_ERR);
  453. }
  454. // Skip post order if no Purchase ID exists
  455. if (empty($Purchase->id)) return true;
  456. // Empty cart on successful order
  457. $Shopp->Cart->unload();
  458. session_destroy();
  459. // Start new cart session
  460. $Shopp->Cart = new Cart();
  461. session_start();
  462. // Keep the user logged in or log them in if they are a new customer
  463. if ($Shopp->Cart->data->login || $authentication != "none")
  464. $Shopp->Cart->loggedin($Order->Customer);
  465. // Save the purchase ID for later lookup
  466. $Shopp->Cart->data->Purchase = new Purchase($Purchase->id);
  467. $Shopp->Cart->data->Purchase->load_purchased();
  468. // // $Shopp->Cart->save();
  469. // Allow other WordPress plugins access to Purchase data to extend
  470. // what Shopp does after a successful transaction
  471. do_action_ref_array('shopp_order_success',array(&$Shopp->Cart->data->Purchase));
  472. // Send email notifications
  473. // notification(addressee name, email, subject, email template, receipt template)
  474. $Purchase->notification(
  475. "$Purchase->firstname $Purchase->lastname",
  476. $Purchase->email,
  477. __('Order Receipt','Shopp')
  478. );
  479. if ($Shopp->Settings->get('receipt_copy') == 1) {
  480. $Purchase->notification(
  481. '',
  482. $Shopp->Settings->get('merchant_email'),
  483. __('New Order','Shopp')
  484. );
  485. }
  486. $ssl = true;
  487. // Test Mode will not require encrypted checkout
  488. if (strpos($gateway,"TestMode.php") !== false
  489. || isset($_GET['shopp_xco'])
  490. || $orderisfree
  491. || SHOPP_NOSSL)
  492. $ssl = false;
  493. shopp_redirect($Shopp->link('receipt',$ssl));
  494. }
  495. // Display the confirm order screen
  496. function order_confirmation () {
  497. global $Shopp;
  498. $Cart = $Shopp->Cart;
  499. ob_start();
  500. include(SHOPP_TEMPLATES."/confirm.php");
  501. $content = ob_get_contents();
  502. ob_end_clean();
  503. return apply_filters('shopp_order_confirmation','<div id="shopp">'.$content.'</div>');
  504. }
  505. // Display a sales receipt
  506. function order_receipt ($template="receipt.php") {
  507. global $Shopp;
  508. $Cart = $Shopp->Cart;
  509. ob_start();
  510. include(trailingslashit(SHOPP_TEMPLATES).$template);
  511. $content = ob_get_contents();
  512. ob_end_clean();
  513. return apply_filters('shopp_order_receipt','<div id="shopp">'.$content.'</div>');
  514. }
  515. // Display an error page
  516. function error_page ($template="errors.php") {
  517. global $Shopp;
  518. $Cart = $Shopp->Cart;
  519. ob_start();
  520. include(trailingslashit(SHOPP_TEMPLATES).$template);
  521. $content = ob_get_contents();
  522. ob_end_clean();
  523. return apply_filters('shopp_errors_page','<div id="shopp">'.$content.'</div>');
  524. }
  525. /**
  526. * Orders admin flow handlers
  527. */
  528. function orders_list() {
  529. global $Shopp,$Orders;
  530. $db = DB::get();
  531. $defaults = array(
  532. 'page' => false,
  533. 'deleting' => false,
  534. 'selected' => false,
  535. 'update' => false,
  536. 'newstatus' => false,
  537. 'pagenum' => 1,
  538. 'per_page' => false,
  539. 'start' => '',
  540. 'end' => '',
  541. 'status' => false,
  542. 's' => '',
  543. 'range' => '',
  544. 'startdate' => '',
  545. 'enddate' => '',
  546. );
  547. $args = array_merge($defaults,$_GET);
  548. extract($args, EXTR_SKIP);
  549. if ( !current_user_can(SHOPP_USERLEVEL) )
  550. wp_die(__('You do not have sufficient permissions to access this page.','Shopp'));
  551. if ($page == "shopp-orders"
  552. && !empty($deleting)
  553. && !empty($selected)
  554. && is_array($selected)) {
  555. foreach($selected as $selection) {
  556. $Purchase = new Purchase($selection);
  557. $Purchase->load_purchased();
  558. foreach ($Purchase->purchased as $purchased) {
  559. $Purchased = new Purchased($purchased->id);
  560. $Purchased->delete();
  561. }
  562. $Purchase->delete();
  563. }
  564. }
  565. $statusLabels = $this->Settings->get('order_status');
  566. if (empty($statusLabels)) $statusLabels = array('');
  567. $txnStatusLabels = array(
  568. 'PENDING' => __('Pending','Shopp'),
  569. 'CHARGED' => __('Charged','Shopp'),
  570. 'REFUNDED' => __('Refunded','Shopp'),
  571. 'VOID' => __('Void','Shopp')
  572. );
  573. if ($update == "order"
  574. && !empty($selected)
  575. && is_array($selected)) {
  576. foreach($selected as $selection) {
  577. $Purchase = new Purchase($selection);
  578. $Purchase->status = $newstatus;
  579. $Purchase->save();
  580. }
  581. }
  582. $Purchase = new Purchase();
  583. if (!empty($start)) {
  584. $startdate = $start;
  585. list($month,$day,$year) = explode("/",$startdate);
  586. $starts = mktime(0,0,0,$month,$day,$year);
  587. }
  588. if (!empty($end)) {
  589. $enddate = $end;
  590. list($month,$day,$year) = explode("/",$enddate);
  591. $ends = mktime(23,59,59,$month,$day,$year);
  592. }
  593. $pagenum = absint( $pagenum );
  594. if ( empty($pagenum) )
  595. $pagenum = 1;
  596. if( !$per_page || $per_page < 0 )
  597. $per_page = 20;
  598. $start = ($per_page * ($pagenum-1));
  599. $where = '';
  600. if (!empty($status) || $status === '0') $where = "WHERE status='$status'";
  601. if (!empty($s)) {
  602. $s = stripslashes($s);
  603. if (preg_match_all('/(\w+?)\:(?="(.+?)"|(.+?)\b)/',$s,$props,PREG_SET_ORDER) > 0) {
  604. foreach ($props as $search) {
  605. $keyword = !empty($search[2])?$search[2]:$search[3];
  606. switch(strtolower($search[1])) {
  607. case "txn": $where .= (empty($where)?"WHERE ":" AND ")."transactionid='$keyword'"; break;
  608. case "gateway": $where .= (empty($where)?"WHERE ":" AND ")."gateway LIKE '%$keyword%'"; break;
  609. case "cardtype": $where .= ((empty($where))?"WHERE ":" AND ")."cardtype LIKE '%$keyword%'"; break;
  610. case "address": $where .= ((empty($where))?"WHERE ":" AND ")."(address LIKE '%$keyword%' OR xaddress='%$keyword%')"; break;
  611. case "city": $where .= ((empty($where))?"WHERE ":" AND ")."city LIKE '%$keyword%'"; break;
  612. case "province":
  613. case "state": $where .= ((empty($where))?"WHERE ":" AND ")."state='$keyword'"; break;
  614. case "zip":
  615. case "zipcode":
  616. case "postcode": $where .= ((empty($where))?"WHERE ":" AND ")."postcode='$keyword'"; break;
  617. case "country": $where .= ((empty($where))?"WHERE ":" AND ")."country='$keyword'"; break;
  618. }
  619. }
  620. if (empty($where)) $where .= ((empty($where))?"WHERE ":" AND ")." (id='$s' OR CONCAT(firstname,' ',lastname) LIKE '%$s%')";
  621. } elseif (strpos($s,'@') !== false) {
  622. $where .= ((empty($where))?"WHERE ":" AND ")." email='$s'";
  623. } else $where .= ((empty($where))?"WHERE ":" AND ")." (id='$s' OR CONCAT(firstname,' ',lastname) LIKE '%$s%')";
  624. }
  625. if (!empty($starts) && !empty($ends)) $where .= ((empty($where))?"WHERE ":" AND ").' (UNIX_TIMESTAMP(created) >= '.$starts.' AND UNIX_TIMESTAMP(created) <= '.$ends.')';
  626. $ordercount = $db->query("SELECT count(*) as total,SUM(total) AS sales,AVG(total) AS avgsale FROM $Purchase->_table $where ORDER BY created DESC");
  627. $query = "SELECT * FROM $Purchase->_table $where ORDER BY created DESC LIMIT $start,$per_page";
  628. $Orders = $db->query($query,AS_ARRAY);
  629. $num_pages = ceil($ordercount->total / $per_page);
  630. $page_links = paginate_links( array(
  631. 'base' => add_query_arg( 'pagenum', '%#%' ),
  632. 'format' => '',
  633. 'total' => $num_pages,
  634. 'current' => $pagenum
  635. ));
  636. $ranges = array(
  637. 'all' => __('Show All Orders','Shopp'),
  638. 'today' => __('Today','Shopp'),
  639. 'week' => __('This Week','Shopp'),
  640. 'month' => __('This Month','Shopp'),
  641. 'quarter' => __('This Quarter','Shopp'),
  642. 'year' => __('This Year','Shopp'),
  643. 'yesterday' => __('Yesterday','Shopp'),
  644. 'lastweek' => __('Last Week','Shopp'),
  645. 'last30' => __('Last 30 Days','Shopp'),
  646. 'last90' => __('Last 3 Months','Shopp'),
  647. 'lastmonth' => __('Last Month','Shopp'),
  648. 'lastquarter' => __('Last Quarter','Shopp'),
  649. 'lastyear' => __('Last Year','Shopp'),
  650. 'lastexport' => __('Last Export','Shopp'),
  651. 'custom' => __('Custom Dates','Shopp')
  652. );
  653. $exports = array(
  654. 'tab' => __('Tab-separated.txt','Shopp'),
  655. 'csv' => __('Comma-separated.csv','Shopp'),
  656. 'xls' => __('Microsoft&reg; Excel.xls','Shopp'),
  657. 'iif' => __('Intuit&reg; QuickBooks.iif','Shopp')
  658. );
  659. $formatPref = $Shopp->Settings->get('purchaselog_format');
  660. if (!$formatPref) $formatPref = 'tab';
  661. $columns = array_merge(Purchase::exportcolumns(),Purchased::exportcolumns());
  662. $selected = $Shopp->Settings->get('purchaselog_columns');
  663. if (empty($selected)) $selected = array_keys($columns);
  664. include("{$this->basepath}/core/ui/orders/orders.php");
  665. }
  666. function orders_list_columns () {
  667. shopp_register_column_headers('toplevel_page_shopp-orders', array(
  668. 'cb'=>'<input type="checkbox" />',
  669. 'order'=>__('Order','Shopp'),
  670. 'name'=>__('Name','Shopp'),
  671. 'destination'=>__('Destination','Shopp'),
  672. 'total'=>__('Total','Shopp'),
  673. 'txn'=>__('Transaction','Shopp'),
  674. 'date'=>__('Date','Shopp'))
  675. );
  676. }
  677. function order_manager () {
  678. global $Shopp;
  679. global $is_IIS;
  680. if ( !current_user_can(SHOPP_USERLEVEL) )
  681. wp_die(__('You do not have sufficient permissions to access this page.','Shopp'));
  682. if (preg_match("/\d+/",$_GET['id'])) {
  683. $Shopp->Cart->data->Purchase = new Purchase($_GET['id']);
  684. $Shopp->Cart->data->Purchase->load_purchased();
  685. } else $Shopp->Cart->data->Purchase = new Purchase();
  686. $Purchase = $Shopp->Cart->data->Purchase;
  687. $Customer = new Customer($Purchase->customer);
  688. if (!empty($_POST['update'])) {
  689. check_admin_referer('shopp-save-order');
  690. if ($_POST['transtatus'] != $Purchase->transtatus)
  691. do_action_ref_array('shopp_order_txnstatus_update',array(&$_POST['transtatus'],&$Purchase));
  692. $Purchase->updates($_POST);
  693. if ($_POST['notify'] == "yes") {
  694. $labels = $this->Settings->get('order_status');
  695. // Send the e-mail notification
  696. $notification = array();
  697. $notification['from'] = $Shopp->Settings->get('merchant_email');
  698. if($is_IIS) $notification['to'] = $Purchase->email;
  699. else $notification['to'] = "\"{$Purchase->firstname} {$Purchase->lastname}\" <{$Purchase->email}>";
  700. $notification['subject'] = __('Order Updated','Shopp');
  701. $notification['url'] = get_bloginfo('siteurl');
  702. $notification['sitename'] = get_bloginfo('name');
  703. if ($_POST['receipt'] == "yes")
  704. $notification['receipt'] = $this->order_receipt();
  705. $notification['status'] = strtoupper($labels[$Purchase->status]);
  706. $notification['message'] = wpautop($_POST['message']);
  707. shopp_email(SHOPP_TEMPLATES."/notification.html",$notification);
  708. }
  709. $Purchase->save();
  710. $updated = __('Order status updated.','Shopp');
  711. }
  712. $targets = $this->Settings->get('target_markets');
  713. $txnStatusLabels = array(
  714. 'PENDING' => __('Pending','Shopp'),
  715. 'CHARGED' => __('Charged','Shopp'),
  716. 'REFUNDED' => __('Refunded','Shopp'),
  717. 'VOID' => __('Void','Shopp')
  718. );
  719. $statusLabels = $this->Settings->get('order_status');
  720. if (empty($statusLabels)) $statusLabels = array('');
  721. $taxrate = 0;
  722. $base = $Shopp->Settings->get('base_operations');
  723. if ($base['vat']) $taxrate = $Shopp->Cart->taxrate();
  724. include("{$this->basepath}/core/ui/orders/order.php");
  725. }
  726. function order_status_counts () {
  727. $db = DB::get();
  728. $purchase_table = DatabaseObject::tablename(Purchase::$table);
  729. $labels = $this->Settings->get('order_status');
  730. if (empty($labels)) return false;
  731. $r = $db->query("SELECT status,COUNT(status) AS total FROM $purchase_table GROUP BY status ORDER BY status ASC",AS_ARRAY);
  732. $status = array();
  733. foreach ($r as $count) $status[$count->status] = $count->total;
  734. foreach ($labels as $id => $label) if (empty($status[$id])) $status[$id] = 0;
  735. return $status;
  736. }
  737. function account () {
  738. global $Shopp,$wp;
  739. if ($Shopp->Cart->data->login
  740. && isset($Shopp->Cart->data->Order->Customer))
  741. $Shopp->Cart->data->Order->Customer->management();
  742. if (isset($_GET['acct']) && $_GET['acct'] == "rp") $Shopp->Cart->data->Order->Customer->reset_password($_GET['key']);
  743. if (isset($_POST['recover-login'])) $Shopp->Cart->data->Order->Customer->recovery();
  744. ob_start();
  745. if (isset($wp->query_vars['shopp_download'])) include(SHOPP_TEMPLATES."/errors.php");
  746. elseif ($Shopp->Cart->data->login) include(SHOPP_TEMPLATES."/account.php");
  747. else include(SHOPP_TEMPLATES."/login.php");
  748. $content = ob_get_contents();
  749. ob_end_clean();
  750. return apply_filters('shopp_account_template','<div id="shopp">'.$content.'</div>');
  751. }
  752. function customers_list () {
  753. global $Shopp,$Customers,$wpdb;
  754. $db = DB::get();
  755. $defaults = array(
  756. 'page' => false,
  757. 'deleting' => false,
  758. 'selected' => false,
  759. 'update' => false,
  760. 'newstatus' => false,
  761. 'pagenum' => 1,
  762. 'per_page' => false,
  763. 'start' => '',
  764. 'end' => '',
  765. 'status' => false,
  766. 's' => '',
  767. 'range' => '',
  768. 'startdate' => '',
  769. 'enddate' => '',
  770. );
  771. $args = array_merge($defaults,$_GET);
  772. extract($args, EXTR_SKIP);
  773. if ($page == "shopp-customers"
  774. && !empty($deleting)
  775. && !empty($selected)
  776. && is_array($selected)) {
  777. foreach($selected as $deletion) {
  778. $Customer = new Customer($deletion);
  779. $Billing = new Billing($Customer->id,'customer');
  780. $Billing->delete();
  781. $Shipping = new Shipping($Customer->id,'customer');
  782. $Shipping->delete();
  783. $Customer->delete();
  784. }
  785. }
  786. if (!empty($_POST['save'])) {
  787. check_admin_referer('shopp-save-customer');
  788. if ($_POST['id'] != "new") {
  789. $Customer = new Customer($_POST['id']);
  790. $Billing = new Billing($Customer->id,'customer');
  791. $Shipping = new Shipping($Customer->id,'customer');
  792. } else $Customer = new Customer();
  793. $Customer->updates($_POST);
  794. if (!empty($_POST['new-password']) && !empty($_POST['confirm-password'])
  795. && $_POST['new-password'] == $_POST['confirm-password']) {
  796. $Customer->password = wp_hash_password($_POST['new-password']);
  797. if (!empty($Customer->wpuser)) wp_set_password($_POST['new-password'], $Customer->wpuser);
  798. }
  799. $Customer->save();
  800. $Billing->updates($_POST['billing']);
  801. $Billing->save();
  802. $Shipping->updates($_POST['shipping']);
  803. $Shipping->save();
  804. }
  805. $pagenum = absint( $pagenum );
  806. if ( empty($pagenum) )
  807. $pagenum = 1;
  808. if( !$per_page || $per_page < 0 )
  809. $per_page = 20;
  810. $index = ($per_page * ($pagenum-1));
  811. if (!empty($start)) {
  812. $startdate = $start;
  813. list($month,$day,$year) = explode("/",$startdate);
  814. $starts = mktime(0,0,0,$month,$day,$year);
  815. }
  816. if (!empty($end)) {
  817. $enddate = $end;
  818. list($month,$day,$year) = explode("/",$enddate);
  819. $ends = mktime(23,59,59,$month,$day,$year);
  820. }
  821. $customer_table = DatabaseObject::tablename(Customer::$table);
  822. $billing_table = DatabaseObject::tablename(Billing::$table);
  823. $purchase_table = DatabaseObject::tablename(Purchase::$table);
  824. $users_table = $wpdb->users;
  825. $where = '';
  826. if (!empty($s)) {
  827. $s = stripslashes($s);
  828. if (preg_match_all('/(\w+?)\:(?="(.+?)"|(.+?)\b)/',$s,$props,PREG_SET_ORDER)) {
  829. foreach ($props as $search) {
  830. $keyword = !empty($search[2])?$search[2]:$search[3];
  831. switch(strtolower($search[1])) {
  832. case "company": $where .= ((empty($where))?"WHERE ":" AND ")."c.company LIKE '%$keyword%'"; break;
  833. case "login": $where .= ((empty($where))?"WHERE ":" AND ")."u.user_login LIKE '%$keyword%'"; break;
  834. case "address": $where .= ((empty($where))?"WHERE ":" AND ")."(b.address LIKE '%$keyword%' OR b.xaddress='%$keyword%')"; break;
  835. case "city": $where .= ((empty($where))?"WHERE ":" AND ")."b.city LIKE '%$keyword%'"; break;
  836. case "province":
  837. case "state": $where .= ((empty($where))?"WHERE ":" AND ")."b.state='$keyword'"; break;
  838. case "zip":
  839. case "zipcode":
  840. case "postcode": $where .= ((empty($where))?"WHERE ":" AND ")."b.postcode='$keyword'"; break;
  841. case "country": $where .= ((empty($where))?"WHERE ":" AND ")."b.country='$keyword'"; break;
  842. }
  843. }
  844. } elseif (strpos($s,'@') !== false) {
  845. $where .= ((empty($where))?"WHERE ":" AND ")."c.email='$s'";
  846. } else $where .= ((empty($where))?"WHERE ":" AND ")." (c.id='$s' OR CONCAT(c.firstname,' ',c.lastname) LIKE '%$s%' OR c.company LIKE '%$s%')";
  847. }
  848. if (!empty($starts) && !empty($ends)) $where .= ((empty($where))?"WHERE ":" AND ").' (UNIX_TIMESTAMP(c.created) >= '.$starts.' AND UNIX_TIMESTAMP(c.created) <= '.$ends.')';
  849. $customercount = $db->query("SELECT count(*) as total FROM $customer_table AS c $where");
  850. $query = "SELECT c.*,b.city,b.state,b.country, u.user_login, SUM(p.total) AS total,count(distinct p.id) AS orders FROM $customer_table AS c LEFT JOIN $purchase_table AS p ON p.customer=c.id LEFT JOIN $billing_table AS b ON b.customer=c.id LEFT JOIN $users_table AS u ON u.ID=c.wpuser AND (c.wpuser IS NULL OR c.wpuser !=0) $where GROUP BY c.id ORDER BY c.created DESC LIMIT $index,$per_page";
  851. $Customers = $db->query($query,AS_ARRAY);
  852. $num_pages = ceil($customercount->total / $per_page);
  853. $page_links = paginate_links( array(
  854. 'base' => add_query_arg( 'pagenum', '%#%' ),
  855. 'format' => '',
  856. 'total' => $num_pages,
  857. 'current' => $pagenum
  858. ));
  859. $ranges = array(
  860. 'all' => __('Show New Customers','Shopp'),
  861. 'today' => __('Today','Shopp'),
  862. 'week' => __('This Week','Shopp'),
  863. 'month' => __('This Month','Shopp'),
  864. 'quarter' => __('This Quarter','Shopp'),
  865. 'year' => __('This Year','Shopp'),
  866. 'yesterday' => __('Yesterday','Shopp'),
  867. 'lastweek' => __('Last Week','Shopp'),
  868. 'last30' => __('Last 30 Days','Shopp'),
  869. 'last90' => __('Last 3 Months','Shopp'),
  870. 'lastmonth' => __('Last Month','Shopp'),
  871. 'lastquarter' => __('Last Quarter','Shopp'),
  872. 'lastyear' => __('Last Year','Shopp'),
  873. 'lastexport' => __('Last Export','Shopp'),
  874. 'custom' => __('Custom Dates','Shopp')
  875. );
  876. $exports = array(
  877. 'tab' => __('Tab-separated.txt','Shopp'),
  878. 'csv' => __('Comma-separated.csv','Shopp'),
  879. 'xls' => __('Microsoft&reg; Excel.xls','Shopp')
  880. );
  881. $formatPref = $Shopp->Settings->get('customerexport_format');
  882. if (!$formatPref) $formatPref = 'tab';
  883. $columns = array_merge(Customer::exportcolumns(),Billing::exportcolumns(),Shipping::exportcolumns());
  884. $selected = $Shopp->Settings->get('customerexport_columns');
  885. if (empty($selected)) $selected = array_keys($columns);
  886. $authentication = $Shopp->Settings->get('account_system');
  887. include("{$this->basepath}/core/ui/customers/customers.php");
  888. }
  889. function customers_list_columns () {
  890. shopp_register_column_headers('shopp_page_shopp-customers', array(
  891. 'cb'=>'<input type="checkbox" />',
  892. 'name'=>__('Name','Shopp'),
  893. 'login'=>__('Login','Shopp'),
  894. 'email'=>__('Email','Shopp'),
  895. 'location'=>__('Location','Shopp'),
  896. 'orders'=>__('Orders','Shopp'),
  897. 'joined'=>__('Joined','Shopp'))
  898. );
  899. }
  900. function customer_editor_ui () {
  901. global $Shopp;
  902. include("{$this->basepath}/core/ui/customers/ui.php");
  903. }
  904. function customer_editor () {
  905. global $Shopp,$Customer;
  906. if ( !current_user_can(SHOPP_USERLEVEL) )
  907. wp_die(__('You do not have sufficient permissions to access this page.'));
  908. if ($_GET['id'] != "new") {
  909. $Customer = new Customer($_GET['id']);
  910. $Customer->Billing = new Billing($Customer->id,'customer');
  911. $Customer->Shipping = new Shipping($Customer->id,'customer');
  912. if (empty($Customer->id))
  913. wp_die(__('The requested customer record does not exist.','Shopp'));
  914. } else $Customer = new Customer();
  915. $countries = array(''=>'');
  916. $countrydata = $Shopp->Settings->get('countries');
  917. foreach ($countrydata as $iso => $c) {
  918. if (isset($_POST['settings']) && $_POST['settings']['base_operations']['country'] == $iso)
  919. $base_region = $c['region'];
  920. $countries[$iso] = $c['name'];
  921. }
  922. $Customer->countries = $countries;
  923. $regions = $Shopp->Settings->get('zones');
  924. $Customer->billing_states = array_merge(array(''),(array)$regions[$Customer->Billing->country]);
  925. $Customer->shipping_states = array_merge(array(''),(array)$regions[$Customer->Shipping->country]);
  926. include("{$this->basepath}/core/ui/customers/editor.php");
  927. }
  928. /**
  929. * Products admin flow handlers
  930. **/
  931. function products_list($workflow=false) {
  932. global $Products,$Shopp;
  933. $db = DB::get();
  934. if ( !current_user_can(SHOPP_USERLEVEL) )
  935. wp_die(__('You do not have sufficient permissions to access this page.'));
  936. $defaults = array(
  937. 'cat' => false,
  938. 'pagenum' => 1,
  939. 'per_page' => 20,
  940. 's' => '',
  941. 'sl' => '',
  942. 'matchcol' => ''
  943. );
  944. $args = array_merge($defaults,$_GET);
  945. extract($args,EXTR_SKIP);
  946. if (!$workflow) {
  947. if (empty($categories)) $categories = array('');
  948. $category_table = DatabaseObject::tablename(Category::$table);
  949. $query = "SELECT id,name,parent FROM $category_table ORDER BY parent,name";
  950. $categories = $db->query($query,AS_ARRAY);
  951. $categories = sort_tree($categories);
  952. if (empty($categories)) $categories = array();
  953. $categories_menu = '<option value="">'.__('View all categories','Shopp').'</option>';
  954. $categories_menu .= '<option value="-"'.($cat=='-'?' selected="selected"':'').'>'.__('Uncategorized','Shopp').'</option>';
  955. foreach ($categories as $category) {
  956. $padding = str_repeat("&nbsp;",$category->depth*3);
  957. if ($cat == $category->id) $categories_menu .= '<option value="'.$category->id.'" selected="selected">'.$padding.$category->name.'</option>';
  958. else $categories_menu .= '<option value="'.$category->id.'">'.$padding.$category->name.'</option>';
  959. }
  960. $inventory_filters = array(
  961. 'all' => __('View all products','Shopp'),
  962. 'is' => __('In stock','Shopp'),
  963. 'ls' => __('Low stock','Shopp'),
  964. 'oos' => __('Out-of-stock','Shopp'),
  965. 'ns' => __('Not stocked','Shopp')
  966. );
  967. $inventory_menu = menuoptions($inventory_filters,$sl,true);
  968. }
  969. $pagenum = absint( $pagenum );
  970. if ( empty($pagenum) )
  971. $pagenum = 1;
  972. if( !$per_page || $per_page < 0 )
  973. $per_page = 20;
  974. $start = ($per_page * ($pagenum-1));
  975. $pd = DatabaseObject::tablename(Product::$table);
  976. $pt = DatabaseObject::tablename(Price::$table);
  977. $catt = DatabaseObject::tablename(Category::$table);
  978. $clog = DatabaseObject::tablename(Catalog::$table);
  979. $orderby = "pd.created DESC";
  980. $where = "true";
  981. $having = "";
  982. if (!empty($s)) {
  983. if (strpos($s,"sku:") !== false) { // SKU search
  984. $where .= ' AND pt.sku="'.substr($s,4).'"';
  985. $orderby = "pd.name";
  986. } else { // keyword search
  987. $interference = array("'s","'",".","\"");
  988. $search = preg_replace('/(\s?)(\w+)(\s?)/','\1*\2*\3',str_replace($interference,"", stripslashes($s)));
  989. $match = "MATCH(pd.name,pd.summary,pd.description) AGAINST ('$search' IN BOOLEAN MODE)";
  990. $where .= " AND $match";
  991. $matchcol = ", $match AS score";
  992. $orderby = "score DESC";
  993. }
  994. }
  995. // if (!empty($cat)) $where .= " AND cat.id='$cat' AND (clog.category != 0 OR clog.id IS NULL)";
  996. if (!empty($cat)) {
  997. if ($cat == "-") {
  998. $having = "HAVING COUNT(cat.id) = 0";
  999. } else {
  1000. $matchcol .= ", GROUP_CONCAT(DISTINCT cat.id ORDER BY cat.id SEPARATOR ',') AS catids";
  1001. $where .= " AND (clog.category != 0 OR clog.id IS NULL)";
  1002. $having = "HAVING FIND_IN_SET('$cat',catids) > 0";
  1003. }
  1004. }
  1005. if (!empty($sl)) {
  1006. switch($sl) {
  1007. case "ns": $where .= " AND pt.inventory='off'"; break;
  1008. case "oos":
  1009. $where .= " AND (pt.inventory='on')";
  1010. $having .= (empty($having)?"HAVING ":" AND ")."SUM(pt.stock) = 0";
  1011. break;
  1012. case "ls":
  1013. $ls = $Shopp->Settings->get('lowstock_level');
  1014. if (empty($ls)) $ls = '0';
  1015. $where .= " AND (pt.inventory='on' AND pt.stock <= $ls AND pt.stock > 0)";
  1016. break;
  1017. case "is": $where .= " AND (pt.inventory='on' AND pt.stock > 0)";
  1018. }
  1019. }
  1020. $base = $Shopp->Settings->get('base_operations');
  1021. if ($base['vat']) $taxrate = $Shopp->Cart->taxrate();
  1022. if (empty($taxrate)) $taxrate = 0;
  1023. $columns = "SQL_CALC_FOUND_ROWS pd.id,pd.name,pd.slug,pd.featured,pd.variations,GROUP_CONCAT(DISTINCT cat.name ORDER BY cat.name SEPARATOR ', ') AS categories,if(pt.options=0,IF(pt.tax='off',pt.price,pt.price+(pt.price*$taxrate)),-1) AS mainprice,IF(MAX(pt.tax)='off',MAX(pt.price),MAX(pt.price+(pt.price*$taxrate))) AS maxprice,IF(MAX(pt.tax)='off',MIN(pt.price),MIN(pt.price+(pt.price*$taxrate))) AS minprice,IF(pt.inventory='on','on','off') AS inventory,ROUND(SUM(pt.stock)/count(DISTINCT clog.id),0) AS stock";
  1024. if ($workflow) $columns = "pd.id";
  1025. // Load the products
  1026. $query = "SELECT $columns $matchcol FROM $pd AS pd LEFT JOIN $pt AS pt ON pd.id=pt.product AND pt.type != 'N/A' LEFT JOIN $clog AS clog ON pd.id=clog.product LEFT JOIN $catt AS cat ON cat.id=clog.category WHERE $where GROUP BY pd.id $having ORDER BY $orderby LIMIT $start,$per_page";
  1027. $Products = $db->query($query,AS_ARRAY);
  1028. $productcount = $db->query("SELECT FOUND_ROWS() as total");
  1029. $num_pages = ceil($productcount->total / $per_page);
  1030. $page_links = paginate_links( array(
  1031. 'base' => add_query_arg(array("edit"=>null,'pagenum' => '%#%')),
  1032. 'format' => '',
  1033. 'total' => $num_pages,
  1034. 'current' => $pagenum,
  1035. ));
  1036. if ($workflow) return $Products;
  1037. include("{$this->basepath}/core/ui/products/products.php");
  1038. }
  1039. function products_list_columns () {
  1040. shopp_register_column_headers('shopp_page_shopp-products', array(
  1041. 'cb'=>'<input type="checkbox" />',
  1042. 'name'=>__('Name','Shopp'),
  1043. 'category'=>__('Category','Shopp'),
  1044. 'price'=>__('Price','Shopp'),
  1045. 'inventory'=>__('Inventory','Shopp'),
  1046. 'featured'=>__('Featured','Shopp'))
  1047. );
  1048. }
  1049. function product_shortcode ($atts) {
  1050. global $Shopp;
  1051. if (isset($atts['name'])) {
  1052. $Shopp->Product = new Product($atts['name'],'name');
  1053. } elseif (isset($atts['slug'])) {
  1054. $Shopp->Product = new Product($atts['slug'],'slug');
  1055. } elseif (isset($atts['id'])) {
  1056. $Shopp->Product = new Product($atts['id']);
  1057. } else return "";
  1058. if (isset($atts['nowrap']) && value_is_true($atts['nowrap']))
  1059. return $Shopp->Catalog->tag('product',$atts);
  1060. else return '<div id="shopp">'.$Shopp->Catalog->tag('product',$atts).'<div class="clear"></div></div>';
  1061. }
  1062. function category_shortcode ($atts) {
  1063. global $Shopp;
  1064. $tag = 'category';
  1065. if (isset($atts['name'])) {
  1066. $Shopp->Category = new Category($atts['name'],'name');
  1067. unset($atts['name']);
  1068. } elseif (isset($atts['slug'])) {
  1069. switch ($atts['slug']) {
  1070. case SearchResults::$_slug: $tag = 'search-products'; unset($atts['slug']);
  1071. break;
  1072. case TagProducts::$_slug: $tag = 'tag-products'; unset($atts['slug']);
  1073. break;
  1074. case BestsellerProducts::$_slug: $tag = 'bestseller-products'; unset($atts['slug']);
  1075. break;
  1076. case CatalogProducts::$_slug: $tag = 'catalog-products'; unset($atts['slug']);
  1077. break;
  1078. case NewProducts::$_slug: $tag = 'new-products'; unset($atts['slug']);
  1079. break;
  1080. case FeaturedProducts::$_slug: $tag = 'featured-products'; unset($atts['slug']);
  1081. break;
  1082. case OnSaleProducts::$_slug: $tag = 'onsale-products'; unset($atts['slug']);
  1083. break;
  1084. case RandomProducts::$_slug: $tag = 'random-products'; unset($atts['slug']);
  1085. break;
  1086. }
  1087. } elseif (isset($atts['id'])) {
  1088. $Shopp->Category = new Category($atts['id']);
  1089. unset($atts['id']);
  1090. } else return "";
  1091. if (isset($atts['nowrap']) && value_is_true($atts['nowrap']))
  1092. return $Shopp->Catalog->tag($tag,$atts);
  1093. else return '<div id="shopp">'.$Shopp->Catalog->tag($tag,$atts).'<div class="clear"></div></div>';
  1094. }
  1095. function maintenance_shortcode ($atts) {
  1096. return '<div id="shopp" class="update"><p>The store is currently down for maintenance. We\'ll be back soon!</p><div class="clear"></div></div>';
  1097. }
  1098. function product_editor_ui () {
  1099. global $Shopp;
  1100. include("{$this->basepath}/core/ui/products/ui.php");
  1101. }
  1102. function product_editor() {
  1103. global $Shopp;
  1104. $db = DB::get();
  1105. if ( !current_user_can(SHOPP_USERLEVEL) )
  1106. wp_die(__('You do not have sufficient permissions to access this page.'));
  1107. if (empty($Shopp->Product)) {
  1108. $Product = new Product();
  1109. $Product->published = "on";
  1110. } else $Product = $Shopp->Product;
  1111. // $Product->load_data(array('images'));
  1112. // echo "<pre>"; print_r($Product->imagesets); echo "</pre>";
  1113. $Product->slug = apply_filters('editable_slug',$Product->slug);
  1114. $permalink = $Shopp->shopuri;
  1115. require_once("{$this->basepath}/core/model/Asset.php");
  1116. require_once("{$this->basepath}/core/model/Category.php");
  1117. $Price = new Price();
  1118. $priceTypes = array(
  1119. array('value'=>'Shipped','label'=>__('Shipped','Shopp')),
  1120. array('value'=>'Virtual','label'=>__('Virtual','Shopp')),
  1121. array('value'=>'Download','label'=>__('Download','Shopp')),
  1122. array('value'=>'Donation','label'=>__('Donation','Shopp')),
  1123. array('value'=>'N/A','label'=>__('Disabled','Shopp')),
  1124. );
  1125. $workflows = array(
  1126. "continue" => __('Continue Editing','Shopp'),
  1127. "close" => __('Products Manager','Shopp'),
  1128. "new" => __('New Product','Shopp'),
  1129. "next" => __('Edit Next','Shopp'),
  1130. "previous" => __('Edit Previous','Shopp')
  1131. );
  1132. $taglist = array();
  1133. foreach ($Product->tags as $tag) $taglist[] = $tag->name;
  1134. if ($Product->id) {
  1135. $Assets = new Asset();
  1136. $Images = $db->query("SELECT id,src,properties FROM $Assets->_table WHERE context='product' AND parent=$Product->id AND datatype='thumbnail' ORDER BY sortorder",AS_ARRAY);
  1137. unset($Assets);
  1138. }
  1139. $shiprates = $this->Settings->get('shipping_rates');
  1140. if (!empty($shiprates)) ksort($shiprates);
  1141. $uploader = $Shopp->Settings->get('uploader_pref');
  1142. if (!$uploader) $uploader = 'flash';
  1143. $process = (!empty($Product->id)?$Product->id:'new');
  1144. $_POST['action'] = add_query_arg(array_merge($_GET,array('page'=>$this->Admin->products)),$Shopp->wpadminurl."admin.php");
  1145. include("{$this->basepath}/core/ui/products/editor.php");
  1146. }
  1147. function save_product($Product) {
  1148. global $Shopp;
  1149. $db = DB::get();
  1150. check_admin_referer('shopp-save-product');
  1151. if ( !current_user_can(SHOPP_USERLEVEL) )
  1152. wp_die(__('You do not have sufficient permissions to access this page.'));
  1153. $this->settings_save(); // Save workflow setting
  1154. $base = $Shopp->Settings->get('base_operations');
  1155. $taxrate = 0;
  1156. if ($base['vat']) $taxrate = $Shopp->Cart->taxrate();
  1157. if (!$_POST['options']) $Product->options = array();
  1158. else $_POST['options'] = stripslashes_deep($_POST['options']);
  1159. if (empty($Product->slug)) $Product->slug = sanitize_title_with_dashes($_POST['name']);
  1160. // Check for an existing product slug
  1161. $exclude_product = !empty($Product->id)?"AND id != $Product->id":"";
  1162. $existing = $db->query("SELECT slug FROM $Product->_table WHERE slug='$Product->slug' $exclude_product LIMIT 1");
  1163. if ($existing) {
  1164. $suffix = 2;
  1165. while($existing) {
  1166. $altslug = substr($Product->slug, 0, 200-(strlen($suffix)+1)). "-$suffix";
  1167. $existing = $db->query("SELECT slug FROM $Product->_table WHERE slug='$altslug' $exclude_product LIMIT 1");
  1168. $suffix++;
  1169. }
  1170. $Product->slug = $altslug;
  1171. }
  1172. if (isset($_POST['content'])) $_POST['description'] = $_POST['content'];
  1173. $Product->updates($_POST,array('categories'));
  1174. $Product->save();
  1175. $Product->save_categories($_POST['categories']);
  1176. $Product->save_tags(explode(",",$_POST['taglist']));
  1177. if (!empty($_POST['price']) && is_array($_POST['price'])) {
  1178. // Delete prices that were marked for removal
  1179. if (!empty($_POST['deletePrices'])) {
  1180. $deletes = array();
  1181. if (strpos($_POST['deletePrices'],",")) $deletes = explode(',',$_POST['deletePrices']);
  1182. else $deletes = array($_POST['deletePrices']);
  1183. foreach($deletes as $option) {
  1184. $Price = new Price($option);
  1185. $Price->delete();
  1186. }
  1187. }
  1188. // Save prices that there are updates for
  1189. foreach($_POST['price'] as $i => $option) {
  1190. if (empty($option['id'])) {
  1191. $Price = new Price();
  1192. $option['product'] = $Product->id;
  1193. } else $Price = new Price($option['id']);
  1194. $option['sortorder'] = array_search($i,$_POST['sortorder'])+1;
  1195. // Remove VAT amount to save in DB
  1196. if ($base['vat'] && $option['tax'] == "on") {
  1197. $option['price'] = number_format(floatnum($option['price'])/(1+$taxrate),2);
  1198. $option['saleprice'] = number_format(floatnum($option['saleprice'])/(1+$taxrate),2);
  1199. }
  1200. $Price->updates($option);
  1201. $Price->save();
  1202. if (!empty($option['download'])) $Price->attach_download($option['download']);
  1203. if (!empty($option['downloadpath'])) {
  1204. $basepath = trailingslashit($Shopp->Settings->get('products_path'));
  1205. $download = $basepath.ltrim($option['downloadpath'],"/");
  1206. if (file_exists($download)) {
  1207. $File = new Asset();
  1208. $File->parent = 0;
  1209. $File->context = "price";
  1210. $File->datatype = "download";
  1211. $File->name = basename($download);
  1212. $File->value = substr(dirname($download),strlen($basepath));
  1213. $File->size = filesize($download);
  1214. $File->properties = array("mimetype" => file_mimetype($download,$File->name));
  1215. $File->save();
  1216. $Price->attach_download($File->id);
  1217. }
  1218. }
  1219. }
  1220. unset($Price);
  1221. }
  1222. // No variation options at all, delete all variation-pricelines
  1223. if (empty($Product->options) && !empty($Product->prices) && is_array($Product->prices)) {
  1224. foreach ($Product->prices as $priceline) {
  1225. // Skip if not tied to variation options
  1226. if ($priceline->optionkey == 0) continue;
  1227. $Price = new Price($priceline->id);
  1228. $Price->delete();
  1229. }
  1230. }
  1231. if (!empty($_POST['details']) || !empty($_POST['deletedSpecs'])) {
  1232. $deletes = array();
  1233. if (!empty($_POST['deletedSpecs'])) {
  1234. if (strpos($_POST['deletedSpecs'],",")) $deletes = explode(',',$_POST['deletedSpecs']);
  1235. else $deletes = array($_POST['deletedSpecs']);
  1236. foreach($deletes as $option) {
  1237. $Spec = new Spec($option);
  1238. $Spec->delete();
  1239. }
  1240. unset($Spec);
  1241. }
  1242. if (is_array($_POST['details'])) {
  1243. foreach ($_POST['details'] as $i => $spec) {
  1244. if (in_array($spec['id'],$deletes)) continue;
  1245. if (isset($spec['new'])) {
  1246. $Spec = new Spec();
  1247. $spec['id'] = '';
  1248. $spec['product'] = $Product->id;
  1249. } else $Spec = new Spec($spec['id']);
  1250. $spec['sortorder'] = array_search($i,$_POST['details-sortorder'])+1;
  1251. $Spec->updates($spec);
  1252. if (preg_match('/^.*?(\d+[\.\,\d]*).*$/',$spec['content']))
  1253. $Spec->numeral = preg_replace('/^.*?(\d+[\.\,\d]*).*$/','$1',$spec['content']);
  1254. $Spec->save();
  1255. }
  1256. }
  1257. }
  1258. if (!empty($_POST['deleteImages'])) {
  1259. $deletes = array();
  1260. if (strpos($_POST['deleteImages'],",")) $deletes = explode(',',$_POST['deleteImages']);
  1261. else $deletes = array($_POST['deleteImages']);
  1262. $Product->delete_images($deletes);
  1263. }
  1264. if (!empty($_POST['images']) && is_array($_POST['images'])) {
  1265. $Product->link_images($_POST['images']);
  1266. $Product->save_imageorder($_POST['images']);
  1267. if (!empty($_POST['imagedetails']))
  1268. $Product->update_images($_POST['imagedetails']);
  1269. }
  1270. do_action_ref_array('shopp_product_saved',array(&$Product));
  1271. unset($Product);
  1272. return true;
  1273. }
  1274. function product_downloads () {
  1275. $error = false;
  1276. if (isset($_FILES['Filedata']['error'])) $error = $_FILES['Filedata']['error'];
  1277. if ($error) die(json_encode(array("error" => $this->uploadErrors[$error])));
  1278. // Save the uploaded file
  1279. $File = new Asset();
  1280. $File->parent = 0;
  1281. $File->context = "price";
  1282. $File->datatype = "download";
  1283. $File->name = $_FILES['Filedata']['name'];
  1284. $File->size = filesize($_FILES['Filedata']['tmp_name']);
  1285. $File->properties = array("mimetype" => file_mimetype($_FILES['Filedata']['tmp_name'],$File->name));
  1286. $File->data = addslashes(file_get_contents($_FILES['Filedata']['tmp_name']));
  1287. $File->save();
  1288. unset($File->data); // Remove file contents from memory
  1289. do_action('add_product_download',$File,$_FILES['Filedata']);
  1290. echo json_encode(array("id"=>$File->id,"name"=>stripslashes($File->name),"type"=>$File->properties['mimetype'],"size"=>$File->size));
  1291. }
  1292. function add_images () {
  1293. $QualityValue = array(100,92,80,70,60);
  1294. $error = false;
  1295. if (isset($_FILES['Filedata']['error'])) $error = $_FILES['Filedata']['error'];
  1296. if ($error) die(json_encode(array("error" => $this->uploadErrors[$error])));
  1297. require("{$this->basepath}/core/model/Image.php");
  1298. if (isset($_POST['product'])) {
  1299. $parent = $_POST['product'];
  1300. $context = "product";
  1301. }
  1302. if (isset($_POST['category'])) {
  1303. $parent = $_POST['category'];
  1304. $context = "category";
  1305. }
  1306. // Save the source image
  1307. $Image = new Asset();
  1308. $Image->parent = $parent;
  1309. $Image->context = $context;
  1310. $Image->datatype = "image";
  1311. $Image->name = $_FILES['Filedata']['name'];
  1312. list($width, $height, $mimetype, $attr) = getimagesize($_FILES['Filedata']['tmp_name']);
  1313. $Image->properties = array(
  1314. "width" => $width,
  1315. "height" => $height,
  1316. "mimetype" => image_type_to_mime_type($mimetype),
  1317. "attr" => $attr);
  1318. $Image->data = addslashes(file_get_contents($_FILES['Filedata']['tmp_name']));
  1319. $Image->save();
  1320. unset($Image->data); // Save memory for small image & thumbnail processing
  1321. // Generate Small Size
  1322. $SmallSettings = array();
  1323. $SmallSettings['width'] = $this->Settings->get('gallery_small_width');
  1324. $SmallSettings['height'] = $this->Settings->get('gallery_small_height');
  1325. $SmallSettings['sizing'] = $this->Settings->get('gallery_small_sizing');
  1326. $SmallSettings['quality'] = $this->Settings->get('gallery_small_quality');
  1327. $Small = new Asset();
  1328. $Small->parent = $Image->parent;
  1329. $Small->context = $context;
  1330. $Small->datatype = "small";
  1331. $Small->src = $Image->id;
  1332. $Small->name = "small_".$Image->name;
  1333. $Small->data = file_get_contents($_FILES['Filedata']['tmp_name']);
  1334. $SmallSizing = new ImageProcessor($Small->data,$width,$height);
  1335. switch ($SmallSettings['sizing']) {
  1336. // case "0": $SmallSizing->scaleToWidth($SmallSettings['width']); break;
  1337. // case "1": $SmallSizing->scaleToHeight($SmallSettings['height']); break;
  1338. case "0": $SmallSizing->scaleToFit($SmallSettings['width'],$SmallSettings['height']); break;
  1339. case "1": $SmallSizing->scaleCrop($SmallSettings['width'],$SmallSettings['height']); break;
  1340. }
  1341. $SmallSizing->UnsharpMask(75);
  1342. $Small->data = addslashes($SmallSizing->imagefile($QualityValue[$SmallSettings['quality']]));
  1343. $Small->properties = array();
  1344. $Small->properties['width'] = $SmallSizing->Processed->width;
  1345. $Small->properties['height'] = $SmallSizing->Processed->height;
  1346. $Small->properties['mimetype'] = "image/jpeg";
  1347. unset($SmallSizing);
  1348. $Small->save();
  1349. unset($Small);
  1350. // Generate Thumbnail
  1351. $ThumbnailSettings = array();
  1352. $ThumbnailSettings['width'] = $this->Settings->get('gallery_thumbnail_width');
  1353. $ThumbnailSettings['height'] = $this->Settings->get('gallery_thumbnail_height');
  1354. $ThumbnailSettings['sizing'] = $this->Settings->get('gallery_thumbnail_sizing');
  1355. $ThumbnailSettings['quality'] = $this->Settings->get('gallery_thumbnail_quality');
  1356. $Thumbnail = new Asset();
  1357. $Thumbnail->parent = $Image->parent;
  1358. $Thumbnail->context = $context;
  1359. $Thumbnail->datatype = "thumbnail";
  1360. $Thumbnail->src = $Image->id;
  1361. $Thumbnail->name = "thumbnail_".$Image->name;
  1362. $Thumbnail->data = file_get_contents($_FILES['Filedata']['tmp_name']);
  1363. $ThumbnailSizing = new ImageProcessor($Thumbnail->data,$width,$height);
  1364. switch ($ThumbnailSettings['sizing']) {
  1365. // case "0": $ThumbnailSizing->scaleToWidth($ThumbnailSettings['width']); break;
  1366. // case "1": $ThumbnailSizing->scaleToHeight($ThumbnailSettings['height']); break;
  1367. case "0": $ThumbnailSizing->scaleToFit($ThumbnailSettings['width'],$ThumbnailSettings['height']); break;
  1368. case "1": $ThumbnailSizing->scaleCrop($ThumbnailSettings['width'],$ThumbnailSettings['height']); break;
  1369. }
  1370. $ThumbnailSizing->UnsharpMask();
  1371. $Thumbnail->data = addslashes($ThumbnailSizing->imagefile($QualityValue[$ThumbnailSettings['quality']]));
  1372. $Thumbnail->properties = array();
  1373. $Thumbnail->properties['width'] = $ThumbnailSizing->Processed->width;
  1374. $Thumbnail->properties['height'] = $ThumbnailSizing->Processed->height;
  1375. $Thumbnail->properties['mimetype'] = "image/jpeg";
  1376. unset($ThumbnailSizing);
  1377. $Thumbnail->save();
  1378. unset($Thumbnail->data);
  1379. echo json_encode(array("id"=>$Thumbnail->id,"src"=>$Thumbnail->src));
  1380. }
  1381. /**
  1382. * Category flow handlers
  1383. **/
  1384. function categories_list ($workflow=false) {
  1385. global $Shopp;
  1386. $db = DB::get();
  1387. if ( !current_user_can(SHOPP_USERLEVEL) )
  1388. wp_die(__('You do not have sufficient permissions to access this page.'));
  1389. $defaults = array(
  1390. 'pagenum' => 1,
  1391. 'per_page' => 20,
  1392. 's' => ''
  1393. );
  1394. $args = array_merge($defaults,$_GET);
  1395. extract($args,EXTR_SKIP);
  1396. $pagenum = absint( $pagenum );
  1397. if ( empty($pagenum) )
  1398. $pagenum = 1;
  1399. if( !$per_page || $per_page < 0 )
  1400. $per_page = 20;
  1401. $start = ($per_page * ($pagenum-1));
  1402. $filters = array();
  1403. // $filters['limit'] = "$start,$per_page";
  1404. if (!empty($s))
  1405. $filters['where'] = "cat.name LIKE '%$s%'";
  1406. else $filters['where'] = "true";
  1407. $table = DatabaseObject::tablename(Category::$table);
  1408. $Catalog = new Catalog();
  1409. $Catalog->outofstock = true;
  1410. if ($workflow) {
  1411. $filters['columns'] = "cat.id,cat.parent";
  1412. $results = $Catalog->load_categories($filters,false,true);
  1413. return array_slice($results,$start,$per_page);
  1414. } else {
  1415. $filters['columns'] = "cat.id,cat.parent,cat.name,cat.description,cat.uri,cat.slug,cat.spectemplate,cat.facetedmenus,count(DISTINCT pd.id) AS total";
  1416. $Catalog->load_categories($filters);
  1417. $Categories = array_slice($Catalog->categories,$start,$per_page);
  1418. }
  1419. $count = $db->query("SELECT count(*) AS total FROM $table");
  1420. $num_pages = ceil($count->total / $per_page);
  1421. $page_links = paginate_links( array(
  1422. 'base' => add_query_arg( array('edit'=>null,'pagenum' => '%#%' )),
  1423. 'format' => '',
  1424. 'total' => $num_pages,
  1425. 'current' => $pagenum
  1426. ));
  1427. include("{$this->basepath}/core/ui/categories/categories.php");
  1428. }
  1429. function categories_list_columns () {
  1430. shopp_register_column_headers('shopp_page_shopp-categories', array(
  1431. 'cb'=>'<input type="checkbox" />',
  1432. 'name'=>__('Name','Shopp'),
  1433. 'description'=>__('Description','Shopp'),
  1434. 'links'=>__('Products','Shopp'),
  1435. 'templates'=>__('Templates','Shopp'),
  1436. 'menus'=>__('Menus','Shopp'))
  1437. );
  1438. }
  1439. function category_editor_ui () {
  1440. global $Shopp;
  1441. include("{$this->basepath}/core/ui/categories/ui.php");
  1442. }
  1443. function category_editor () {
  1444. global $Shopp;
  1445. $db = DB::get();
  1446. if ( !current_user_can(SHOPP_USERLEVEL) )
  1447. wp_die(__('You do not have sufficient permissions to access this page.'));
  1448. if (empty($Shopp->Category)) $Category = new Category();
  1449. else $Category = $Shopp->Category;
  1450. $Price = new Price();
  1451. $priceTypes = array(
  1452. array('value'=>'Shipped','label'=>__('Shipped','Shopp')),
  1453. array('value'=>'Virtual','label'=>__('Virtual','Shopp')),
  1454. array('value'=>'Download','label'=>__('Download','Shopp')),
  1455. array('value'=>'Donation','label'=>__('Donation','Shopp')),
  1456. array('value'=>'N/A','label'=>__('N/A','Shopp'))
  1457. );
  1458. // Build permalink for slug editor
  1459. $permalink = trailingslashit($Shopp->link('catalog'))."category/";
  1460. $Category->slug = apply_filters('editable_slug',$Category->slug);
  1461. if (!empty($Category->slug))
  1462. $permalink .= substr($Category->uri,0,strpos($Category->uri,$Category->slug));
  1463. $pricerange_menu = array(
  1464. "disabled" => __('Price ranges disabled','Shopp'),
  1465. "auto" => __('Build price ranges automatically','Shopp'),
  1466. "custom" => __('Use custom price ranges','Shopp'),
  1467. );
  1468. $Images = array();
  1469. if (!empty($Category->id)) {
  1470. $asset_table = DatabaseObject::tablename(Asset::$table);
  1471. $Images = $db->query("SELECT id,src,properties FROM $asset_table WHERE context='category' AND parent=$Category->id AND datatype='thumbnail' ORDER BY sortorder",AS_ARRAY);
  1472. }
  1473. $categories_menu = $this->category_menu($Category->parent,$Category->id);
  1474. $categories_menu = '<option value="0" rel="-1,-1">'.__('Parent Category','Shopp').'&hellip;</option>'.$categories_menu;
  1475. $uploader = $Shopp->Settings->get('uploader_pref');
  1476. if (!$uploader) $uploader = 'flash';
  1477. $workflows = array(
  1478. "continue" => __('Continue Editing','Shopp'),
  1479. "close" => __('Categories Manager','Shopp'),
  1480. "new" => __('New Category','Shopp'),
  1481. "next" => __('Edit Next','Shopp'),
  1482. "previous" => __('Edit Previous','Shopp')
  1483. );
  1484. include("{$this->basepath}/core/ui/categories/category.php");
  1485. }
  1486. function save_category ($Category) {
  1487. global $Shopp;
  1488. $db = DB::get();
  1489. check_admin_referer('shopp-save-category');
  1490. if ( !current_user_can(SHOPP_USERLEVEL) )
  1491. wp_die(__('You do not have sufficient permissions to access this page.'));
  1492. $this->settings_save(); // Save workflow setting
  1493. $Shopp->Catalog = new Catalog();
  1494. $Shopp->Catalog->load_categories(array('where'=>'true'));
  1495. if (!isset($_POST['slug']) && empty($Category->slug))
  1496. $Category->slug = sanitize_title_with_dashes($_POST['name']);
  1497. if (isset($_POST['slug'])) unset($_POST['slug']);
  1498. // Work out pathing
  1499. $paths = array();
  1500. if (!empty($Category->slug)) $paths = array($Category->slug); // Include self
  1501. $parentkey = -1;
  1502. // If we're saving a new category, lookup the parent
  1503. if ($_POST['parent'] > 0) {
  1504. array_unshift($paths,$Shopp->Catalog->categories[$_POST['parent']]->slug);
  1505. $parentkey = $Shopp->Catalog->categories[$_POST['parent']]->parent;
  1506. }
  1507. while ($category_tree = $Shopp->Catalog->categories[$parentkey]) {
  1508. array_unshift($paths,$category_tree->slug);
  1509. $parentkey = $category_tree->parent;
  1510. }
  1511. if (count($paths) > 1) $_POST['uri'] = join("/",$paths);
  1512. else $_POST['uri'] = $paths[0];
  1513. if (!empty($_POST['deleteImages'])) {
  1514. $deletes = array();
  1515. if (strpos($_POST['deleteImages'],",")) $deletes = explode(',',$_POST['deleteImages']);
  1516. else $deletes = array($_POST['deleteImages']);
  1517. $Category->delete_images($deletes);
  1518. }
  1519. if (!empty($_POST['images']) && is_array($_POST['images'])) {
  1520. $Category->link_images($_POST['images']);
  1521. $Category->save_imageorder($_POST['images']);
  1522. if (!empty($_POST['imagedetails']) && is_array($_POST['imagedetails'])) {
  1523. foreach($_POST['imagedetails'] as $i => $data) {
  1524. $Image = new Asset();
  1525. unset($Image->_datatypes['data'],$Image->data);
  1526. $Image->load($data['id']);
  1527. $Image->properties['title'] = $data['title'];
  1528. $Image->properties['alt'] = $data['alt'];
  1529. $Image->save();
  1530. }
  1531. }
  1532. }
  1533. // Variation price templates
  1534. if (!empty($_POST['price']) && is_array($_POST['price'])) {
  1535. foreach ($_POST['price'] as &$pricing) {
  1536. $pricing['price'] = floatvalue($pricing['price']);
  1537. $pricing['saleprice'] = floatvalue($pricing['saleprice']);
  1538. $pricing['shipfee'] = floatvalue($pricing['shipfee']);
  1539. }
  1540. $Category->prices = stripslashes_deep($_POST['price']);
  1541. } else $Category->prices = array();
  1542. if (empty($_POST['specs'])) $Category->specs = array();
  1543. else $_POST['specs'] = stripslashes_deep($_POST['specs']);
  1544. if (empty($_POST['options'])
  1545. || (count($_POST['options'])) == 1 && !isset($_POST['options'][1]['options'])) {
  1546. $_POST['options'] = $Category->options = array();
  1547. $_POST['prices'] = $Category->prices = array();
  1548. } else $_POST['options'] = stripslashes_deep($_POST['options']);
  1549. if (isset($_POST['content'])) $_POST['description'] = $_POST['content'];
  1550. $Category->updates($_POST);
  1551. $Category->save();
  1552. do_action_ref_array('shopp_category_saved',array(&$Category));
  1553. $updated = '<strong>'.$Category->name.'</strong> '.__('category saved.','Shopp');
  1554. }
  1555. function category_menu ($selection=false,$current=false) {
  1556. $db = DB::get();
  1557. $table = DatabaseObject::tablename(Category::$table);
  1558. $categories = $db->query("SELECT id,name,parent FROM $table ORDER BY parent,name",AS_ARRAY);
  1559. $categories = sort_tree($categories);
  1560. $options = '';
  1561. foreach ($categories as $category) {
  1562. $padding = str_repeat("&nbsp;",$category->depth*3);
  1563. $selected = ($category->id == $selection)?' selected="selected"':'';
  1564. $disabled = ($current && $category->id == $current)?' disabled="disabled"':'';
  1565. $options .= '<option value="'.$category->id.'" rel="'.$category->parent.','.$category->depth.'"'.$selected.$disabled.'>'.$padding.$category->name.'</option>';
  1566. }
  1567. return $options;
  1568. }
  1569. function category_products () {
  1570. $db = DB::get();
  1571. $catalog = DatabaseObject::tablename(Catalog::$table);
  1572. $category = DatabaseObject::tablename(Category::$table);
  1573. $products = DatabaseObject::tablename(Product::$table);
  1574. $results = $db->query("SELECT p.id,p.name FROM $catalog AS catalog LEFT JOIN $category AS cat ON cat.id = catalog.category LEFT JOIN $products AS p ON p.id=catalog.product WHERE cat.id={$_GET['category']} ORDER BY p.name ASC",AS_ARRAY);
  1575. $products = array();
  1576. $products[0] = __("Select a product&hellip;","Shopp");
  1577. foreach ($results as $result) $products[$result->id] = $result->name;
  1578. return menuoptions($products,0,true);
  1579. }
  1580. function promotions_list () {
  1581. global $Shopp;
  1582. $db = DB::get();
  1583. if ( !current_user_can(SHOPP_USERLEVEL) )
  1584. wp_die(__('You do not have sufficient permissions to access this page.'));
  1585. require_once("{$this->basepath}/core/model/Promotion.php");
  1586. $defaults = array(
  1587. 'page' => false,
  1588. 'deleting' => false,
  1589. 'delete' => false,
  1590. 'pagenum' => 1,
  1591. 'per_page' => 20,
  1592. 's' => ''
  1593. );
  1594. $args = array_merge($defaults,$_GET);
  1595. extract($args,EXTR_SKIP);
  1596. if ($page == "shopp-promotions"
  1597. && !empty($deleting)
  1598. && !empty($delete)
  1599. && is_array($delete)) {
  1600. foreach($delete as $deletion) {
  1601. $Promotion = new Promotion($deletion);
  1602. $Promotion->delete();
  1603. }
  1604. }
  1605. if (!empty($_POST['save'])) {
  1606. check_admin_referer('shopp-save-promotion');
  1607. if ($_POST['id'] != "new") {
  1608. $Promotion = new Promotion($_POST['id']);
  1609. } else $Promotion = new Promotion();
  1610. if (!empty($_POST['starts']['month']) && !empty($_POST['starts']['date']) && !empty($_POST['starts']['year']))
  1611. $_POST['starts'] = mktime(0,0,0,$_POST['starts']['month'],$_POST['starts']['date'],$_POST['starts']['year']);
  1612. else $_POST['starts'] = 1;
  1613. if (!empty($_POST['ends']['month']) && !empty($_POST['ends']['date']) && !empty($_POST['ends']['year']))
  1614. $_POST['ends'] = mktime(23,59,59,$_POST['ends']['month'],$_POST['ends']['date'],$_POST['ends']['year']);
  1615. else $_POST['ends'] = 1;
  1616. if (isset($_POST['rules'])) $_POST['rules'] = stripslashes_deep($_POST['rules']);
  1617. $Promotion->updates($_POST);
  1618. $Promotion->save();
  1619. do_action_ref_array('shopp_promo_saved',array(&$Promotion));
  1620. if ($Promotion->scope == "Catalog")
  1621. $Promotion->build_discounts();
  1622. // Reset cart promotions cache
  1623. // to force reload for these updates
  1624. $Shopp->Cart->data->Promotions = false;
  1625. }
  1626. $pagenum = absint( $pagenum );
  1627. if ( empty($pagenum) )
  1628. $pagenum = 1;
  1629. if( !$per_page || $per_page < 0 )
  1630. $per_page = 20;
  1631. $start = ($per_page * ($pagenum-1));
  1632. $where = "";
  1633. if (!empty($s)) $where = "WHERE name LIKE '%$s%'";
  1634. $table = DatabaseObject::tablename(Promotion::$table);
  1635. $promocount = $db->query("SELECT count(*) as total FROM $table $where");
  1636. $Promotions = $db->query("SELECT * FROM $table $where",AS_ARRAY);
  1637. $status = array(
  1638. 'enabled' => __('Enabled','Shopp'),
  1639. 'disabled' => __('Disabled','Shopp')
  1640. );
  1641. $num_pages = ceil($promocount->total / $per_page);
  1642. $page_links = paginate_links( array(
  1643. 'base' => add_query_arg( 'pagenum', '%#%' ),
  1644. 'format' => '',
  1645. 'total' => $num_pages,
  1646. 'current' => $pagenum
  1647. ));
  1648. include("{$this->basepath}/core/ui/promotions/promotions.php");
  1649. }
  1650. function promotions_list_columns () {
  1651. shopp_register_column_headers('shopp_page_shopp-promotions', array(
  1652. 'cb'=>'<input type="checkbox" />',
  1653. 'name'=>__('Name','Shopp'),
  1654. 'discount'=>__('Discount','Shopp'),
  1655. 'applied'=>__('Applied To','Shopp'),
  1656. 'eff'=>__('Status','Shopp'))
  1657. );
  1658. }
  1659. function promotion_editor_ui () {
  1660. global $Shopp;
  1661. include("{$this->basepath}/core/ui/promotions/ui.php");
  1662. }
  1663. function promotion_editor () {
  1664. global $Shopp;
  1665. if ( !current_user_can(SHOPP_USERLEVEL) )
  1666. wp_die(__('You do not have sufficient permissions to access this page.'));
  1667. require_once("{$this->basepath}/core/model/Promotion.php");
  1668. if ($_GET['id'] != "new") {
  1669. $Promotion = new Promotion($_GET['id']);
  1670. } else $Promotion = new Promotion();
  1671. $scopes = array(
  1672. 'Catalog' => __('Catalog','Shopp'),
  1673. 'Order' => __('Order','Shopp')
  1674. );
  1675. $types = array(
  1676. 'Percentage Off' => __('Percentage Off','Shopp'),
  1677. 'Amount Off' => __('Amount Off','Shopp'),
  1678. 'Free Shipping' => __('Free Shipping','Shopp'),
  1679. 'Buy X Get Y Free' => __('Buy X Get Y Free','Shopp')
  1680. );
  1681. include("{$this->basepath}/core/ui/promotions/editor.php");
  1682. }
  1683. /**
  1684. * Dashboard Widgets
  1685. */
  1686. function dashboard_stats ($args=null) {
  1687. global $Shopp;
  1688. $db = DB::get();
  1689. $defaults = array(
  1690. 'before_widget' => '',
  1691. 'before_title' => '',
  1692. 'widget_name' => '',
  1693. 'after_title' => '',
  1694. 'after_widget' => ''
  1695. );
  1696. if (!$args) $args = array();
  1697. $args = array_merge($defaults,$args);
  1698. if (!empty($args)) extract( $args, EXTR_SKIP );
  1699. echo $before_widget;
  1700. echo $before_title;
  1701. echo $widget_name;
  1702. echo $after_title;
  1703. $purchasetable = DatabaseObject::tablename(Purchase::$table);
  1704. $results = $db->query("SELECT count(id) AS orders, SUM(total) AS sales, AVG(total) AS average,
  1705. SUM(IF(UNIX_TIMESTAMP(created) > UNIX_TIMESTAMP()-(86400*30),1,0)) AS wkorders,
  1706. SUM(IF(UNIX_TIMESTAMP(created) > UNIX_TIMESTAMP()-(86400*30),total,0)) AS wksales,
  1707. AVG(IF(UNIX_TIMESTAMP(created) > UNIX_TIMESTAMP()-(86400*30),total,null)) AS wkavg
  1708. FROM $purchasetable");
  1709. $orderscreen = add_query_arg('page',$this->Admin->orders,$Shopp->wpadminurl."admin.php");
  1710. echo '<div class="table"><table><tbody>';
  1711. echo '<tr><th colspan="2">'.__('Last 30 Days','Shopp').'</th><th colspan="2">'.__('Lifetime','Shopp').'</th></tr>';
  1712. echo '<tr><td class="amount"><a href="'.$orderscreen.'">'.$results->wkorders.'</a></td><td>'.__('Orders','Shopp').'</td>';
  1713. echo '<td class="amount"><a href="'.$orderscreen.'">'.$results->orders.'</a></td><td>'.__('Orders','Shopp').'</td></tr>';
  1714. echo '<tr><td class="amount"><a href="'.$orderscreen.'">'.money($results->wksales).'</a></td><td>'.__('Sales','Shopp').'</td>';
  1715. echo '<td class="amount"><a href="'.$orderscreen.'">'.money($results->sales).'</a></td><td>'.__('Sales','Shopp').'</td></tr>';
  1716. echo '<tr><td class="amount"><a href="'.$orderscreen.'">'.money($results->wkavg).'</a></td><td>'.__('Average Order','Shopp').'</td>';
  1717. echo '<td class="amount"><a href="'.$orderscreen.'">'.money($results->average).'</a></td><td>'.__('Average Order','Shopp').'</td></tr>';
  1718. echo '</tbody></table></div>';
  1719. echo $after_widget;
  1720. }
  1721. function dashboard_orders ($args=null) {
  1722. global $Shopp;
  1723. $db = DB::get();
  1724. $defaults = array(
  1725. 'before_widget' => '',
  1726. 'before_title' => '',
  1727. 'widget_name' => '',
  1728. 'after_title' => '',
  1729. 'after_widget' => ''
  1730. );
  1731. if (!$args) $args = array();
  1732. $args = array_merge($defaults,$args);
  1733. if (!empty($args)) extract( $args, EXTR_SKIP );
  1734. $statusLabels = $this->Settings->get('order_status');
  1735. echo $before_widget;
  1736. echo $before_title;
  1737. echo $widget_name;
  1738. echo $after_title;
  1739. $purchasetable = DatabaseObject::tablename(Purchase::$table);
  1740. $purchasedtable = DatabaseObject::tablename(Purchased::$table);
  1741. $Orders = $db->query("SELECT p.*,count(i.id) as items FROM $purchasetable AS p LEFT JOIN $purchasedtable AS i ON i.purchase=p.id GROUP BY i.purchase ORDER BY created DESC LIMIT 6",AS_ARRAY);
  1742. if (!empty($Orders)) {
  1743. echo '<table class="widefat">';
  1744. echo '<tr><th scope="col">'.__('Name','Shopp').'</th><th scope="col">'.__('Date','Shopp').'</th><th scope="col" class="num">'.__('Items','Shopp').'</th><th scope="col" class="num">'.__('Total','Shopp').'</th><th scope="col" class="num">'.__('Status','Shopp').'</th></tr>';
  1745. echo '<tbody id="orders" class="list orders">';
  1746. $even = false;
  1747. foreach ($Orders as $Order) {
  1748. echo '<tr'.((!$even)?' class="alternate"':'').'>';
  1749. $even = !$even;
  1750. echo '<td><a class="row-title" href="'.add_query_arg(array('page'=>$this->Admin->orders,'id'=>$Order->id),$Shopp->wpadminurl."admin.php").'" title="View &quot;Order '.$Order->id.'&quot;">'.((empty($Order->firstname) && empty($Order->lastname))?'(no contact name)':$Order->firstname.' '.$Order->lastname).'</a></td>';
  1751. echo '<td>'.date("Y/m/d",mktimestamp($Order->created)).'</td>';
  1752. echo '<td class="num">'.$Order->items.'</td>';
  1753. echo '<td class="num">'.money($Order->total).'</td>';
  1754. echo '<td class="num">'.$statusLabels[$Order->status].'</td>';
  1755. echo '</tr>';
  1756. }
  1757. echo '</tbody></table>';
  1758. } else {
  1759. echo '<p>'.__('No orders, yet.','Shopp').'</p>';
  1760. }
  1761. echo $after_widget;
  1762. }
  1763. function dashboard_products ($args=null) {
  1764. global $Shopp;
  1765. $db = DB::get();
  1766. $defaults = array(
  1767. 'before_widget' => '',
  1768. 'before_title' => '',
  1769. 'widget_name' => '',
  1770. 'after_title' => '',
  1771. 'after_widget' => ''
  1772. );
  1773. if (!$args) $args = array();
  1774. $args = array_merge($defaults,$args);
  1775. if (!empty($args)) extract( $args, EXTR_SKIP );
  1776. echo $before_widget;
  1777. echo $before_title;
  1778. echo $widget_name;
  1779. echo $after_title;
  1780. $RecentBestsellers = new BestsellerProducts(array('where'=>'UNIX_TIMESTAMP(pur.created) > UNIX_TIMESTAMP()-(86400*30)','show'=>3));
  1781. $RecentBestsellers->load_products();
  1782. echo '<table><tbody><tr>';
  1783. echo '<td><h4>'.__('Recent Bestsellers','Shopp').'</h4>';
  1784. echo '<ul>';
  1785. foreach ($RecentBestsellers->products as $product)
  1786. echo '<li><a href="'.add_query_arg(array('page'=>$this->Admin->editproduct,'id'=>$product->id),$Shopp->wpadminurl."admin.php").'">'.$product->name.'</a> ('.$product->sold.')</li>';
  1787. echo '</ul></td>';
  1788. $LifetimeBestsellers = new BestsellerProducts(array('show'=>3));
  1789. $LifetimeBestsellers->load_products();
  1790. echo '<td><h4>'.__('Lifetime Bestsellers','Shopp').'</h4>';
  1791. echo '<ul>';
  1792. foreach ($LifetimeBestsellers->products as $product)
  1793. echo '<li><a href="'.add_query_arg(array('page'=>$this->Admin->editproduct,'id'=>$product->id),$Shopp->wpadminurl."admin.php").'">'.$product->name.'</a> ('.$product->sold.')</li>';
  1794. echo '</ul></td>';
  1795. echo '</tr></tbody></table>';
  1796. echo $after_widget;
  1797. }
  1798. /**
  1799. * Settings flow handlers
  1800. **/
  1801. function settings_general () {
  1802. global $Shopp;
  1803. if ( !current_user_can('manage_options') )
  1804. wp_die(__('You do not have sufficient permissions to access this page.'));
  1805. $country = (isset($_POST['settings']))?$_POST['settings']['base_operations']['country']:'';
  1806. $countries = array();
  1807. $countrydata = $Shopp->Settings->get('countries');
  1808. foreach ($countrydata as $iso => $c) {
  1809. if (isset($_POST['settings']) && $_POST['settings']['base_operations']['country'] == $iso)
  1810. $base_region = $c['region'];
  1811. $countries[$iso] = $c['name'];
  1812. }
  1813. if (!empty($_POST['setup'])) {
  1814. $_POST['settings']['display_welcome'] = "off";
  1815. $this->settings_save();
  1816. }
  1817. if (!empty($_POST['save'])) {
  1818. check_admin_referer('shopp-settings-general');
  1819. $vat_countries = $Shopp->Settings->get('vat_countries');
  1820. $zone = $_POST['settings']['base_operations']['zone'];
  1821. $_POST['settings']['base_operations'] = $countrydata[$_POST['settings']['base_operations']['country']];
  1822. $_POST['settings']['base_operations']['country'] = $country;
  1823. $_POST['settings']['base_operations']['zone'] = $zone;
  1824. $_POST['settings']['base_operations']['currency']['format'] =
  1825. scan_money_format($_POST['settings']['base_operations']['currency']['format']);
  1826. if (in_array($_POST['settings']['base_operations']['country'],$vat_countries))
  1827. $_POST['settings']['base_operations']['vat'] = true;
  1828. else $_POST['settings']['base_operations']['vat'] = false;
  1829. $this->settings_save();
  1830. $updated = __('Shopp settings saved.');
  1831. }
  1832. $operations = $Shopp->Settings->get('base_operations');
  1833. if (!empty($operations['zone'])) {
  1834. $zones = $Shopp->Settings->get('zones');
  1835. $zones = $zones[$operations['country']];
  1836. }
  1837. $targets = $Shopp->Settings->get('target_markets');
  1838. if (!$targets) $targets = array();
  1839. $statusLabels = $Shopp->Settings->get('order_status');
  1840. include(SHOPP_ADMINPATH."/settings/settings.php");
  1841. }
  1842. function settings_presentation () {
  1843. if ( !current_user_can('manage_options') )
  1844. wp_die(__('You do not have sufficient permissions to access this page.'));
  1845. if (isset($_POST['settings']['theme_templates']) && $_POST['settings']['theme_templates'] == "on")
  1846. $_POST['settings']['theme_templates'] = addslashes(template_path(STYLESHEETPATH.DIRECTORY_SEPARATOR."shopp"));
  1847. if (!empty($_POST['save'])) {
  1848. check_admin_referer('shopp-settings-presentation');
  1849. if (empty($_POST['settings']['catalog_pagination']))
  1850. $_POST['settings']['catalog_pagination'] = 0;
  1851. $this->settings_save();
  1852. $updated = __('Shopp presentation settings saved.');
  1853. }
  1854. $builtin_path = $this->basepath.DIRECTORY_SEPARATOR."templates";
  1855. $theme_path = template_path(STYLESHEETPATH.DIRECTORY_SEPARATOR."shopp");
  1856. // Copy templates to the current WordPress theme
  1857. if (!empty($_POST['install'])) {
  1858. check_admin_referer('shopp-settings-presentation');
  1859. copy_shopp_templates($builtin_path,$theme_path);
  1860. }
  1861. $status = "available";
  1862. if (!is_dir($theme_path)) $status = "directory";
  1863. else {
  1864. if (!is_writable($theme_path)) $status = "permissions";
  1865. else {
  1866. $builtin = array_filter(scandir($builtin_path),"filter_dotfiles");
  1867. $theme = array_filter(scandir($theme_path),"filter_dotfiles");
  1868. if (empty($theme)) $status = "ready";
  1869. else if (array_diff($builtin,$theme)) $status = "incomplete";
  1870. }
  1871. }
  1872. $category_views = array("grid" => __('Grid','Shopp'),"list" => __('List','Shopp'));
  1873. $row_products = array(2,3,4,5,6,7);
  1874. $productOrderOptions = Category::sortoptions();
  1875. $orderOptions = array("ASC" => __('Order','Shopp'),
  1876. "DESC" => __('Reverse Order','Shopp'),
  1877. "RAND" => __('Shuffle','Shopp'));
  1878. $orderBy = array("sortorder" => __('Custom arrangement','Shopp'),
  1879. "name" => __('File name','Shopp'),
  1880. "created" => __('Upload date','Shopp'));
  1881. $sizingOptions = array( __('Scale to fit','Shopp'),
  1882. __('Scale &amp; crop','Shopp'));
  1883. $qualityOptions = array(__('Highest quality, largest file size','Shopp'),
  1884. __('Higher quality, larger file size','Shopp'),
  1885. __('Balanced quality &amp; file size','Shopp'),
  1886. __('Lower quality, smaller file size','Shopp'),
  1887. __('Lowest quality, smallest file size','Shopp'));
  1888. include(SHOPP_ADMINPATH."/settings/presentation.php");
  1889. }
  1890. function settings_catalog () {
  1891. // check_admin_referer('shopp-settings-catalog');
  1892. if ( !current_user_can('manage_options') )
  1893. wp_die(__('You do not have sufficient permissions to access this page.'));
  1894. if (!empty($_POST['save'])) $this->settings_save();
  1895. include(SHOPP_ADMINPATH."/settings/catalog.php");
  1896. }
  1897. function settings_cart () {
  1898. if ( !current_user_can('manage_options') )
  1899. wp_die(__('You do not have sufficient permissions to access this page.'));
  1900. if (!empty($_POST['save'])) $this->settings_save();
  1901. include(SHOPP_ADMINPATH."/settings/cart.php");
  1902. }
  1903. function settings_checkout () {
  1904. global $Shopp;
  1905. $db =& DB::get();
  1906. if ( !current_user_can('manage_options') )
  1907. wp_die(__('You do not have sufficient permissions to access this page.'));
  1908. $purchasetable = DatabaseObject::tablename(Purchase::$table);
  1909. $next = $db->query("SELECT IF ((MAX(id)) > 0,(MAX(id)+1),1) AS id FROM $purchasetable LIMIT 1");
  1910. $next_setting = $Shopp->Settings->get('next_order_id');
  1911. if ($next->id > $next_setting) $next_setting = $next->id;
  1912. if (!empty($_POST['save'])) {
  1913. check_admin_referer('shopp-settings-checkout');
  1914. if ($_POST['settings']['next_order_id'] != $next->id) {
  1915. if ($db->query("ALTER TABLE $purchasetable AUTO_INCREMENT={$_POST['settings']['next_order_id']}"))
  1916. $next->id = $_POST['settings']['next_order_id'];
  1917. }
  1918. $this->settings_save();
  1919. $updated = __('Shopp checkout settings saved.','Shopp');
  1920. }
  1921. $downloads = array("1","2","3","5","10","15","25","100");
  1922. $promolimit = array("1","2","3","4","5","6","7","8","9","10","15","20","25");
  1923. $time = array(
  1924. '1800' => __('30 minutes','Shopp'),
  1925. '3600' => __('1 hour','Shopp'),
  1926. '7200' => __('2 hours','Shopp'),
  1927. '10800' => __('3 hours','Shopp'),
  1928. '21600' => __('6 hours','Shopp'),
  1929. '43200' => __('12 hours','Shopp'),
  1930. '86400' => __('1 day','Shopp'),
  1931. '172800' => __('2 days','Shopp'),
  1932. '259200' => __('3 days','Shopp'),
  1933. '604800' => __('1 week','Shopp'),
  1934. '2678400' => __('1 month','Shopp'),
  1935. '7952400' => __('3 months','Shopp'),
  1936. '15901200' => __('6 months','Shopp'),
  1937. '31536000' => __('1 year','Shopp'),
  1938. );
  1939. include(SHOPP_ADMINPATH."/settings/checkout.php");
  1940. }
  1941. function settings_shipping () {
  1942. global $Shopp;
  1943. if ( !current_user_can('manage_options') )
  1944. wp_die(__('You do not have sufficient permissions to access this page.'));
  1945. if (!empty($_POST['save'])) {
  1946. check_admin_referer('shopp-settings-shipping');
  1947. // Sterilize $values
  1948. foreach ($_POST['settings']['shipping_rates'] as $i => &$method) {
  1949. $method['name'] = stripslashes($method['name']);
  1950. foreach ($method as $key => &$mr) {
  1951. if (!is_array($mr)) continue;
  1952. foreach ($mr as $id => &$v) {
  1953. if ($v == ">" || $v == "+" || $key == "services") continue;
  1954. $v = floatnum($v);
  1955. }
  1956. }
  1957. }
  1958. $_POST['settings']['order_shipfee'] = floatnum($_POST['settings']['order_shipfee']);
  1959. $this->settings_save();
  1960. $updated = __('Shipping settings saved.','Shopp');
  1961. $Shopp->ShipCalcs = new ShipCalcs($Shopp->path);
  1962. $rates = $Shopp->Settings->get('shipping_rates');
  1963. $Errors = &ShoppErrors();
  1964. foreach ($rates as $rate) {
  1965. $process = '';
  1966. $ShipCalcClass = $rate['method'];
  1967. if (strpos($rate['method'],'::') != false)
  1968. list($ShipCalcClass,$process) = explode("::",$rate['method']);
  1969. if (isset($Shopp->ShipCalcs->modules[$ShipCalcClass]->requiresauth)
  1970. && $Shopp->ShipCalcs->modules[$ShipCalcClass]->requiresauth) {
  1971. $Shopp->ShipCalcs->modules[$ShipCalcClass]->verifyauth();
  1972. if ($Errors->exist()) $autherrors = $Errors->get();
  1973. }
  1974. }
  1975. if (!empty($autherrors)) {
  1976. $updated = __('Shipping settings saved but there were errors: ','Shopp');
  1977. foreach ((array)$autherrors as $error) $updated .= '<p>'.$error->message().'</p>';
  1978. $Errors->reset();
  1979. }
  1980. }
  1981. $methods = $Shopp->ShipCalcs->methods;
  1982. $base = $Shopp->Settings->get('base_operations');
  1983. $regions = $Shopp->Settings->get('regions');
  1984. $region = $regions[$base['region']];
  1985. $useRegions = $Shopp->Settings->get('shipping_regions');
  1986. $areas = $Shopp->Settings->get('areas');
  1987. if (is_array($areas[$base['country']]) && $useRegions == "on")
  1988. $areas = array_keys($areas[$base['country']]);
  1989. else $areas = array($base['country'] => $base['name']);
  1990. unset($countries,$regions);
  1991. $rates = $Shopp->Settings->get('shipping_rates');
  1992. if (!empty($rates)) ksort($rates);
  1993. $lowstock = $Shopp->Settings->get('lowstock_level');
  1994. if (empty($lowstock)) $lowstock = 0;
  1995. include(SHOPP_ADMINPATH."/settings/shipping.php");
  1996. }
  1997. function settings_taxes () {
  1998. if ( !current_user_can('manage_options') )
  1999. wp_die(__('You do not have sufficient permissions to access this page.'));
  2000. if (!empty($_POST['save'])) {
  2001. check_admin_referer('shopp-settings-taxes');
  2002. $this->settings_save();
  2003. $updated = __('Shopp taxes settings saved.','Shopp');
  2004. }
  2005. $rates = $this->Settings->get('taxrates');
  2006. $base = $this->Settings->get('base_operations');
  2007. $countries = array_merge(array('*' => __('All Markets','Shopp')),
  2008. $this->Settings->get('target_markets'));
  2009. $zones = $this->Settings->get('zones');
  2010. include(SHOPP_ADMINPATH."/settings/taxes.php");
  2011. }
  2012. function settings_payments () {
  2013. global $Shopp;
  2014. if ( !current_user_can('manage_options') )
  2015. wp_die(__('You do not have sufficient permissions to access this page.'));
  2016. // $gateway_dir = SHOPP_PATH.DIRECTORY_SEPARATOR."gateways".DIRECTORY_SEPARATOR;
  2017. $payment_gateway = gateway_path($this->Settings->get('payment_gateway'));
  2018. if (!empty($_POST['save'])) {
  2019. check_admin_referer('shopp-settings-payments');
  2020. // Update the accepted credit card payment methods
  2021. if (!empty($_POST['settings']['payment_gateway'])
  2022. && file_exists(SHOPP_GATEWAYS.$_POST['settings']['payment_gateway'])) {
  2023. $gateway = $this->scan_gateway_meta(SHOPP_GATEWAYS.$_POST['settings']['payment_gateway']);
  2024. $ProcessorClass = $gateway->tags['class'];
  2025. // Load the gateway in case there are any save-time processes to be run
  2026. $Processor = $Shopp->gateway($_POST['settings']['payment_gateway'],true);
  2027. $_POST['settings']['gateway_cardtypes'] = $_POST['settings'][$ProcessorClass]['cards'];
  2028. }
  2029. if (is_array($_POST['settings']['xco_gateways'])) {
  2030. foreach($_POST['settings']['xco_gateways'] as &$gateway) {
  2031. $gateway = str_replace("\\","/",stripslashes($gateway));
  2032. if (!file_exists(SHOPP_GATEWAYS.$gateway)) continue;
  2033. $meta = $this->scan_gateway_meta(SHOPP_GATEWAYS.$gateway);
  2034. $_POST['settings'][$ProcessorClass]['path'] = str_replace("\\","/",stripslashes($_POST['settings'][$ProcessorClass]['path']));
  2035. $ProcessorClass = $meta->tags['class'];
  2036. // Load the gateway in case there are any save-time processes to be run
  2037. $Processor = $Shopp->gateway($gateway);
  2038. }
  2039. }
  2040. do_action('shopp_save_payment_settings');
  2041. $this->settings_save();
  2042. $payment_gateway = stripslashes($this->Settings->get('payment_gateway'));
  2043. $updated = __('Shopp payments settings saved.','Shopp');
  2044. }
  2045. // Get all of the installed gateways
  2046. $data = $this->settings_get_gateways();
  2047. $gateways = array();
  2048. $LocalProcessors = array();
  2049. $XcoProcessors = array();
  2050. foreach ($data as $gateway) {
  2051. $ProcessorClass = $gateway->tags['class'];
  2052. include_once($gateway->file);
  2053. $processor = new $ProcessorClass();
  2054. if (isset($processor->type) && strtolower($processor->type) == "xco") {
  2055. $XcoProcessors[] = $processor;
  2056. } else {
  2057. $gateways[gateway_path($gateway->file)] = $gateway->name;
  2058. $LocalProcessors[] = $processor;
  2059. }
  2060. }
  2061. include(SHOPP_ADMINPATH."/settings/payments.php");
  2062. }
  2063. function settings_update () {
  2064. global $Shopp;
  2065. if ( !current_user_can('manage_options') )
  2066. wp_die(__('You do not have sufficient permissions to access this page.'));
  2067. $ftpsupport = (function_exists('ftp_connect'))?true:false;
  2068. $credentials = $this->Settings->get('ftp_credentials');
  2069. if (!isset($credentials['password'])) $credentials['password'] = false;
  2070. $updatekey = $this->Settings->get('updatekey');
  2071. if (empty($updatekey))
  2072. $updatekey = array('key' => '','type' => 'single','status' => 'deactivated');
  2073. if (!empty($_POST['save'])) {
  2074. $updatekey['key'] = $_POST['updatekey'];
  2075. $_POST['settings']['updatekey'] = $updatekey;
  2076. $this->settings_save();
  2077. }
  2078. if (!empty($_POST['activation'])) {
  2079. check_admin_referer('shopp-settings-update');
  2080. $updatekey['key'] = trim($_POST['updatekey']);
  2081. $_POST['settings']['updatekey'] = $updatekey;
  2082. $this->settings_save();
  2083. $request = array(
  2084. "ShoppServerRequest" => $_POST['process'],
  2085. "ver" => '1.0',
  2086. "key" => $updatekey['key'],
  2087. "type" => $updatekey['type'],
  2088. "site" => get_bloginfo('siteurl')
  2089. );
  2090. $response = $this->callhome($request);
  2091. $response = explode("::",$response);
  2092. if (count($response) == 1)
  2093. $activation = '<span class="shopp error">'.$response[0].'</span>';
  2094. if ($_POST['process'] == "activate-key" && $response[0] == "1") {
  2095. $updatekey['type'] = $response[1];
  2096. $type = $updatekey['type'];
  2097. $updatekey['key'] = $response[2];
  2098. $updatekey['status'] = 'activated';
  2099. $this->Settings->save('updatekey',$updatekey);
  2100. $activation = __('This key has been successfully activated.','Shopp');
  2101. }
  2102. if ($_POST['process'] == "deactivate-key" && $response[0] == "1") {
  2103. $updatekey['status'] = 'deactivated';
  2104. if ($updatekey['type'] == "dev") $updatekey['key'] = '';
  2105. $this->Settings->save('updatekey',$updatekey);
  2106. $activation = __('This key has been successfully de-activated.','Shopp');
  2107. }
  2108. } else {
  2109. if ($updatekey['status'] == "activated")
  2110. $activation = __('This key has been successfully activated.','Shopp');
  2111. else $activation = __('Enter your Shopp upgrade key and activate it to enable easy, automatic upgrades.','Shopp');
  2112. }
  2113. $type = "text";
  2114. if ($updatekey['status'] == "activated" && $updatekey['type'] == "dev") $type = "password";
  2115. include(SHOPP_ADMINPATH."/settings/update.php");
  2116. }
  2117. function settings_system () {
  2118. global $Shopp;
  2119. if ( !current_user_can('manage_options') )
  2120. wp_die(__('You do not have sufficient permissions to access this page.'));
  2121. $error = false;
  2122. // Image path processing
  2123. if (isset($_POST['settings']) && isset($_POST['settings']['image_storage_pref']))
  2124. $_POST['settings']['image_storage'] = $_POST['settings']['image_storage_pref'];
  2125. $imagepath = $this->Settings->get('image_path');
  2126. if (isset($_POST['settings']['products_path'])) $imagepath = $_POST['settings']['image_path'];
  2127. $imagepath_status = __("File system image hosting is enabled and working.","Shopp");
  2128. if (!file_exists($imagepath)) $error = __("The current path does not exist. Using database instead.","Shopp");
  2129. if (!is_dir($imagepath)) $error = __("The file path supplied is not a directory. Using database instead.","Shopp");
  2130. if (!is_writable($imagepath) || !is_readable($imagepath))
  2131. $error = __("Permissions error. This path must be writable by the web server. Using database instead.","Shopp");
  2132. if (empty($imagepath)) $error = __("Enter the absolute path starting from the root of the server file system to your image storage directory.","Shopp");
  2133. if ($error) {
  2134. $_POST['settings']['image_storage'] = 'db';
  2135. $imagepath_status = '<span class="error">'.$error.'</span>';
  2136. }
  2137. // Product path processing
  2138. if (isset($_POST['settings']) && isset($_POST['settings']['product_storage_pref']))
  2139. $_POST['settings']['product_storage'] = $_POST['settings']['product_storage_pref'];
  2140. $productspath = $this->Settings->get('products_path');
  2141. if (isset($_POST['settings']['products_path'])) $productspath = $_POST['settings']['products_path'];
  2142. $error = ""; // Reset the error tracker
  2143. $productspath_status = __("File system product file hosting is enabled and working.","Shopp");
  2144. if (!file_exists($productspath)) $error = __("The current path does not exist. Using database instead.","Shopp");
  2145. if (!is_dir($productspath)) $error = __("The file path supplied is not a directory. Using database instead.","Shopp");
  2146. if (!is_writable($productspath) || !is_readable($productspath))
  2147. $error = __("Permissions error. This path must be writable by the web server. Using database instead.","Shopp");
  2148. if (empty($productspath)) $error = __("Enter the absolute path starting from the root of the server file system to your product file storage directory.","Shopp");
  2149. if ($error) {
  2150. $_POST['settings']['product_storage'] = 'db';
  2151. $productspath_status = '<span class="error">'.$error.'</span>';
  2152. }
  2153. if (!empty($_POST['save'])) {
  2154. check_admin_referer('shopp-settings-system');
  2155. if (!isset($_POST['settings']['error_notifications']))
  2156. $_POST['settings']['error_notifications'] = array();
  2157. $this->settings_save();
  2158. // Reinitialize Error System
  2159. $Shopp->Cart->data->Errors = new ShoppErrors();
  2160. $Shopp->ErrorLog = new ShoppErrorLogging($this->Settings->get('error_logging'));
  2161. $Shopp->ErrorNotify = new ShoppErrorNotification($this->Settings->get('merchant_email'),
  2162. $this->Settings->get('error_notifications'));
  2163. $updated = __('Shopp system settings saved.','Shopp');
  2164. }
  2165. if (isset($_POST['resetlog'])) $Shopp->ErrorLog->reset();
  2166. $notifications = $this->Settings->get('error_notifications');
  2167. if (empty($notifications)) $notifications = array();
  2168. $notification_errors = array(
  2169. SHOPP_TRXN_ERR => __("Transaction Errors","Shopp"),
  2170. SHOPP_AUTH_ERR => __("Login Errors","Shopp"),
  2171. SHOPP_ADDON_ERR => __("Add-on Errors","Shopp"),
  2172. SHOPP_COMM_ERR => __("Communication Errors","Shopp"),
  2173. SHOPP_STOCK_ERR => __("Inventory Warnings","Shopp")
  2174. );
  2175. $errorlog_levels = array(
  2176. 0 => __("Disabled","Shopp"),
  2177. SHOPP_ERR => __("General Shopp Errors","Shopp"),
  2178. SHOPP_TRXN_ERR => __("Transaction Errors","Shopp"),
  2179. SHOPP_AUTH_ERR => __("Login Errors","Shopp"),
  2180. SHOPP_ADDON_ERR => __("Add-on Errors","Shopp"),
  2181. SHOPP_COMM_ERR => __("Communication Errors","Shopp"),
  2182. SHOPP_STOCK_ERR => __("Inventory Warnings","Shopp"),
  2183. SHOPP_ADMIN_ERR => __("Admin Errors","Shopp"),
  2184. SHOPP_DB_ERR => __("Database Errors","Shopp"),
  2185. SHOPP_PHP_ERR => __("PHP Errors","Shopp"),
  2186. SHOPP_ALL_ERR => __("All Errors","Shopp"),
  2187. SHOPP_DEBUG_ERR => __("Debugging Messages","Shopp")
  2188. );
  2189. $filesystems = array("db" => __("Database","Shopp"),"fs" => __("File System","Shopp"));
  2190. $loading = array("shopp" => __('Load on Shopp-pages only','Shopp'),"all" => __('Load on entire site','Shopp'));
  2191. if ($this->Settings->get('error_logging') > 0)
  2192. $recentlog = $Shopp->ErrorLog->tail(500);
  2193. include(SHOPP_ADMINPATH."/settings/system.php");
  2194. }
  2195. function settings_get_gateways () {
  2196. $gateway_path = $this->basepath.DIRECTORY_SEPARATOR."gateways";
  2197. $gateways = array();
  2198. $gwfiles = array();
  2199. find_files(".php",$gateway_path,$gateway_path,$gwfiles);
  2200. if (empty($gwfiles)) return $gwfiles;
  2201. foreach ($gwfiles as $file) {
  2202. if (! is_readable($gateway_path.$file)) continue;
  2203. if (! $gateway = $this->scan_gateway_meta($gateway_path.$file)) continue;
  2204. $gateways[$file] = $gateway;
  2205. }
  2206. return $gateways;
  2207. }
  2208. function validate_addons () {
  2209. $addons = array();
  2210. $gateway_path = $this->basepath.DIRECTORY_SEPARATOR."gateways";
  2211. find_files(".php",$gateway_path,$gateway_path,$gateways);
  2212. foreach ($gateways as $file) {
  2213. if (in_array(basename($file),$this->coremods)) continue;
  2214. $addons[] = md5_file($gateway_path.$file);
  2215. }
  2216. $shipping_path = $this->basepath.DIRECTORY_SEPARATOR."shipping";
  2217. find_files(".php",$shipping_path,$shipping_path,$shipmods);
  2218. foreach ($shipmods as $file) {
  2219. if (in_array(basename($file),$this->coremods)) continue;
  2220. $addons[] = md5_file($shipping_path.$file);
  2221. }
  2222. return $addons;
  2223. }
  2224. function scan_gateway_meta ($file) {
  2225. $metadata = array();
  2226. $meta = get_filemeta($file);
  2227. if ($meta) {
  2228. $lines = explode("\n",substr($meta,1));
  2229. foreach($lines as $line) {
  2230. preg_match("/^(?:[\s\*]*?\b([^@\*\/]*))/",$line,$match);
  2231. if (!empty($match[1])) $data[] = $match[1];
  2232. preg_match("/^(?:[\s\*]*?@([^\*\/]+?)\s(.+))/",$line,$match);
  2233. if (!empty($match[1]) && !empty($match[2])) $tags[$match[1]] = $match[2];
  2234. }
  2235. $gateway = new stdClass();
  2236. $gateway->file = $file;
  2237. $gateway->name = $data[0];
  2238. $gateway->description = (!empty($data[1]))?$data[1]:"";
  2239. $gateway->tags = $tags;
  2240. $gateway->activated = false;
  2241. if ($this->Settings->get('payment_gateway') == $file) $module->activated = true;
  2242. return $gateway;
  2243. }
  2244. return false;
  2245. }
  2246. function settings_save () {
  2247. if (empty($_POST['settings']) || !is_array($_POST['settings'])) return false;
  2248. foreach ($_POST['settings'] as $setting => $value)
  2249. $this->Settings->save($setting,$value);
  2250. }
  2251. /**
  2252. * Installation, upgrade and initialization functions
  2253. */
  2254. function update () {
  2255. global $Shopp,$wp_version;
  2256. $db = DB::get();
  2257. $log = array();
  2258. if (!isset($_POST['update'])) die("Update Failed: Update request is invalid. No update specified.");
  2259. if (!isset($_POST['type'])) die("Update Failed: Update request is invalid. Update type not specified");
  2260. if (!isset($_POST['password'])) die("Update Failed: Update request is invalid. No FTP password provided.");
  2261. $updatekey = $this->Settings->get('updatekey');
  2262. $credentials = $this->Settings->get('ftp_credentials');
  2263. if (empty($credentials)) {
  2264. // Try to load from WordPress settings
  2265. $credentials = get_option('ftp_credentials');
  2266. if (!$credentials) $credentials = array();
  2267. }
  2268. // Make sure we can connect to FTP
  2269. $ftp = new FTPClient($credentials['hostname'],$credentials['username'],$_POST['password']);
  2270. if (!$ftp->connected) die("ftp-failed");
  2271. else $log[] = "Connected with FTP successfully.";
  2272. // Get zip functions from WP Admin
  2273. if (class_exists('PclZip')) $log[] = "ZIP library available.";
  2274. else {
  2275. @require_once(ABSPATH.'wp-admin/includes/class-pclzip.php');
  2276. $log[] = "ZIP library loaded.";
  2277. }
  2278. // Put site in maintenance mode
  2279. if ($this->Settings->get('maintenance') != "on") {
  2280. $this->Settings->save("maintenance","on");
  2281. $log[] = "Enabled maintenance mode.";
  2282. }
  2283. // Find our temporary filesystem workspace
  2284. $tmpdir = defined('SHOPP_TEMP_PATH') ? SHOPP_TEMP_PATH : sys_get_temp_dir();
  2285. $tmpdir = str_replace('\\', '/', $tmpdir); //Windows path sanitiation
  2286. $log[] = "Found temp directory: $tmpdir";
  2287. // Download the new version of Shopp
  2288. $updatefile = tempnam($tmpdir,"shopp_update_");
  2289. if (($download = fopen($updatefile, 'wb')) === false) {
  2290. $log[] = "A temporary file could not be created under $tmpdir, trying WordPress upload directory instead.";
  2291. $tmpdir = trailingslashit(WP_CONTENT_DIR."/uploads");
  2292. $updatefile = tempnam($tmpdir,"shopp_update_");
  2293. $log[] = "Found temp directory: $tmpdir";
  2294. if (($download = fopen($updatefile, 'wb')) === false)
  2295. die(join("\n\n",$log)."\n\nUpdate Failed: Cannot save the Shopp update to the temporary workspace because of a write permission error.");
  2296. }
  2297. $query = build_query_request(array(
  2298. "ShoppServerRequest" => "download-update",
  2299. "ver" => "1.0",
  2300. ));
  2301. $data = build_query_request(array(
  2302. "key" => $updatekey['key'],
  2303. "core" => SHOPP_VERSION,
  2304. "wp" => $wp_version,
  2305. "site" => get_bloginfo('siteurl'),
  2306. "update" => $_POST['update']
  2307. ));
  2308. $connection = curl_init();
  2309. curl_setopt($connection, CURLOPT_URL, SHOPP_HOME."?".$query);
  2310. curl_setopt($connection, CURLOPT_USERAGENT, SHOPP_GATEWAY_USERAGENT);
  2311. curl_setopt($connection, CURLOPT_HEADER, 0);
  2312. curl_setopt($connection, CURLOPT_POST, 1);
  2313. curl_setopt($connection, CURLOPT_POSTFIELDS, $data);
  2314. curl_setopt($connection, CURLOPT_TIMEOUT, 20);
  2315. curl_setopt($connection, CURLOPT_FILE, $download);
  2316. curl_exec($connection);
  2317. curl_close($connection);
  2318. fclose($download);
  2319. $downloadsize = filesize($updatefile);
  2320. // Report error message returned by the server request
  2321. if (filesize($updatefile) < 256) die(join("\n\n",$log)."\nUpdate Failed: ".file_get_contents($updatefile));
  2322. // Nothing downloaded... couldn't reach the server?
  2323. if (filesize($updatefile) == 0) die(join("\n\n",$log)."\n\Update Failed: The download did not complete succesfully.");
  2324. // Download successful, log the size
  2325. $log[] = "Downloaded update of ".number_format($downloadsize)." bytes";
  2326. // Extract data
  2327. $log[] = "Unpacking updates...";
  2328. $archive = new PclZip($updatefile);
  2329. $files = $archive->extract(PCLZIP_OPT_EXTRACT_AS_STRING);
  2330. if (!is_array($files)) die(join("\n\n",$log)."\n\nUpdate Failed: The downloaded update did not complete or is corrupted and cannot be used.");
  2331. else unlink($updatefile);
  2332. $target = trailingslashit($tmpdir);
  2333. // Move old updates that still exist in $tmpdir to a new location
  2334. if (file_exists($target.$files[0]['filename'])
  2335. && is_dir($target.$files[0]['filename']))
  2336. rename($target.$files[0]['filename'],$updatefile.'_old_update');
  2337. // Create file structure in working path target
  2338. foreach ($files as $file) {
  2339. if (!$file['folder'] ) {
  2340. if (file_put_contents($target.$file['filename'], $file['content']))
  2341. @chmod($target.$file['filename'], 0644);
  2342. } else {
  2343. if (!is_dir($target.$file['filename'])) {
  2344. if (!@mkdir($target.$file['filename'],0755,true))
  2345. die(join("\n\n",$log)."\n\nUpdate Failed: Couldn't create directory $target{$file['filename']}");
  2346. }
  2347. }
  2348. }
  2349. $log[] = "Successfully unpacked the update.";
  2350. // FTP files to make it "easier" than dealing with permissions
  2351. $log[] = "Updating files via FTP connection";
  2352. switch($_POST['type']) {
  2353. case "core":
  2354. $results = $ftp->update($target.$files[0]['filename'],$Shopp->path);
  2355. if (!empty($results)) die(join("\n\n",$log).join("\n\n",$results)."\n\nFTP transfer failed.");
  2356. break;
  2357. case "Payment Gateway":
  2358. $results = $ftp->update($target.$files[0]['filename'],
  2359. $Shopp->path.DIRECTORY_SEPARATOR."gateways".DIRECTORY_SEPARATOR.$files[0]['filename']);
  2360. if (!empty($results)) die(join("\n\n",$log).join("\n\n",$results)."\n\nFTP transfer failed.");
  2361. break;
  2362. case "Shipping Module":
  2363. $results = $ftp->update($target.$files[0]['filename'],
  2364. $Shopp->path.DIRECTORY_SEPARATOR."shipping".DIRECTORY_SEPARATOR.$files[0]['filename']);
  2365. if (!empty($results)) die(join("\n\n",$log).join("\n\n",$results)."\n\nFTP transfer failed.");
  2366. break;
  2367. }
  2368. echo "updated"; // Report success!
  2369. exit();
  2370. }
  2371. function upgrade () {
  2372. global $Shopp,$table_prefix;
  2373. $db = DB::get();
  2374. require_once(ABSPATH.'wp-admin/includes/upgrade.php');
  2375. // Check for the schema definition file
  2376. if (!file_exists(SHOPP_DBSCHEMA))
  2377. die("Could not upgrade the Shopp database tables because the table definitions file is missing: ".SHOPP_DBSCHEMA);
  2378. ob_start();
  2379. include(SHOPP_DBSCHEMA);
  2380. $schema = ob_get_contents();
  2381. ob_end_clean();
  2382. // Update the table schema
  2383. $tables = preg_replace('/;\s+/',';',$schema);
  2384. dbDelta($tables);
  2385. $this->setup_regions();
  2386. $this->setup_countries();
  2387. $this->setup_zones();
  2388. $this->setup_areas();
  2389. $this->setup_vat();
  2390. // Update the version number
  2391. $settings = DatabaseObject::tablename(Settings::$table);
  2392. $db->query("UPDATE $settings SET value='".SHOPP_VERSION." WHERE name='version'");
  2393. $db->query("DELETE FROM $settings WHERE name='data_model' OR name='shipcalc_lastscan");
  2394. return true;
  2395. }
  2396. function callhome ($request=array(),$data=array()) {
  2397. $query = build_query_request($request);
  2398. $data = build_query_request($data);
  2399. $connection = curl_init();
  2400. curl_setopt($connection, CURLOPT_URL, SHOPP_HOME."?".$query);
  2401. curl_setopt($connection, CURLOPT_USERAGENT, SHOPP_GATEWAY_USERAGENT);
  2402. curl_setopt($connection, CURLOPT_HEADER, 0);
  2403. curl_setopt($connection, CURLOPT_POST, 1);
  2404. curl_setopt($connection, CURLOPT_POSTFIELDS, $data);
  2405. curl_setopt($connection, CURLOPT_TIMEOUT, 20);
  2406. curl_setopt($connection, CURLOPT_RETURNTRANSFER, 1);
  2407. $result = curl_exec($connection);
  2408. curl_close ($connection);
  2409. return $result;
  2410. }
  2411. /**
  2412. * setup()
  2413. * Initialize default install settings and lists */
  2414. function setup () {
  2415. $this->setup_regions();
  2416. $this->setup_countries();
  2417. $this->setup_zones();
  2418. $this->setup_areas();
  2419. $this->setup_vat();
  2420. $this->Settings->save('show_welcome','on');
  2421. $this->Settings->save('display_welcome','on');
  2422. // General Settings
  2423. $this->Settings->save('version',SHOPP_VERSION);
  2424. $this->Settings->save('shipping','on');
  2425. $this->Settings->save('order_status',array('Pending','Completed'));
  2426. $this->Settings->save('shopp_setup','completed');
  2427. $this->Settings->save('maintenance','off');
  2428. $this->Settings->save('dashboard','on');
  2429. // Checkout Settings
  2430. $this->Settings->save('order_confirmation','ontax');
  2431. $this->Settings->save('receipt_copy','1');
  2432. $this->Settings->save('account_system','none');
  2433. // Presentation Settings
  2434. $this->Settings->save('theme_templates','off');
  2435. $this->Settings->save('row_products','3');
  2436. $this->Settings->save('catalog_pagination','25');
  2437. $this->Settings->save('product_image_order','ASC');
  2438. $this->Settings->save('product_image_orderby','sortorder');
  2439. $this->Settings->save('gallery_small_width','240');
  2440. $this->Settings->save('gallery_small_height','240');
  2441. $this->Settings->save('gallery_small_sizing','1');
  2442. $this->Settings->save('gallery_small_quality','2');
  2443. $this->Settings->save('gallery_thumbnail_width','96');
  2444. $this->Settings->save('gallery_thumbnail_height','96');
  2445. $this->Settings->save('gallery_thumbnail_sizing','1');
  2446. $this->Settings->save('gallery_thumbnail_quality','3');
  2447. // System Settinggs
  2448. $this->Settings->save('image_storage_pref','db');
  2449. $this->Settings->save('product_storage_pref','db');
  2450. $this->Settings->save('uploader_pref','flash');
  2451. $this->Settings->save('script_loading','global');
  2452. // Payment Gateway Settings
  2453. $this->Settings->save('PayPalExpress',array('enabled'=>'off'));
  2454. $this->Settings->save('GoogleCheckout',array('enabled'=>'off'));
  2455. }
  2456. function setup_regions () {
  2457. global $Shopp;
  2458. include_once("init.php");
  2459. $this->Settings->save('regions',get_global_regions());
  2460. }
  2461. function setup_countries () {
  2462. global $Shopp;
  2463. include_once("init.php");
  2464. $this->Settings->save('countries',addslashes(serialize(get_countries())),false);
  2465. }
  2466. function setup_zones () {
  2467. global $Shopp;
  2468. include_once("init.php");
  2469. $this->Settings->save('zones',get_country_zones(),false);
  2470. }
  2471. function setup_areas () {
  2472. global $Shopp;
  2473. include_once("init.php");
  2474. $this->Settings->save('areas',get_country_areas(),false);
  2475. }
  2476. function setup_vat () {
  2477. global $Shopp;
  2478. include_once("init.php");
  2479. $this->Settings->save('vat_countries',get_vat_countries(),false);
  2480. }
  2481. }
  2482. ?>