* @license PHP License * @package WB * @subpackage content */ /** * Load classes */ WBClass::load('WBContent' , 'WBHtml_JS' ); /** * Content component: Shop Cart * * @version 0.2.0 * @package WB * @subpackage content */ class WBContent_Shop_Cart extends WBContent { /** * my parameter list * @var array */ protected $config = array( 'mode' => 'display', 'action' => 'list', 'status' => '', 'cart' => '__active', 'tmpl' => '', 'csvdelimiter' => ',', 'csvenclosure' => '"' ); /** * cart * @var WBShop_Cart */ private $cart; /** * article * @var WBShop_Article */ private $article; /** * shopping-cart is not cachable * @var */ protected $cachable = false; /** * run * * Dispatch action config parameter and call method that actually fills * HTML templates. * * @return array parameter list */ public function run() { $this->article = WBClass::create('WBShop_Article'); $this->cart = WBClass::create('WBShop_Cart'); $this->cart->setUser($this->user->getId()); $this->config['action'] = strtolower($this->config['action']); switch ($this->config['action']) { case 'update': $this->updateItem(); break; case 'create': $this->create(); break; case 'copy': $this->copy(); break; case 'copypublic': $this->copypublic(); break; case 'merge': $this->merge(); break; case 'activate': $this->activate(); break; case 'delete': $this->delete(); break; case 'import': $this->import(); break; case 'export': $this->export(); break; case 'edit': $this->edit(); break; case 'display': $this->display(); break; case 'displaypublic': $this->display(true); break; case 'list': default: $this->listCarts(); break; } return $this->config; } /** * Update items in cart * * Update quantity and comment */ private function updateItem() { $this->load(); $item = $this->req->get('item', ''); if (empty($item)) { $this->req->set('save'); $this->edit(); return; } $quantity = intval($this->req->get('quantity', '-1')); if (0 > $quantity) { $this->req->set('save'); $this->edit(); return; } $comment = $this->req->get('comment', ''); $this->tmpl->addGlobalVar('message', 'item_saved'); $this->cart->set($item, $quantity, $comment); // set attributes $data = $this->req->export(); $this->cart->setItemAttributes($item, $data); $this->cart->save(); $this->req->set('save'); $this->edit(); } /** * create a new cart */ private function create() { if (!$this->user->getId()) { $this->listCarts(); return; } $this->cart->save(); $this->listCarts(); } /** * create a new cart */ private function copy() { if (!$this->user->getId()) { $this->listCarts(); return; } $this->load(); $copy = WBClass::create('WBShop_Cart'); $copy->setUser($this->user->getId()); $copy->merge($this->cart); $copy->setTitle($copy->getTitle() . ' (copy)'); $copy->save(); $this->cart = $copy; $this->listCarts(); } /** * Copy public cart */ private function copypublic() { if (!$this->user->getId()) { $this->listCarts(); return; } $public = WBClass::create('WBShop_Cart'); /** @var $public WBShop_Cart */ $public->load($this->config['cart'], true); $copy = WBClass::create('WBShop_Cart'); /** @var $copy WBShop_Cart */ $copy->setUser($this->user->getId()); $copy->merge($public); $copy->save(); $this->config['cart'] = $copy->getId(); $this->listCarts(); } /** * Merge one cart with other * * Select other cart and merge with current (loaded). * In case no other cart is selected yet, display list of * selectable carts */ private function merge() { if (!$this->user->getId()) { $this->listCarts(); return; } $this->load(); $other = $this->req->get('other', ''); // try to load other cart if (!empty($other)) { $cart = WBClass::create('WBShop_Cart'); $cart->setUser($this->user->getId()); $cart->load($other); if ('__new' == $cart->getId() || $thiy->cart->getId() == $cart->getId()) { $other = ''; } } if (empty($other)) { $this->loadTemplates('merge'); $this->tmpl->addGlobalVars($this->cart->get(), 'ORG_'); $status = array('saved', 'active'); $list = $this->cart->getList($status, $this->cart->getId()); $this->tmpl->addRows('cart_entry', $list); return; } // actual merge $this->cart->merge($cart); $this->cart->save(); $this->listCarts(); } /** * Edit cart attributes * * Process form "edit" */ private function activate() { $this->load(); $this->cart->activate(); $this->listCarts(); } /** * Edit cart attributes * * Process form "edit" */ private function edit() { $this->parseFormTmpl = false; $this->load(); $this->tmpl->addGlobalVars($this->cart->get()); $this->processForm('edit', $this->cart->get()); if ($this->tmpl->exists('item_entry')) { $items = $this->getItems(); $this->tmpl->addRows('item_entry', $items); } $this->parseFormTmpl = true; } /** * Save cart entry * * Store record in database and add user id for new records. * Return false to avoid loading "editValid" template. Instead, * remove "save" parameter from request and redisplay the form * * @param patForms $form * @param array $values * @return bool always false */ public function onEditValid($form, $values) { if (!isset($values['public'])) { $values['public'] = 0; } if (isset($values['title'])) { $this->cart->setTitle($values['title']); } if (isset($values['note'])) { $this->cart->setNote($values['note']); } if (isset($values['public'])) { $this->cart->setPublic($values['public']); } $this->cart->setAttributes($values); $this->cart->save(); // redisplay form $this->req->set('save'); $this->processForm('edit', $this->cart->get()); $this->tmpl->addGlobalVar('message', 'cart_saved'); return false; } /** * Remova cart * */ private function delete() { if (!$this->user->getId()) { $this->listCarts(); return; } $this->load(); $this->cart->delete(); $this->listCarts(); } /** * List Carts * * List all carts owned by current user. */ private function listCarts() { $tmpl = $this->getSubTmplName('list'); $this->loadTemplates($tmpl); $status = array('saved', 'active'); if (!empty($this->config['status'])) { $status = $this->config['status']; } $list = $this->cart->getList($status); $this->tmpl->addRows('cart_entry', $list); } /** * Display cart's detailstus: active Public: * * Load cart by id (or active one) * * @param string $tmpl */ private function display() { $tmpl = $this->getSubTmplName('display'); $this->load(); $this->loadTemplates($tmpl); if ('gauge' == strtolower($this->config['mode'])) { WBHtml_JS::add('WB/Ajax/Shop'); WBHtml_JS::add('WB/Ajax/Shop/Cart'); WBHtml_JS::add(sprintf('WB.Ajax.Shop.Cart.useGauge("%s", "%s");', $this->part, $this->config['tmpl']), WBHtml_JS::AREA_FOOT); } if ($this->tmpl->exists('item_entry')) { $items = $this->getItems(); $this->tmpl->addRows('item_entry', $items); } $this->tmpl->addGlobalVars($this->cart->get()); } /** * Import CSV file as new cart * * Upload CSV file, either in "standard" format (same as export format) or as simple list of * articles and quantities. */ private function import() { $this->loadTemplates('import'); if (!isset($_FILES) || empty($_FILES)) { return; } $error = array(); $import = array(); foreach ($_FILES as $file) { $file['errormsg'] = ''; if ($file['error'] || empty($file['tmp_name']) || (1 > $file['size'])) { $file['errormsg'] = 'upload'; $error[] = $file; continue; } if ('text/csv' != $file['type']) { $file['errormsg'] = 'type'; $error[] = $file; continue; } $imp = $this->importUpload($file); if (empty($imp)) { $file['errormsg'] = 'format'; $error[] = $file; continue; } $import[] = $imp; } if (!empty($error)) { $this->tmpl->addGlobalVar('error_count', count($error)); $this->tmpl->addRows('error_entry', $error); } if (!empty($import)) { $this->tmpl->addGlobalVar('import_count', count($import)); $this->tmpl->addRows('import_entry', $import); } } /** * Export cart as CSV-table * * Load selected cart. Transform to CSV format and start download * * CAUTION. This method does not return. CSV-Download will be initiated */ private function export() { $this->load(); $file = WBClass::create('WBFile'); /** @var WBFile $file */ $file = $file->tempnam('cart'); $csv = fopen($file->realpath(), 'w'); $data = $this->cart->get(); fputcsv($csv, array('name', $data['title']), $this->config['csvdelimiter'], $this->config['csvenclosure']); fputcsv($csv, array('note', $data['note']), $this->config['csvdelimiter'], $this->config['csvenclosure']); fputcsv($csv, array(), $this->config['csvdelimiter'], $this->config['csvenclosure']); fputcsv($csv, array('article', 'quantity', 'unit', 'name', 'comment'), $this->config['csvdelimiter'], $this->config['csvenclosure']); $items = $this->getItems(); foreach ($items as $i) { $row = array( $i['said'], $i['quantity'], $i['unit'], $i['tradename'], $i['comment'] ); fputcsv($csv, $row, $this->config['csvdelimiter'], $this->config['csvenclosure']); } fclose($csv); // switch off compression - otherwise complete download files are loaded to RAM WBParam::set('wb/response/compress', 0); $res = WBClass::create('WBResponse'); /** @var $res WBResponse */ $res->add(file_get_contents($file->realpath())); $file->unlink(); $res->addHeader('Content-Type', 'text/csv'); $res->addHeader('Content-Disposition', 'filename="cart-' . $this->cart->getId() . '.csv"'); $res->send($this->req); exit(0); } /** * load cart object * */ private function load() { if ('__active' == $this->config['cart']) { $this->cart->loadActive(); return ; } $this->cart->load($this->config['cart']); } /** * Fetch and enrich items * * Get items from current cart and enrich them with article data * Auxilliary method. * * @param bool $enrich * @return array $items */ private function getItems($enrich = true) { $items = $this->cart->getItems(); if (!$enrich) { return $items; } $primary = $this->article->getIdentifer(); foreach ($items as &$i) { $this->article->load($i[$primary]); $i = array_merge($this->article->get(), $i); } return $items; } /** * Get template name in sub dir * * @param string $tmpl */ private function getSubTmplName($tmpl) { if (empty($this->config['tmpl'])) { return $tmpl; } return $tmpl .= '/' . $this->config['tmpl']; } /** * receive output * * Display parsed template * * @return string */ public function getString() { return $this->tmpl->getParsedTemplate(); } /** * location of form config * * Return sub directory where form element definitions are located * * @return string folder */ protected function getFormConfigDir() { return 'shop/cart/form'; } /** * Import uploaded CSV file * * Upload poarameter represents on uploaded file, see: $_FILES * Detect format of CSV file and import it, if possible. * * @param array $upload */ private function importUpload($upload) { $csv = fopen($upload['tmp_name'], 'r'); $fmt = $this->detectCSVFormat($csv); if (!$fmt['ok']) { fclose($csv); unlink($upload['tmp_name']); return null; } $this->importUploadCSV($csv, $fmt, $upload); fclose($csv); unlink($upload['tmp_name']); return $upload; } /** * Try to detect CSV file's format * * Detect delimiter, enclosure of CSV file. * Furthermore detects whether the contains standard format (same as export format) * * @param stream $csv * @return array $fmt */ private function detectCSVFormat($csv) { $delimiter = array(';', ',', "\t"); $enclosure = array("'", '"'); $fmt = array( 'ok' => false, 'std' => false, 'del' => '', 'enc' => '' ); foreach ($delimiter as $d) { foreach ($enclosure as $e) { fseek($csv, 0); $row1 = fgetcsv($csv, 0, $d, $e); $row2 = array(); if (feof($csv)) { fseek($csv, 0); } $row2 = fgetcsv($csv, 0, $d, $e); // require two columns if (2 > min(count($row1), count($row2))) { continue; } if ('name' == strtolower($row1[0]) && 'note' == strtolower($row2[0])) { $fmt['std'] = true; } $oe = '"'; if ('"' == $e) { $oe = "'"; } // does enclsure work as expected? foreach ($row1 as $c) { // onclosure does not seem to match, try next if ($c != trim($c, $oe)) { continue 2; } } $fmt['ok'] = true; $fmt['enc'] = $e; $fmt['del'] = $d; break 2; } } fseek($csv, 0); return $fmt; } /** * Import CSV file * * Create new cart, set title and note (for standard CSV-files) and add all the items * * @param stream $csv * @param array $fmt * @param array $upload */ private function importUploadCSV($csv, $fmt, &$upload) { // new cart $cart = WBClass::create('WBShop_Cart'); $cart->setUser($this->user->getId()); // standard format? if ($fmt['std']) { $row = fgetcsv($csv, 0, $fmt['del'], $fmt['enc']); array_map('trim', $row); $cart->setTitle($row[1]); $upload['title'] = $row[1]; $row = fgetcsv($csv, 0, $fmt['del'], $fmt['enc']); array_map('trim', $row); $cart->setNote($row[1]); } // default map: article: column 0, quantity: column 1 $mapFound = false; $map = array( 'article' => 0, 'quantity' => 1 ); $upload['item_count'] = 0; // try to find better map using headline while (!feof($csv)) { $row = fgetcsv($csv, 0, $fmt['del'], $fmt['enc']); if (empty($row)) { continue; } array_map('trim', $row); $row = array_map('strtolower', $row); if (!in_array('article', $row)) { continue; } $map = array_flip($row); $mapFound = true; break; } if (!$mapFound) { fseek($csv, 0); } $upload['item_count'] = $this->importItems2Cart($csv, $fmt, $map, $cart); $cart->save(); $upload['id'] = $cart->getId(); } /** * Import items from CSV file * * Use column map to load article ids from CSV and add articles with quantity and comment * * @param stream $csv * @param array $fmt * @param array $map * @param array $upload */ private function importItems2Cart($csv, $fmt, $map, $cart) { $count = 0; // add items to cart $art = WBClass::create('WBShop_Article'); while (!feof($csv)) { $row = fgetcsv($csv, 0, $fmt['del'], $fmt['enc']); if (empty($row)) { continue; } array_map('trim', $row); $a = $row[$map['article']]; $art->load($a); // invalid article id if ($art->getId() != $a) { continue; } $q = 1; if (isset($map['quantity'])) { $q = intval($row[$map['quantity']]); } $c = ''; if (isset($map['comment'])) { $c = $row[$map['comment']]; } ++$count; $cart->add($a, $q, $c); } return $count; } } ?>