PageRenderTime 56ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/blog/wp-content/plugins/shopp/core/model/Product.php

https://github.com/kennethreitz-archive/wordpress-skeleton
PHP | 1327 lines | 1083 code | 173 blank | 71 comment | 442 complexity | cf4104245eab87dca1b26ad8218c03da MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * Product class
  4. * Catalog products
  5. *
  6. * @author Jonathan Davis
  7. * @version 1.0
  8. * @copyright Ingenesis Limited, 28 March, 2008
  9. * @package shopp
  10. **/
  11. require("Asset.php");
  12. require("Spec.php");
  13. require("Price.php");
  14. require("Promotion.php");
  15. class Product extends DatabaseObject {
  16. static $table = "product";
  17. var $prices = array();
  18. var $pricekey = array();
  19. var $priceid = array();
  20. var $pricerange = array('max'=>array(),'min'=>array());
  21. var $onsale = false;
  22. var $categories = array();
  23. var $tags = array();
  24. var $images = array();
  25. var $imagesets = array();
  26. var $imageset = false;
  27. var $specs = array();
  28. var $ranges = array('max'=>array(),'min'=>array());
  29. var $freeshipping = false;
  30. var $priceloop = false;
  31. var $specloop = false;
  32. var $categoryloop = false;
  33. var $imageloop = false;
  34. var $tagloop = false;
  35. var $outofstock = false;
  36. var $stock = 0;
  37. var $options = 0;
  38. function Product ($id=false,$key=false) {
  39. $this->init(self::$table);
  40. if ($this->load($id,$key)) {
  41. add_filter('shopp_product_description', 'wptexturize');
  42. add_filter('shopp_product_description', 'convert_chars');
  43. add_filter('shopp_product_description', 'wpautop');
  44. add_filter('shopp_product_description', 'do_shortcode', 11); // AFTER wpautop()
  45. add_filter('shopp_product_spec', 'wptexturize');
  46. add_filter('shopp_product_spec', 'convert_chars');
  47. add_filter('shopp_product_spec', 'do_shortcode', 11); // AFTER wpautop()
  48. return true;
  49. }
  50. return false;
  51. }
  52. function load_data ($options=false,&$products=false) {
  53. global $Shopp;
  54. $db =& DB::get();
  55. // Load object schemas on request
  56. $Dataset = array();
  57. if (in_array('prices',$options)) {
  58. $promotable = DatabaseObject::tablename(Promotion::$table);
  59. $discounttable = DatabaseObject::tablename(Discount::$table);
  60. $assettable = DatabaseObject::tablename(Asset::$table);
  61. $Dataset['prices'] = new Price();
  62. $Dataset['prices']->_datatypes['promos'] = "MAX(promo.status)";
  63. $Dataset['prices']->_datatypes['promotions'] = "group_concat(promo.name)";
  64. $Dataset['prices']->_datatypes['percentoff'] = "SUM(IF (promo.type='Percentage Off',promo.discount,0))";
  65. $Dataset['prices']->_datatypes['amountoff'] = "SUM(IF (promo.type='Amount Off',promo.discount,0))";
  66. $Dataset['prices']->_datatypes['freeshipping'] = "SUM(IF (promo.type='Free Shipping',1,0))";
  67. $Dataset['prices']->_datatypes['buyqty'] = "IF (promo.type='Buy X Get Y Free',promo.buyqty,0)";
  68. $Dataset['prices']->_datatypes['getqty'] = "IF (promo.type='Buy X Get Y Free',promo.getqty,0)";
  69. $Dataset['prices']->_datatypes['download'] = "download.id";
  70. $Dataset['prices']->_datatypes['filename'] = "download.name";
  71. $Dataset['prices']->_datatypes['filedata'] = "download.properties";
  72. $Dataset['prices']->_datatypes['filesize'] = "download.size";
  73. }
  74. if (in_array('images',$options)) {
  75. $Dataset['images'] = new Asset();
  76. unset($Dataset['images']->_datatypes['data']);
  77. }
  78. if (in_array('categories',$options)) {
  79. $Dataset['categories'] = new Category();
  80. unset($Dataset['categories']->_datatypes['priceranges']);
  81. unset($Dataset['categories']->_datatypes['specs']);
  82. unset($Dataset['categories']->_datatypes['options']);
  83. unset($Dataset['categories']->_datatypes['prices']);
  84. }
  85. if (in_array('specs',$options)) $Dataset['specs'] = new Spec();
  86. if (in_array('tags',$options)) $Dataset['tags'] = new Tag();
  87. // Determine the maximum columns to allocate
  88. $maxcols = 0;
  89. foreach ($Dataset as $set) {
  90. $cols = count($set->_datatypes);
  91. if ($cols > $maxcols) $maxcols = $cols;
  92. }
  93. // Prepare product list depending on single product or entire list
  94. $ids = array();
  95. if (isset($products) && is_array($products)) {
  96. foreach ($products as $product) $ids[] = $product->id;
  97. } else $ids[0] = $this->id;
  98. // Skip if there are no product ids
  99. if (empty($ids) || empty($ids[0])) return false;
  100. // Build the mega-query
  101. foreach ($Dataset as $rtype => $set) {
  102. // Allocate generic columns for record data
  103. $columns = array(); $i = 0;
  104. foreach ($set->_datatypes as $key => $datatype)
  105. $columns[] = ((strpos($datatype,'.')!==false)?"$datatype":"{$set->_table}.$key")." AS c".($i++);
  106. for ($i = $i; $i < $maxcols; $i++)
  107. $columns[] = "'' AS c$i";
  108. $cols = join(',',$columns);
  109. // Build object-specific selects and UNION them
  110. $where = "";
  111. if (isset($query)) $query .= " UNION ";
  112. else $query = "";
  113. switch($rtype) {
  114. case "prices":
  115. foreach ($ids as $id) $where .= ((!empty($where))?" OR ":"")."$set->_table.product=$id";
  116. $query .= "(SELECT '$set->_table' as dataset,$set->_table.product AS product,'$rtype' AS rtype,'' AS alphaorder,$set->_table.sortorder AS sortorder,$cols FROM $set->_table
  117. LEFT JOIN $assettable AS download ON $set->_table.id=download.parent AND download.context='price' AND download.datatype='download'
  118. LEFT JOIN $discounttable AS discount ON discount.product=$set->_table.product AND discount.price=$set->_table.id
  119. LEFT JOIN $promotable AS promo ON promo.id=discount.promo AND (promo.status='enabled' AND ((UNIX_TIMESTAMP(starts)=1 AND UNIX_TIMESTAMP(ends)=1) OR (UNIX_TIMESTAMP(now()) > UNIX_TIMESTAMP(starts) AND UNIX_TIMESTAMP(now()) < UNIX_TIMESTAMP(ends)) ))
  120. WHERE $where GROUP BY $set->_table.id)";
  121. break;
  122. case "images":
  123. $ordering = $Shopp->Settings->get('product_image_order');
  124. if (empty($ordering)) $ordering = "ASC";
  125. $orderby = $Shopp->Settings->get('product_image_orderby');
  126. $sortorder = "0";
  127. if ($orderby == "sortorder" || $orderby == "created") {
  128. if ($orderby == "created") $orderby = "UNIX_TIMESTAMP(created)";
  129. switch ($ordering) {
  130. case "DESC": $sortorder = "$orderby*-1"; break;
  131. case "RAND": $sortorder = "RAND()"; break;
  132. default: $sortorder = "$orderby";
  133. }
  134. }
  135. $alphaorder = "''";
  136. if ($orderby == "name") {
  137. switch ($ordering) {
  138. case "DESC": $alphaorder = "$orderby"; break;
  139. case "RAND": $alphaorder = "RAND()"; break;
  140. default: $alphaorder = "$orderby";
  141. }
  142. }
  143. foreach ($ids as $id) $where .= ((!empty($where))?" OR ":"")."parent=$id";
  144. $where = "($where) AND context='product'";
  145. $query .= "(SELECT '$set->_table' as dataset,parent AS product,'$rtype' AS rtype,$alphaorder AS alphaorder,$sortorder AS sortorder,$cols FROM $set->_table WHERE $where ORDER BY $orderby)";
  146. break;
  147. case "specs":
  148. foreach ($ids as $id) $where .= ((!empty($where))?" OR ":"")."product=$id";
  149. $query .= "(SELECT '$set->_table' as dataset,product,'$rtype' AS rtype,'' AS alphaorder,sortorder AS sortorder,$cols FROM $set->_table WHERE $where)";
  150. break;
  151. case "categories":
  152. foreach ($ids as $id) $where .= ((!empty($where))?" OR ":"")."catalog.product=$id";
  153. $where = "($where) AND catalog.category > 0";
  154. $query .= "(SELECT '$set->_table' as dataset,catalog.product AS product,'$rtype' AS rtype,$set->_table.name AS alphaorder,0 AS sortorder,$cols FROM {$Shopp->Catalog->_table} AS catalog LEFT JOIN $set->_table ON catalog.category=$set->_table.id WHERE $where)";
  155. break;
  156. case "tags":
  157. foreach ($ids as $id) $where .= ((!empty($where))?" OR ":"")."catalog.product=$id";
  158. $where = "($where) AND catalog.tag > 0";
  159. $query .= "(SELECT '$set->_table' as dataset,catalog.product AS product,'$rtype' AS rtype,$set->_table.name AS alphaorder,0 AS sortorder,$cols FROM {$Shopp->Catalog->_table} AS catalog LEFT JOIN $set->_table ON catalog.tag=$set->_table.id WHERE $where)";
  160. break;
  161. }
  162. }
  163. // Add order by columns
  164. $query .= " ORDER BY sortorder";
  165. // echo $query;
  166. // Execute the query
  167. $data = $db->query($query,AS_ARRAY);
  168. // Process the results into specific product object data in a product set
  169. foreach ($data as $row) {
  170. if (is_array($products) && isset($products[$row->product]))
  171. $target = $products[$row->product];
  172. else $target = $this;
  173. $record = new stdClass(); $i = 0; $name = "";
  174. foreach ($Dataset[$row->rtype]->_datatypes AS $key => $datatype) {
  175. $column = 'c'.$i++;
  176. $record->{$key} = '';
  177. if ($key == "name") $name = $row->{$column};
  178. if (!empty($row->{$column})) {
  179. if (preg_match("/^[sibNaO](?:\:.+?\{.*\}$|\:.+;$|;$)/",$row->{$column}))
  180. $row->{$column} = unserialize($row->{$column});
  181. $record->{$key} = $row->{$column};
  182. }
  183. }
  184. $target->{$row->rtype}[] = $record;
  185. if (!empty($name)) {
  186. if (isset($target->{$row->rtype.'key'}[$name]))
  187. $target->{$row->rtype.'key'}[$name] = array($target->{$row->rtype.'key'}[$name],$record);
  188. else $target->{$row->rtype.'key'}[$name] = $record;
  189. }
  190. }
  191. if (is_array($products)) {
  192. foreach ($products as $product) if (!empty($product->prices)) $product->pricing();
  193. foreach ($products as $product) if (count($product->images) >= 3 && count($product->imagesets) <= 1)
  194. $product->imageset();
  195. } else {
  196. if (!empty($this->prices)) $this->pricing($options);
  197. if (count($this->images) >= 3 && count($this->imagesets) <= 1) $this->imageset();
  198. }
  199. } // end load_data()
  200. function pricing ($options = false) {
  201. global $Shopp;
  202. $variations = ($this->variations == "on");
  203. $freeshipping = true;
  204. $this->inventory = false;
  205. foreach ($this->prices as $i => &$price) {
  206. // Build secondary lookup table using the combined optionkey
  207. $this->pricekey[$price->optionkey] = $price;
  208. // Build third lookup table using the price id as the key
  209. $this->priceid[$price->id] = $price;
  210. if ($price->type == "N/A" || ($i > 0 && !$variations)) continue;
  211. // Boolean flag for custom product sales
  212. $price->onsale = false;
  213. if ($price->sale == "on" && $price->type != "N/A")
  214. $this->onsale = $price->onsale = true;
  215. $price->stocked = false;
  216. if ($price->inventory == "on" && $price->type != "N/A") {
  217. $this->stock += $price->stock;
  218. $this->inventory = $price->stocked = true;
  219. }
  220. if ($price->freeshipping == 0) $freeshipping = false;
  221. if ($price->onsale) $price->promoprice = (float)$price->saleprice;
  222. else $price->promoprice = (float)$price->price;
  223. if ((isset($price->promos) && $price->promos == 'enabled')) {
  224. if ($price->percentoff > 0) {
  225. $price->promoprice = $price->promoprice - ($price->promoprice * ($price->percentoff/100));
  226. $this->onsale = $price->onsale = true;
  227. }
  228. if ($price->amountoff > 0) {
  229. $price->promoprice = $price->promoprice - $price->amountoff;
  230. $this->onsale = $price->onsale = true;;
  231. }
  232. }
  233. // Grab price and saleprice ranges (minimum - maximum)
  234. if ($price->type != "N/A") {
  235. if (!$price->price) $price->price = 0;
  236. if (!isset($this->pricerange['min']['price']))
  237. $this->pricerange['min']['price'] = $this->pricerange['max']['price'] = $price->price;
  238. if ($this->pricerange['min']['price'] > $price->price)
  239. $this->pricerange['min']['price'] = $price->price;
  240. if ($this->pricerange['max']['price'] < $price->price)
  241. $this->pricerange['max']['price'] = $price->price;
  242. if (!isset($this->pricerange['min']['saleprice']))
  243. $this->pricerange['min']['saleprice'] = $this->pricerange['max']['saleprice'] = $price->promoprice;
  244. if ($this->pricerange['min']['saleprice'] > $price->promoprice)
  245. $this->pricerange['min']['saleprice'] = $price->promoprice;
  246. if ($this->pricerange['max']['saleprice'] < $price->promoprice)
  247. $this->pricerange['max']['saleprice'] = $price->promoprice;
  248. if ($price->stocked) {
  249. if (!isset($this->pricerange['min']['stock']))
  250. $this->pricerange['min']['stock'] = $this->pricerange['max']['stock'] = $price->stock;
  251. if ($this->pricerange['min']['stock'] > $price->stock)
  252. $this->pricerange['min']['stock'] = $price->stock;
  253. if ($this->pricerange['max']['stock'] < $price->stock)
  254. $this->pricerange['max']['stock'] = $price->stock;
  255. }
  256. }
  257. // Determine savings ranges
  258. if ($price->onsale
  259. && isset($this->pricerange['min']['price'])
  260. && isset($this->pricerange['min']['saleprice'])) {
  261. if (!isset($this->pricerange['min']['saved'])) {
  262. $this->pricerange['min']['saved'] = $price->price;
  263. $this->pricerange['min']['savings'] = 100;
  264. $this->pricerange['max']['saved'] = 0;
  265. $this->pricerange['max']['savings'] = 0;
  266. }
  267. if ($price->price - $price->promoprice < $this->pricerange['min']['saved'])
  268. $this->pricerange['min']['saved'] =
  269. $price->price - $price->promoprice;
  270. if ($price->price - $price->promoprice > $this->pricerange['max']['saved'])
  271. $this->pricerange['max']['saved'] =
  272. $price->price - $price->promoprice;
  273. // Find lowest savings percentage
  274. if ($price->onsale) {
  275. if ($this->pricerange['min']['saved']/$price->price < $this->pricerange['min']['savings'])
  276. $this->pricerange['min']['savings'] = ($this->pricerange['min']['saved']/$price->price)*100;
  277. if ($this->pricerange['max']['saved']/$price->price < $this->pricerange['min']['savings'])
  278. $this->pricerange['min']['savings'] = ($this->pricerange['max']['saved']/$price->price)*100;
  279. // Find highest savings percentage
  280. if ($this->pricerange['min']['saved']/$price->price > $this->pricerange['max']['savings'])
  281. $this->pricerange['max']['savings'] = ($this->pricerange['min']['saved']/$price->price)*100;
  282. if ($this->pricerange['max']['saved']/$price->price > $this->pricerange['max']['savings'])
  283. $this->pricerange['max']['savings'] = ($this->pricerange['max']['saved']/$price->price)*100;
  284. }
  285. }
  286. // Determine weight ranges
  287. if(!isset($this->weightrange['min'])) $this->weightrange = array('min'=>0,'max'=>0);
  288. if($price->weight && $price->weight > 0){
  289. if(!$this->weightrange['min'] || $price->weight < $this->weightrange['min'])
  290. $this->weightrange['min'] = $price->weight;
  291. if(!$this->weightrange['max'] || $price->weight > $this->weightrange['max'])
  292. $this->weightrange['max'] = $price->weight;
  293. }
  294. if (defined('WP_ADMIN') && !isset($options['taxes'])) $options['taxes'] = true;
  295. if (defined('WP_ADMIN') && value_is_true($options['taxes']) && $price->tax == "on") {
  296. $base = $Shopp->Settings->get('base_operations');
  297. if ($base['vat']) {
  298. $taxrate = $Shopp->Cart->taxrate();
  299. $price->price += $price->price*$taxrate;
  300. $price->saleprice += $price->saleprice*$taxrate;
  301. }
  302. }
  303. } // end foreach($price)
  304. if ($this->inventory && $this->stock <= 0) $this->outofstock = true;
  305. if ($freeshipping) $this->freeshipping = true;
  306. }
  307. function imageset () {
  308. global $Shopp;
  309. // Organize images into groupings by type
  310. $this->imagesets = array();
  311. foreach ($this->images as $key => &$image) {
  312. if (empty($this->imagesets[$image->datatype])) $this->imagesets[$image->datatype] = array();
  313. if ($image->id) {
  314. if (SHOPP_PERMALINKS) $image->uri = $Shopp->imguri.$image->id;
  315. else $image->uri = add_query_arg('shopp_image',$image->id,$Shopp->imguri);
  316. }
  317. $this->imagesets[$image->datatype][] = $image;
  318. }
  319. $this->thumbnail = $this->imagesets['thumbnail'][0];
  320. return true;
  321. }
  322. function merge_specs () {
  323. $merged = array();
  324. foreach ($this->specs as $key => $spec) {
  325. if (!isset($merged[$spec->name])) $merged[$spec->name] = $spec;
  326. else {
  327. if (!is_array($merged[$spec->name]->content))
  328. $merged[$spec->name]->content = array($merged[$spec->name]->content);
  329. $merged[$spec->name]->content[] = $spec->content;
  330. }
  331. }
  332. $this->specs = $merged;
  333. }
  334. function save_categories ($updates) {
  335. $db = DB::get();
  336. if (empty($updates)) $updates = array();
  337. $current = array();
  338. foreach ($this->categories as $category) $current[] = $category->id;
  339. $added = array_diff($updates,$current);
  340. $removed = array_diff($current,$updates);
  341. $table = DatabaseObject::tablename(Catalog::$table);
  342. if (!empty($added)) {
  343. foreach ($added as $id) {
  344. if (empty($id)) continue;
  345. $db->query("INSERT $table SET category='$id',product='$this->id',created=now(),modified=now()");
  346. }
  347. }
  348. if (!empty($removed)) {
  349. foreach ($removed as $id) {
  350. if (empty($id)) continue;
  351. $db->query("DELETE LOW_PRIORITY FROM $table WHERE category='$id' AND product='$this->id'");
  352. }
  353. }
  354. }
  355. function save_tags ($updates) {
  356. $db = DB::get();
  357. if (empty($updates)) $updates = array();
  358. $updates = stripslashes_deep($updates);
  359. $current = array();
  360. foreach ($this->tags as $tag) $current[] = $tag->name;
  361. $added = array_diff($updates,$current);
  362. $removed = array_diff($current,$updates);
  363. if (!empty($added)) {
  364. $catalog = DatabaseObject::tablename(Catalog::$table);
  365. $tagtable = DatabaseObject::tablename(Tag::$table);
  366. $where = "";
  367. foreach ($added as $tag) $where .= ($where == ""?"":" OR ")."name='".$db->escape($tag)."'";
  368. $results = $db->query("SELECT id,name FROM $tagtable WHERE $where",AS_ARRAY);
  369. $exists = array();
  370. foreach ($results as $tag) $exists[$tag->id] = $tag->name;
  371. foreach ($added as $tag) {
  372. if (empty($tag)) continue; // No empty tags
  373. $tagid = array_search($tag,$exists);
  374. if (!$tagid) {
  375. $Tag = new Tag();
  376. $Tag->name = $tag;
  377. $Tag->save();
  378. $tagid = $Tag->id;
  379. }
  380. if (!empty($tagid))
  381. $db->query("INSERT $catalog SET tag='$tagid',product='$this->id',created=now(),modified=now()");
  382. }
  383. }
  384. if (!empty($removed)) {
  385. $catalog = DatabaseObject::tablename(Catalog::$table);
  386. foreach ($removed as $tag) {
  387. $Tag = new Tag($tag,'name');
  388. if (!empty($Tag->id))
  389. $db->query("DELETE LOW_PRIORITY FROM $catalog WHERE tag='$Tag->id' AND product='$this->id'");
  390. }
  391. }
  392. }
  393. /**
  394. * optionkey
  395. * There is no Zul only XOR! */
  396. function optionkey ($ids=array(),$deprecated=false) {
  397. if ($deprecated) $factor = 101;
  398. else $factor = 7001;
  399. if (empty($ids)) return 0;
  400. foreach ($ids as $set => $id)
  401. $key = $key ^ ($id*$factor);
  402. return $key;
  403. }
  404. /**
  405. * save_imageorder()
  406. * Updates the sortorder of image assets (source, featured and thumbnails)
  407. * based on the provided array of image ids */
  408. function save_imageorder ($ordering) {
  409. $db = DB::get();
  410. $table = DatabaseObject::tablename(Asset::$table);
  411. foreach ($ordering as $i => $id)
  412. $db->query("UPDATE LOW_PRIORITY $table SET sortorder='$i' WHERE id='$id' OR src='$id'");
  413. return true;
  414. }
  415. /**
  416. * link_images()
  417. * Updates the product id of the images to link to the product
  418. * when the product being saved is new (has no previous id assigned) */
  419. function link_images ($images) {
  420. $db = DB::get();
  421. $table = DatabaseObject::tablename(Asset::$table);
  422. $query = "";
  423. foreach ($images as $i => $id) {
  424. if (empty($id)) continue;
  425. if ($i > 0) $query .= " OR ";
  426. $query .= "id=$id OR src=$id";
  427. }
  428. if (empty($query)) return false;
  429. else $query = "UPDATE $table SET parent='$this->id',context='product' WHERE ".$query;
  430. $db->query($query);
  431. return true;
  432. }
  433. /**
  434. * update_images()
  435. * Updates the image details for an entire image set (thumbnail, small, image) */
  436. function update_images ($images) {
  437. if (!is_array($images)) return false;
  438. $db = DB::get();
  439. $table = DatabaseObject::tablename(Asset::$table);
  440. foreach($images as $i => $img) {
  441. $query = "SELECT imgs.id FROM $table AS thumb LEFT JOIN $table AS imgs ON thumb.src=imgs.src OR thumb.src=imgs.id WHERE thumb.id={$img['id']}";
  442. $imageset = $db->query($query);
  443. foreach ($imageset as $is) {
  444. $Image = new Asset();
  445. unset($Image->_datatypes['data'],$Image->data);
  446. $Image->load($is->id);
  447. $Image->properties['title'] = $img['title'];
  448. $Image->properties['alt'] = $img['alt'];
  449. $Image->save();
  450. }
  451. }
  452. return true;
  453. }
  454. /**
  455. * delete_images()
  456. * Delete provided array of image ids, removing the source image and
  457. * all related images (small and thumbnails) */
  458. function delete_images ($images) {
  459. $Images = new Asset();
  460. $Images->deleteset($images,'image');
  461. return true;
  462. }
  463. /**
  464. * Deletes the record associated with this object */
  465. function delete () {
  466. $db = DB::get();
  467. $id = $this->{$this->_key};
  468. if (empty($id)) return false;
  469. // Delete from categories
  470. $table = DatabaseObject::tablename(Catalog::$table);
  471. $db->query("DELETE LOW_PRIORITY FROM $table WHERE product='$id'");
  472. // Delete prices
  473. $table = DatabaseObject::tablename(Price::$table);
  474. $db->query("DELETE LOW_PRIORITY FROM $table WHERE product='$id'");
  475. // Delete specs
  476. $table = DatabaseObject::tablename(Spec::$table);
  477. $db->query("DELETE LOW_PRIORITY FROM $table WHERE product='$id'");
  478. // Delete images/files
  479. $table = DatabaseObject::tablename(Asset::$table);
  480. // Delete images
  481. $images = array();
  482. $src = $db->query("SELECT id FROM $table WHERE parent='$id' AND context='product' AND datatype='image'",AS_ARRAY);
  483. foreach ($src as $img) $images[] = $img->id;
  484. $this->delete_images($images);
  485. // Delete product downloads (but keep the file if on file system)
  486. $db->query("DELETE LOW_PRIORITY FROM $table WHERE parent='$id' AND context='product'");
  487. // Delete record
  488. $db->query("DELETE FROM $this->_table WHERE $this->_key='$id'");
  489. }
  490. function duplicate () {
  491. $db =& DB::get();
  492. $this->load_data(array('prices','specs','categories','tags','images','taxes'=>'false'));
  493. $this->id = '';
  494. $this->name = $this->name.' '.__('copy','Shopp');
  495. $this->slug = sanitize_title_with_dashes($this->name);
  496. // Check for an existing product slug
  497. $existing = $db->query("SELECT slug FROM $this->_table WHERE slug='$this->slug' LIMIT 1");
  498. if ($existing) {
  499. $suffix = 2;
  500. while($existing) {
  501. $altslug = substr($this->slug, 0, 200-(strlen($suffix)+1)). "-$suffix";
  502. $existing = $db->query("SELECT slug FROM $this->_table WHERE slug='$altslug' LIMIT 1");
  503. $suffix++;
  504. }
  505. $this->slug = $altslug;
  506. }
  507. $this->created = '';
  508. $this->modified = '';
  509. $this->save();
  510. // Copy prices
  511. foreach ($this->prices as $price) {
  512. $Price = new Price();
  513. $Price->updates($price,array('id','product','created','modified'));
  514. $Price->product = $this->id;
  515. $Price->save();
  516. }
  517. // Copy sepcs
  518. foreach ($this->specs as $spec) {
  519. $Spec = new Spec();
  520. $Spec->updates($spec,array('id','product','created','modified'));
  521. $Spec->product = $this->id;
  522. $Spec->save();
  523. }
  524. // Copy categories
  525. $categories = array();
  526. foreach ($this->categories as $category) $categories[] = $category->id;
  527. $this->categories = array();
  528. $this->save_categories($categories);
  529. // Copy tags
  530. $taglist = array();
  531. foreach ($this->tags as $tag) $taglist[] = $tag->name;
  532. $this->tags = array();
  533. $this->save_tags($taglist);
  534. // // Copy product images
  535. $template = new Asset();
  536. $columns = array(); $values = array();
  537. foreach ($template->_datatypes as $name => $type) {
  538. $colname = $name;
  539. $columns[$colname] = $name;
  540. if ($name == "id") $name = "''";
  541. if ($name == "parent") $name = "'$this->id'";
  542. if ($name == "created" || $name == "modified") $name = "now()";
  543. $values[$colname] = $name;
  544. }
  545. $sets = array('image','small','thumbnail');
  546. $images = array();
  547. foreach ($sets as $set) {
  548. foreach ($this->imagesets[$set] as $image) {
  549. if (isset($images[$image->src])) $values['src'] = $images[$image->src];
  550. $id = $db->query("INSERT $template->_table (".join(',',$columns).") SELECT ".join(",",$values)." FROM $template->_table WHERE id=$image->id");
  551. if ($set == "image") {
  552. $images[$image->id] = $id;
  553. $db->query("UPDATE $template->_table SET src=$id WHERE id=$id LIMIT 1");
  554. }
  555. }
  556. }
  557. }
  558. function tag ($property,$options=array()) {
  559. global $Shopp;
  560. switch ($property) {
  561. case "link":
  562. case "url":
  563. if (SHOPP_PERMALINKS) $url = esc_url(add_query_arg($_GET,$Shopp->shopuri.urldecode($this->slug)."/"));
  564. else $url = add_query_arg('shopp_pid',$this->id,$Shopp->shopuri);
  565. return $url;
  566. break;
  567. case "found":
  568. if (empty($this->id)) return false;
  569. $load = array('prices','images','specs');
  570. if (isset($options['load'])) $load = explode(",",$options['load']);
  571. $this->load_data($load);
  572. return true;
  573. break;
  574. case "id": return $this->id; break;
  575. case "name": return $this->name; break;
  576. case "slug": return $this->slug; break;
  577. case "summary": return $this->summary; break;
  578. case "description":
  579. return apply_filters('shopp_product_description',$this->description);
  580. case "isfeatured":
  581. case "is-featured":
  582. return ($this->featured == "on"); break;
  583. case "price":
  584. if (empty($this->prices)) $this->load_data(array('prices'));
  585. if (!isset($options['taxes'])) $options['taxes'] = null;
  586. // $taxrate = 0;
  587. // $taxes = false;
  588. // $base = $Shopp->Settings->get('base_operations');
  589. // if ($base['vat']) $taxes = true;
  590. // if (isset($options['taxes'])) $taxes = (value_is_true($options['taxes']));
  591. // if ($taxes) $taxrate = $Shopp->Cart->taxrate();
  592. if (count($this->options) > 0) {
  593. $taxrate = shopp_taxrate($options['taxes']);
  594. if ($this->pricerange['min']['price'] == $this->pricerange['max']['price'])
  595. return money($this->pricerange['min']['price'] + ($this->pricerange['min']['price']*$taxrate));
  596. else {
  597. if (!empty($options['starting'])) return $options['starting']." ".money($this->pricerange['min']['price']+($this->pricerange['min']['price']*$taxrate));
  598. return money($this->pricerange['min']['price']+($this->pricerange['min']['price']*$taxrate))." &mdash; ".money($this->pricerange['max']['price'] + ($this->pricerange['max']['price']*$taxrate));
  599. }
  600. } else {
  601. $taxrate = shopp_taxrate($options['taxes'],$this->prices[0]->tax);
  602. return money($this->prices[0]->price + ($this->prices[0]->price*$taxrate));
  603. }
  604. break;
  605. case "weight":
  606. if(empty($this->prices)) $this->load_data(array('prices'));
  607. $unit = (isset($options['units']) && !value_is_true($options['units'])?
  608. false : $Shopp->Settings->get('weight_unit'));
  609. if(!$this->weightrange['min']) return false;
  610. $string = ($this->weightrange['min'] == $this->weightrange['max']) ?
  611. round($this->weightrange['min'],3) :
  612. round($this->weightrange['min'],3) . " - " . round($this->weightrange['max'],3);
  613. $string .= ($unit) ? " $unit" : "";
  614. return $string;
  615. break;
  616. case "onsale":
  617. if (empty($this->prices)) $this->load_data(array('prices'));
  618. if (empty($this->prices)) return false;
  619. return $this->onsale;
  620. // if (empty($this->prices)) $this->load_prices();
  621. $sale = false;
  622. if (count($this->prices) > 1) {
  623. foreach($this->prices as $pricetag)
  624. if (isset($pricetag->onsale) && $pricetag->onsale == "on") $sale = true;
  625. return $sale;
  626. } else return ($this->prices[0]->onsale == "on")?true:false;
  627. break;
  628. case "saleprice":
  629. if (empty($this->prices)) $this->load_data(array('prices'));
  630. if (!isset($options['taxes'])) $options['taxes'] = null;
  631. $pricetag = 'price';
  632. if ($this->onsale) $pricetag = 'saleprice';
  633. if (count($this->options) > 0) {
  634. $taxrate = shopp_taxrate($options['taxes']);
  635. if ($this->pricerange['min'][$pricetag] == $this->pricerange['max'][$pricetag])
  636. return money($this->pricerange['min'][$pricetag]+($this->pricerange['min'][$pricetag]*$taxrate)); // No price range
  637. else {
  638. if (!empty($options['starting'])) return $options['starting']." ".money($this->pricerange['min'][$pricetag]+($this->pricerange['min'][$pricetag]*$taxrate));
  639. return money($this->pricerange['min'][$pricetag]+($this->pricerange['min'][$pricetag]*$taxrate))." &mdash; ".money($this->pricerange['max'][$pricetag]+($this->pricerange['max'][$pricetag]*$taxrate));
  640. }
  641. } else {
  642. $taxrate = shopp_taxrate($options['taxes'],$this->prices[0]->tax);
  643. return money($this->prices[0]->promoprice+($this->prices[0]->promoprice*$taxrate));
  644. }
  645. break;
  646. case "has-savings": return ($this->onsale && $this->pricerange['min']['saved'] > 0)?true:false; break;
  647. case "savings":
  648. if (empty($this->prices)) $this->load_data(array('prices'));
  649. if (!isset($options['taxes'])) $options['taxes'] = null;
  650. $taxrate = shopp_taxrate($options['taxes']);
  651. if (!isset($options['show'])) $options['show'] = '';
  652. if ($options['show'] == "%" || $options['show'] == "percent") {
  653. if ($this->options > 1) {
  654. if (round($this->pricerange['min']['savings']) == round($this->pricerange['max']['savings']))
  655. return percentage($this->pricerange['min']['savings']); // No price range
  656. else return percentage($this->pricerange['min']['savings'])." &mdash; ".percentage($this->pricerange['max']['savings']);
  657. } else return percentage($this->pricerange['max']['savings']);
  658. } else {
  659. if ($this->options > 1) {
  660. if ($this->pricerange['min']['saved'] == $this->pricerange['max']['saved'])
  661. return money($this->pricerange['min']['saved']+($this->pricerange['min']['saved']*$taxrate)); // No price range
  662. else return money($this->pricerange['min']['saved']+($this->pricerange['min']['saved']*$taxrate))." &mdash; ".money($this->pricerange['max']['saved']+($this->pricerange['max']['saved']*$taxrate));
  663. } else return money($this->pricerange['max']['saved']+($this->pricerange['max']['saved']*$taxrate));
  664. }
  665. break;
  666. case "freeshipping":
  667. if (empty($this->prices)) $this->load_data(array('prices'));
  668. // if (empty($this->prices)) $this->load_prices();
  669. return $this->freeshipping;
  670. case "thumbnail":
  671. if (empty($this->imagesets)) $this->load_data(array('images'));
  672. if (empty($options['class'])) $options['class'] = '';
  673. else $options['class'] = ' class="'.$options['class'].'"';
  674. if (isset($this->thumbnail)) {
  675. $img = $this->thumbnail;
  676. $title = !empty($img->properties['title'])?' title="'.attribute_escape($img->properties['title']).'"':'';
  677. $width = (isset($options['width']))?$options['width']:$img->properties['width'];
  678. $height = (isset($options['height']))?$options['height']:$img->properties['height'];
  679. if (isset($options['width']) && !isset($options['height'])) {
  680. $scale = $width/$img->properties['width'];
  681. $height = round($img->properties['height']*$scale);
  682. }
  683. if (isset($options['height']) && !isset($options['width'])) {
  684. $scale = $height/$img->properties['height'];
  685. $width = round($img->properties['width']*$scale);
  686. }
  687. if (!empty($options['title'])) $title = ' title="'.attribute_escape($options['title']).'"';
  688. $alt = attribute_escape(!empty($img->properties['alt'])?$img->properties['alt']:$this->name);
  689. return '<img src="'.$img->uri.'"'.$title.' alt="'.$alt.'" width="'.$width.'" height="'.$height.'" '.$options['class'].' />'; break;
  690. }
  691. break;
  692. case "hasimages":
  693. case "has-images":
  694. if (empty($options['type'])) $options['type'] = "thumbnail";
  695. if (empty($this->images)) $this->load_data(array('images'));
  696. if (!empty($this->imagesets[$options['type']])) {
  697. $this->imageset = &$this->imagesets[$options['type']];
  698. return true;
  699. } else return false;
  700. break;
  701. case "images":
  702. if (!$this->imageset) return false;
  703. if (!$this->imageloop) {
  704. reset($this->imageset);
  705. $this->imageloop = true;
  706. } else next($this->imageset);
  707. if (current($this->imageset)) return true;
  708. else {
  709. $this->imageloop = false;
  710. $this->imageset = false;
  711. return false;
  712. }
  713. break;
  714. case "image":
  715. $img = current($this->imageset);
  716. if (isset($options['property'])) {
  717. switch (strtolower($options['property'])) {
  718. case "url": return $img->uri;
  719. case "width": return $img->properties['width'];
  720. case "height": return $img->properties['height'];
  721. case "title": return attribute_escape($img->properties['title']);
  722. case "alt": return attribute_escape($img->properties['alt']);
  723. default: return $img->id;
  724. }
  725. }
  726. if (!isset($options['class'])) $options['class'] = false;
  727. if (!empty($options['class'])) $options['class'] = ' class="'.$options['class'].'"';
  728. $title = !empty($img->properties['title'])?' title="'.attribute_escape($img->properties['title']).'"':'';
  729. $width = (isset($options['width']))?$options['width']:$img->properties['width'];
  730. $height = (isset($options['height']))?$options['height']:$img->properties['height'];
  731. if (isset($options['width']) && !isset($options['height'])) {
  732. $scale = $width/$img->properties['width'];
  733. $height = round($img->properties['height']*$scale);
  734. }
  735. if (isset($options['height']) && !isset($options['width'])) {
  736. $scale = $height/$img->properties['height'];
  737. $width = round($img->properties['width']*$scale);
  738. }
  739. if (!empty($options['title'])) $title = ' title="'.attribute_escape($options['title']).'"';
  740. $alt = attribute_escape(!empty($img->properties['alt'])?$img->properties['alt']:$this->name);
  741. $string = "";
  742. if (!isset($options['zoomfx'])) $options['zoomfx'] = "shopp-thickbox";
  743. if (!empty($options['zoom'])) $string .= '<a href="'.$Shopp->imguri.$img->src.'/'.str_replace('small_','',$img->name).'" class="'.$options['zoomfx'].'" rel="product-gallery">';
  744. $string .= '<img src="'.$img->uri.'"'.$title.' alt="'.$alt.'" width="'.$width.'" height="'.$height.'" '.$options['class'].' />';
  745. if (!empty($options['zoom'])) $string .= "</a>";
  746. return $string;
  747. break;
  748. case "gallery":
  749. if (empty($this->images)) $this->load_data(array('images'));
  750. if (!isset($options['zoomfx'])) $options['zoomfx'] = "shopp-thickbox";
  751. if (!isset($options['preview'])) $options['preview'] = "click";
  752. $previews = '<ul class="previews">';
  753. $firstPreview = true;
  754. if (!empty($this->imagesets['small'])) {
  755. foreach ($this->imagesets['small'] as $img) {
  756. if ($firstPreview) {
  757. $previews .= '<li id="preview-fill"'.(($firstPreview)?' class="fill"':'').'>';
  758. $previews .= '<img src="'.$Shopp->uri.'/core/ui/icons/clear.png'.'" alt="'.$img->datatype.'" width="'.$img->properties['width'].'" height="'.$img->properties['height'].'" />';
  759. $previews .= '</li>';
  760. }
  761. $title = !empty($img->properties['title'])?' title="'.attribute_escape($img->properties['title']).'"':'';
  762. $alt = attribute_escape(!empty($img->properties['alt'])?$img->properties['alt']:$img->name);
  763. $rel = (isset($options['rel']) && $options['rel'])?' rel="product_'.$this->id.'_gallery"':'';
  764. $previews .= '<li id="preview-'.$img->src.'"'.(($firstPreview)?' class="active"':'').'>';
  765. $previews .= '<a href="'.$Shopp->imguri.$img->src.'/'.str_replace('small_','',$img->name).'" class="product_'.$this->id.'_gallery '.$options['zoomfx'].'"'.$rel.'>';
  766. $previews .= '<img src="'.$Shopp->imguri.$img->id.'"'.$title.' alt="'.$alt.'" width="'.$img->properties['width'].'" height="'.$img->properties['height'].'" />';
  767. $previews .= '</a>';
  768. $previews .= '</li>';
  769. $firstPreview = false;
  770. }
  771. }
  772. $previews .= '</ul>';
  773. $thumbs = "";
  774. if (isset($this->imagesets['thumbnail']) && count($this->imagesets['thumbnail']) > 1) {
  775. $thumbsize = 32;
  776. if (isset($options['thumbsize'])) $thumbsize = $options['thumbsize'];
  777. $thumbwidth = $thumbsize;
  778. $thumbheight = $thumbsize;
  779. if (isset($options['thumbwidth'])) $thumbwidth = $options['thumbwidth'];
  780. if (isset($options['thumbheight'])) $thumbheight = $options['thumbheight'];
  781. $firstThumb = true;
  782. $thumbs = '<ul class="thumbnails">';
  783. foreach ($this->imagesets['thumbnail'] as $img) {
  784. if (isset($options['thumbwidth']) && !isset($options['thumbheight'])) {
  785. $scale = $thumbwidth/$img->properties['width'];
  786. $thumbheight = round($img->properties['height']*$scale);
  787. }
  788. if (isset($options['thumbheight']) && !isset($options['thumbwidth'])) {
  789. $scale = $thumbheight/$img->properties['height'];
  790. $thumbwidth = round($img->properties['width']*$scale);
  791. }
  792. $title = !empty($img->properties['title'])?' title="'.attribute_escape($img->properties['title']).'"':'';
  793. $alt = attribute_escape(!empty($img->properties['alt'])?$img->properties['alt']:$img->name);
  794. $thumbs .= '<li id="thumbnail-'.$img->src.'" class="preview-'.$img->src.(($firstThumb)?' first':' test').'">';
  795. $thumbs .= '<img src="'.$Shopp->imguri.$img->id.'"'.$title.' alt="'.$alt.'" width="'.$thumbwidth.'" height="'.$thumbheight.'" />';
  796. $thumbs .= '</li>';
  797. $firstThumb = false;
  798. }
  799. $thumbs .= '</ul>';
  800. }
  801. $result = '<div id="gallery-'.$this->id.'" class="gallery">'.$previews.$thumbs.'</div>';
  802. $result .= '<script type="text/javascript"><!--
  803. jQuery(document).ready( function() { shopp_gallery("#gallery-'.$this->id.'","'.$options['preview'].'"); });
  804. // --></script>';
  805. return $result;
  806. break;
  807. case "has-categories":
  808. if (empty($this->categories)) $this->load_data(array('categories'));
  809. if (count($this->categories) > 0) return true; else return false; break;
  810. case "categories":
  811. if (!$this->categoryloop) {
  812. reset($this->categories);
  813. $this->categoryloop = true;
  814. } else next($this->categories);
  815. if (current($this->categories)) return true;
  816. else {
  817. $this->categoryloop = false;
  818. return false;
  819. }
  820. break;
  821. case "in-category":
  822. if (empty($this->categories)) $this->load_data(array('categories'));
  823. if (isset($options['id'])) $field = "id";
  824. if (isset($options['name'])) $field = "name";
  825. if (isset($options['slug'])) $field = "slug";
  826. foreach ($this->categories as $category)
  827. if ($category->{$field} == $options[$field]) return true;
  828. return false;
  829. case "category":
  830. $category = current($this->categories);
  831. if (isset($options['show'])) {
  832. if ($options['show'] == "id") return $category->id;
  833. if ($options['show'] == "slug") return $category->slug;
  834. }
  835. return $category->name;
  836. break;
  837. case "has-tags":
  838. if (empty($this->tags)) $this->load_data(array('tags'));
  839. if (count($this->tags) > 0) return true; else return false; break;
  840. case "tags":
  841. if (!$this->tagloop) {
  842. reset($this->tags);
  843. $this->tagloop = true;
  844. } else next($this->tags);
  845. if (current($this->tags)) return true;
  846. else {
  847. $this->tagloop = false;
  848. return false;
  849. }
  850. break;
  851. case "tagged":
  852. if (empty($this->tags)) $this->load_data(array('tags'));
  853. if (isset($options['id'])) $field = "id";
  854. if (isset($options['name'])) $field = "name";
  855. foreach ($this->tags as $tag)
  856. if ($tag->{$field} == $options[$field]) return true;
  857. return false;
  858. case "tag":
  859. $tag = current($this->tags);
  860. if (isset($options['show'])) {
  861. if ($options['show'] == "id") return $tag->id;
  862. }
  863. return $tag->name;
  864. break;
  865. case "has-specs":
  866. if (empty($this->specs)) $this->load_data(array('specs'));
  867. if (count($this->specs) > 0) {
  868. $this->merge_specs();
  869. return true;
  870. } else return false; break;
  871. case "specs":
  872. if (!$this->specloop) {
  873. reset($this->specs);
  874. $this->specloop = true;
  875. } else next($this->specs);
  876. if (current($this->specs)) return true;
  877. else {
  878. $this->specloop = false;
  879. return false;
  880. }
  881. break;
  882. case "spec":
  883. $string = "";
  884. $separator = ": ";
  885. $delimiter = ", ";
  886. if (isset($options['separator'])) $separator = $options['separator'];
  887. if (isset($options['delimiter'])) $separator = $options['delimiter'];
  888. $spec = current($this->specs);
  889. if (is_array($spec->content)) $spec->content = join($delimiter,$spec->content);
  890. if (isset($options['name'])
  891. && !empty($options['name'])
  892. && isset($this->specskey[$options['name']])) {
  893. $spec = $this->specskey[$options['name']];
  894. if (is_array($spec)) {
  895. if (isset($options['index'])) {
  896. foreach ($spec as $index => $entry)
  897. if ($index+1 == $options['index'])
  898. $content = $entry->content;
  899. } else {
  900. foreach ($spec as $entry) $contents[] = $entry->content;
  901. $content = join($delimiter,$contents);
  902. }
  903. } else $content = $spec->content;
  904. $string = apply_filters('shopp_product_spec',$content);
  905. return $string;
  906. }
  907. if (isset($options['name']) && isset($options['content']))
  908. $string = "{$spec->name}{$separator}".apply_filters('shopp_product_spec',$spec->content);
  909. elseif (isset($options['name'])) $string = $spec->name;
  910. elseif (isset($options['content'])) $string = apply_filters('shopp_product_spec',$spec->content);
  911. else $string = "{$spec->name}{$separator}".apply_filters('shopp_product_spec',$spec->content);
  912. return $string;
  913. break;
  914. case "has-variations":
  915. return ($this->variations == "on" && !empty($this->options)); break;
  916. case "variations":
  917. $string = "";
  918. if (!isset($options['mode'])) {
  919. if (!$this->priceloop) {
  920. reset($this->prices);
  921. $this->priceloop = true;
  922. } else next($this->prices);
  923. $thisprice = current($this->prices);
  924. if ($thisprice && $thisprice->type == "N/A")
  925. next($this->prices);
  926. if (current($this->prices)) return true;
  927. else {
  928. $this->priceloop = false;
  929. return false;
  930. }
  931. return true;
  932. }
  933. if ($this->outofstock) return false; // Completely out of stock, hide menus
  934. if (!isset($options['taxes'])) $options['taxes'] = null;
  935. $defaults = array(
  936. 'defaults' => '',
  937. 'disabled' => 'show',
  938. 'before_menu' => '',
  939. 'after_menu' => ''
  940. );
  941. $options = array_merge($defaults,$options);
  942. if (!isset($options['label'])) $options['label'] = "on";
  943. if (!isset($options['required'])) $options['required'] = __('You must select the options for this item before you can add it to your shopping cart.','Shopp');
  944. if ($options['mode'] == "single") {
  945. if (!empty($options['before_menu'])) $string .= $options['before_menu']."\n";
  946. if (value_is_true($options['label'])) $string .= '<label for="product-options'.$this->id.'">Options: </label> '."\n";
  947. $string .= '<select name="products['.$this->id.'][price]" id="product-options'.$this->id.'">';
  948. if (!empty($options['defaults'])) $string .= '<option value="">'.$options['defaults'].'</option>'."\n";
  949. foreach ($this->prices as $pricetag) {
  950. if ($pricetag->context != "variation") continue;
  951. $taxrate = shopp_taxrate($options['taxes'],$pricetag->tax);
  952. $currently = ($pricetag->sale == "on")?$pricetag->promoprice:$pricetag->price;
  953. $disabled = ($pricetag->inventory == "on" && $pricetag->stock == 0)?' disabled="disabled"':'';
  954. $price = ' ('.money($currently).')';
  955. if ($pricetag->type != "N/A")
  956. $string .= '<option value="'.$pricetag->id.'"'.$disabled.'>'.$pricetag->label.$price.'</option>'."\n";
  957. }
  958. $string .= '</select>';
  959. if (!empty($options['after_menu'])) $string .= $options['after_menu']."\n";
  960. } else {
  961. $taxrate = shopp_taxrate($options['taxes'],true);
  962. ob_start();
  963. ?>
  964. <script type="text/javascript">
  965. <!--
  966. (function($) {
  967. $(document).ready(function () {
  968. productOptions[<?php echo $this->id; ?>] = new Array();
  969. productOptions[<?php echo $this->id; ?>]['pricing'] = <?php echo json_encode($this->pricekey); ?>;
  970. options_default = <?php echo (!empty($options['defaults']))?'true':'false'; ?>;
  971. options_required = "<?php echo $options['required']; ?>";
  972. productOptions[<?php echo $this->id; ?>]['menu'] = new ProductOptionsMenus('select<?php if (isset($Shopp->Category->slug)) echo ".category-".$Shopp->Category->slug; ?>.product<?php echo $this->id; ?>',<?php echo ($options['disabled'] == "hide")?"true":"false"; ?>,productOptions[<?php echo $this->id; ?>]['pricing'],<?php echo empty($taxrate)?'0':$taxrate; ?>);
  973. });
  974. })(jQuery)
  975. //-->
  976. </script>
  977. <?php
  978. $script = ob_get_contents();
  979. ob_end_clean();
  980. $options['after_menu'] = $script.$options['after_menu'];
  981. if (isset($this->options['variations'])) {
  982. foreach ($this->options['variations'] as $id => $menu) {
  983. if (!empty($options['before_menu'])) $string .= $options['before_menu']."\n";
  984. if (value_is_true($options['label'])) $string .= '<label for="options-'.$id.'">'.$menu['menu'].'</label> '."\n";
  985. $string .= '<select name="products['.$this->id.'][options][]" class="product'.$this->id.' options">';
  986. if (!empty($options['defaults'])) $string .= '<option value="">'.$options['defaults'].'</option>'."\n";
  987. foreach ($menu['label'] as $key => $option)
  988. $string .= '<option value="'.$menu['id'][$key].'">'.$option.'</option>'."\n";
  989. $string .= '</select>';
  990. if (!empty($options['after_menu'])) $string .= $options['after_menu']."\n";
  991. }
  992. } else {
  993. foreach ($this->options as $id => $menu) {
  994. if (!empty($options['before_menu'])) $string .= $options['before_menu']."\n";
  995. if (value_is_true($options['label'])) $string .= '<label for="options-'.$menu['id'].'">'.$menu['name'].'</label> '."\n";
  996. $category_class = isset($Shopp->Category->slug)?'category-'.$Shopp->Category->slug:'';
  997. $string .= '<select name="products['.$this->id.'][options][]" class="'.$category_class.' product'.$this->id.' options" id="options-'.$menu['id'].'">';
  998. if (!empty($options['defaults'])) $string .= '<option value="">'.$options['defaults'].'</option>'."\n";
  999. foreach ($menu['options'] as $key => $option)
  1000. $string .= '<option value="'.$option['id'].'">'.$option['name'].'</option>'."\n";
  1001. $string .= '</select>';
  1002. if (!empty($options['after_menu'])) $string .= $options['after_menu']."\n";
  1003. }
  1004. }
  1005. }
  1006. return $string;
  1007. break;
  1008. case "variation":
  1009. $variation = current($this->prices);
  1010. if (!isset($options['taxes'])) $options['taxes'] = null;
  1011. $taxrate = shopp_taxrate($options['taxes'],$variation->tax);
  1012. $weightunit = (isset($options['units']) && !value_is_true($options['units']) ) ? false : $Shopp->Settings->get('weight_unit');
  1013. $string = '';
  1014. if (array_key_exists('id',$options)) $string .= $variation->id;
  1015. if (array_key_exists('label',$options)) $string .= $variation->label;
  1016. if (array_key_exists('type',$options)) $string .= $variation->type;
  1017. if (array_key_exists('sku',$options)) $string .= $variation->sku;
  1018. if (array_key_exists('price',$options)) $string .= money($variation->price+($variation->price*$taxrate));
  1019. if (array_key_exists('saleprice',$options)) $string .= money($variation->saleprice+($variation->saleprice*$taxrate));
  1020. if (array_key_exists('stock',$options)) $string .= $variation->stock;
  1021. if (array_key_exists('weight',$options)) $string .= round($variation->weight, 3) . ($weightunit ? " $weightunit" : false);
  1022. if (array_key_exists('shipfee',$options)) $string .= money(floatvalue($variation->shipfee));
  1023. if (array_key_exists('sale',$options)) return ($variation->sale == "on");
  1024. if (array_key_exists('shipping',$options)) return ($variation->shipping == "on");
  1025. if (array_key_exists('tax',$options)) return ($variation->tax == "on");
  1026. if (array_key_exists('inventory',$options)) return ($variation->inventory == "on");
  1027. return $string;
  1028. break;
  1029. case "has-addons":
  1030. if (isset($this->options['addons'])) return true; else return false; break;
  1031. break;
  1032. case "donation":
  1033. case "amount":
  1034. case "quantity":
  1035. if ($this->outofstock) return false;
  1036. if (!isset($options['value'])) $options['value'] = 1;
  1037. if (!isset($options['input'])) $options['input'] = "text";
  1038. if (!isset($options['labelpos'])) $options['labelpos'] = "before";
  1039. if (!isset($options['label'])) $label ="";
  1040. else $label = '<label for="quantity'.$this->id.'">'.$options['label'].'</label>';
  1041. $result = "";
  1042. if ($options['labelpos'] == "before") $result .= "$label ";
  1043. if (!$this->priceloop) reset($this->prices);
  1044. $variation = current($this->prices);
  1045. if (isset($options['input']) && $options['input'] == "menu") {
  1046. if (!isset($options['options']))
  1047. $values = "1-15,20,25,30,40,50,75,100";
  1048. else $values = $options['options'];
  1049. if ($this->inventory && $this->pricerange['max']['stock'] == 0) return "";
  1050. if (strpos($values,",") !== false) $values = explode(",",$values);
  1051. else $values = array($values);
  1052. $qtys = array();
  1053. foreach ($values as $value) {
  1054. if (strpos($value,"-") !== false) {
  1055. $value = explode("-",$value);
  1056. if ($value[0] >= $value[1]) $qtys[] = $value[0];
  1057. else for ($i = $value[0]; $i < $value[1]+1; $i++) $qtys[] = $i;
  1058. } else $qtys[] = $value;
  1059. }
  1060. $result .= '<select name="products['.$this->id.'][quantity]" id="quantity-'.$this->id.'">';
  1061. foreach ($qtys as $qty) {
  1062. $amount = $qty;
  1063. $selected = (isset($this->quantity))?$this->quantity:1;
  1064. if ($variation->type == "Donation" && $variation->donation['var'] == "on") {
  1065. if ($variation->donation['min'] == "on" && $amount < $variation->price) continue;
  1066. $amount = money($amount);
  1067. $selected = $variation->price;
  1068. } else {
  1069. if ($this->inventory && $amount > $this->pricerange['max']['stock']) continue;
  1070. }
  1071. $result .= '<option'.(($qty == $selected)?' selected="selected"':'').' value="'.$qty.'">'.$amount.'</option>';
  1072. }
  1073. $result .= '</select>';
  1074. if ($options['labelpos'] == "after") $result .= " $label";
  1075. return $result;
  1076. }
  1077. if (valid_input($options['input'])) {
  1078. if (!isset($options['size'])) $options['size'] = 3;
  1079. if ($variation->type == "Donation" && $variation->donation['var'] == "on") {
  1080. if ($variation->donation['min']) $options['value'] = $variation->price;
  1081. $options['class'] .= " currency";
  1082. }
  1083. $result = '<input type="'.$options['input'].'" name="products['.$this->id.'][quantity]" id="quantity-'.$this->id.'"'.inputattrs($options).' />';
  1084. }
  1085. if ($options['labelpos'] == "after") $result .= " $label";
  1086. return $result;
  1087. brea

Large files files are truncated, but you can click here to view the full file