Index: branches/5.0.x/units/products/products_event_handler.php =================================================================== --- branches/5.0.x/units/products/products_event_handler.php (revision 13285) +++ branches/5.0.x/units/products/products_event_handler.php (revision 13286) @@ -1,1292 +1,1316 @@ <?php /** * @version $Id$ * @package In-Commerce * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license Commercial License * This software is protected by copyright law and international treaties. * Unauthorized reproduction or unlicensed usage of the code of this program, * or any portion of it may result in severe civil and criminal penalties, * and will be prosecuted to the maximum extent possible under the law * See http://www.in-portal.org/commercial-license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class ProductsEventHandler extends kCatDBEventHandler { /** * Allows to override standart permission mapping * */ function mapPermissions() { parent::mapPermissions(); $permissions = Array( // front 'OnCancelAction' => Array('self' => true), 'OnRateProduct' => Array('self' => true), 'OnClearRecent' => Array('self' => true), 'OnRecommendProduct' => Array('self' => true), // admin 'OnQtyAdd' => Array('self' => 'add|edit'), 'OnQtyRemove' => Array('self' => 'add|edit'), 'OnQtyOrder' => Array('self' => 'add|edit'), 'OnQtyReceiveOrder' => Array('self' => 'add|edit'), 'OnQtyCancelOrder' => Array('self' => 'add|edit'), ); $this->permMapping = array_merge($this->permMapping, $permissions); } function mapEvents() { parent::mapEvents(); // ensure auto-adding of approve/decine and so on events $product_events = Array( 'OnQtyAdd'=>'InventoryAction', 'OnQtyRemove'=>'InventoryAction', 'OnQtyOrder'=>'InventoryAction', 'OnQtyReceiveOrder'=>'InventoryAction', 'OnQtyCancelOrder'=>'InventoryAction',); $this->eventMethods = array_merge($this->eventMethods, $product_events); } /** * Sets default processing data for subscriptions * * @param kEvent $event */ function OnBeforeItemCreate(&$event) { $object =& $event->getObject(); $product_approve_events = Array(2 => 'p:OnSubscriptionApprove', 4 => 'p:OnDownloadableApprove', 5 => 'p:OnPackageApprove'); $product_type = $object->GetDBField('Type'); $type_found = in_array($product_type, array_keys($product_approve_events) ); if($type_found && !$object->GetDBField('ProcessingData') ) { $processing_data = Array('ApproveEvent' => $product_approve_events[$product_type] ); $object->SetDBField( 'ProcessingData', serialize($processing_data) ); } } /** * Process product count manipulations * * @param kEvent $event * @access private */ function InventoryAction(&$event) { $object =& $event->getObject(); $object->SetFieldsFromHash( $this->getSubmittedFields($event) ); if ($object->GetDBField('InventoryStatus') == 2) { // inventory by options (use first selected combination in grid) $combination_id = array_shift( array_keys( $this->Application->GetVar('poc_grid') ) ); } else { // inventory by product $combination_id = 0; } // save id of selected option combination & preselect it in grid $this->Application->SetVar('combination_id', $combination_id); $this->ScheduleInventoryAction($event->Name, $object->GetId(), $object->GetDBField('Qty'), $combination_id); $object->Validate(); if (!isset($object->FieldErrors['Qty']['pseudo'])){ $this->modifyInventory($event->Name, $object, $object->GetDBField('Qty'), $combination_id); } $object->SetDBField('Qty', null); $event->redirect = false; } /** * Perform inventory action on supplied object * * @param string $action event name which is actually called by user * @param ProductsItem $product * @param int $qty * @param int $combination_id */ function modifyInventory($action, &$product, $qty, $combination_id) { if ($product->GetDBField('InventoryStatus') == 2) { // save inventory changes to option combination instead of product $object =& $this->Application->recallObject('poc.-item', null, Array('skip_autoload' => true)); $object->Load($combination_id); } elseif ($combination_id > 0) { // combination id present, but not inventory by combinations => skip return false; } elseif ($product->GetDBField('InventoryStatus') == 1) { // save inventory changes to product $object =& $product; } else { // product has inventory actions, but don't use inventory => skip return false; } if (!$object->isLoaded()) { // product/combination in action doesn't exist in database by now return false; } switch ($action) { case 'OnQtyAdd': $object->SetDBField('QtyInStock', $object->GetDBField('QtyInStock') + $qty); break; case 'OnQtyRemove': if ($object->GetDBField('QtyInStock') < $qty) { $qty = $object->GetDBField('QtyInStock'); } $object->SetDBField('QtyInStock', $object->GetDBField('QtyInStock') - $qty); break; case 'OnQtyOrder': $object->SetDBField('QtyOnOrder', $object->GetDBField('QtyOnOrder') + $qty); break; case 'OnQtyReceiveOrder': $object->SetDBField('QtyOnOrder', $object->GetDBField('QtyOnOrder') - $qty); $object->SetDBField('QtyInStock', $object->GetDBField('QtyInStock') + $qty); break; case 'OnQtyCancelOrder': $object->SetDBField('QtyOnOrder', $object->GetDBField('QtyOnOrder') - $qty); break; } return $object->Update(); } function ScheduleInventoryAction($action, $prod_id, $qty, $combination_id = 0) { $inv_actions = $this->Application->RecallVar('inventory_actions'); if (!$inv_actions) { $inv_actions = Array(); } else { $inv_actions = unserialize($inv_actions); } array_push($inv_actions, Array('action' => $action, 'product_id' => $prod_id, 'combination_id' => $combination_id, 'qty' => $qty)); $this->Application->StoreVar('inventory_actions', serialize($inv_actions)); } function RealInventoryAction($action, $prod_id, $qty, $combination_id) { $product =& $this->Application->recallObject('p.liveitem', null, Array('skip_autoload' => true)); $product->SwitchToLive(); $product->Load($prod_id); $this->modifyInventory($action, $product, $qty, $combination_id); } function RunScheduledInventoryActions(&$event) { $inv_actions = $this->Application->GetVar('inventory_actions'); if (!$inv_actions) { return; } $inv_actions = unserialize($inv_actions); $products = array(); foreach($inv_actions as $an_action) { $this->RealInventoryAction($an_action['action'], $an_action['product_id'], $an_action['qty'], $an_action['combination_id']); array_push($products, $an_action['product_id'].'_'.$an_action['combination_id']); } $products = array_unique($products); if ($products) { $product_obj =& $this->Application->recallObject('p.liveitem', null, Array('skip_autoload' => true)); $product_obj->SwitchToLive(); foreach ($products as $product_key) { list($prod_id, $combination_id) = explode('_', $product_key); $product_obj->Load($prod_id); $this->FullfillBackOrders($product_obj, $combination_id); } } } /** * In case if products arrived into inventory and they are required by old (non processed) orders, then use them (products) in that orders * * @param ProductsItem $product * @param int $combination_id */ function FullfillBackOrders(&$product, $combination_id) { if ( !$this->Application->ConfigValue('Comm_Process_Backorders_Auto') ) return; if ($combination_id && ($product->GetDBField('InventoryStatus') == 2)) { // if combination id present and inventory by combinations $poc_idfield = $this->Application->getUnitOption('poc', 'IDField'); $poc_tablename = $this->Application->getUnitOption('poc', 'TableName'); $sql = 'SELECT QtyInStock FROM '.$poc_tablename.' WHERE '.$poc_idfield.' = '.$combination_id; $stock_qty = $this->Conn->GetOne($sql); } else { // inventory by product $stock_qty = $product->GetDBField('QtyInStock'); } $qty = (int) $stock_qty - $product->GetDBField('QtyInStockMin'); $prod_id = $product->GetID(); if ($prod_id <= 0 || !$prod_id || $qty <= 0) return; //selecting up to $qty backorders with $prod_id where full qty is not reserved $query = 'SELECT '.TABLE_PREFIX.'Orders.OrderId FROM '.TABLE_PREFIX.'OrderItems LEFT JOIN '.TABLE_PREFIX.'Orders ON '.TABLE_PREFIX.'Orders.OrderId = '.TABLE_PREFIX.'OrderItems.OrderId WHERE (ProductId = '.$prod_id.') AND (Quantity > QuantityReserved) AND (Status = '.ORDER_STATUS_BACKORDERS.') GROUP BY '.TABLE_PREFIX.'Orders.OrderId ORDER BY OrderDate ASC LIMIT 0,'.$qty; //assuming 1 item per order - minimum possible $orders = $this->Conn->GetCol($query); if (!$orders) return; $order =& $this->Application->recallObject('ord.-inv', null, Array('skip_autoload' => true)); foreach ($orders as $ord_id) { $order->Load($ord_id); $email_event_admin =& $this->Application->EmailEventAdmin('BACKORDER.FULLFILL'); //reserve what's possible in any case $this->Application->HandleEvent( $event, 'ord:OnReserveItems' ); if ($event->status == erSUCCESS) { // //in case the order is ready to process - process it $this->Application->HandleEvent( $event, 'ord:OnOrderProcess' ); } } } function OnBeforeDeleteFromLive(&$event) { $id = $event->getEventParam('id'); $product =& $this->Application->recallObject($event->Prefix.'.itemlive', null, Array('skip_autoload' => true)); $product->SwitchToLive(); if (!$product->Load($id)) return ; // this will make sure New product will not be overwritten with empty data $temp =& $this->Application->recallObject($event->Prefix.'.itemtemp', null, Array('skip_autoload' => true)); $temp->SwitchToTemp(); $temp->Load($id); $temp->SetDBFieldsFromHash($product->FieldValues, Array('QtyInStock','QtyReserved','QtyBackOrdered','QtyOnOrder')); $temp->Update(); } function clearSelectedIDs(&$event) { parent::clearSelectedIDs($event); $this->Application->SetVar('inventory_actions', $this->Application->RecallVar('inventory_actions')); $this->Application->RemoveVar('inventory_actions'); } function OnSave(&$event) { $res = parent::OnSave($event); if ($event->status == erSUCCESS) { $this->RunScheduledInventoryActions($event); } return $res; } function OnPreCreate(&$event) { parent::onPreCreate($event); $object =& $event->GetObject(); $object->SetDBField('Type', $this->Application->GetVar( $event->getPrefixSpecial(true).'_new_type' )); } function OnPreSaveAndGo(&$event) { $event->CallSubEvent('OnPreSave'); $this->LoadItem($event); $object =& $event->getObject(); $from_type = $object->GetDBField('Type'); if ($event->status==erSUCCESS) { $this->Application->SetVar($event->Prefix_Special.'_id', $this->Application->GetVar($event->getPrefixSpecial(true).'_GoId')); $this->LoadItem($event); $to_type = $object->GetDBField('Type'); if ($from_type != $to_type) { $from_tabs = $this->GetTabs($from_type); $from_tab_i = array_search($this->Application->GetVar('t'), $from_tabs); $to_tabs = $this->GetTabs($to_type); $to_tab = $this->Application->GetVar('t'); $found = false; while ( !isset($to_tabs[$from_tab_i]) && $from_tab_i < count($to_tabs)) { $from_tab_i++; } if ( !isset($to_tabs[$from_tab_i]) ) $from_tab_i = 0; $to_tab = $to_tabs[$from_tab_i]; $event->redirect = $to_tab; } } } function GetTabs($type) { switch($type) { case 1: return Array( 0 => 'in-commerce/products/products_edit', 1 => 'in-commerce/products/products_inventory', 2 => 'in-commerce/products/products_pricing', 3 => 'in-commerce/products/products_categories', 4 => 'in-commerce/products/products_images', 5 => 'in-commerce/products/products_reviews', 6 => 'in-commerce/products/products_custom', ); case 2: return Array( 0 => 'in-commerce/products/products_edit', 1 => 'in-commerce/products/products_access', /*2 => 'in-commerce/products/products_access_pricing',*/ 3 => 'in-commerce/products/products_categories', 4 => 'in-commerce/products/products_images', 5 => 'in-commerce/products/products_reviews', 6 => 'in-commerce/products/products_custom', ); case 3: return Array( 0 => 'in-commerce/products/products_edit', 2 => 'in-commerce/products/products_access_pricing', 3 => 'in-commerce/products/products_categories', 4 => 'in-commerce/products/products_images', 5 => 'in-commerce/products/products_reviews', 6 => 'in-commerce/products/products_custom', ); case 4: return Array( 0 => 'in-commerce/products/products_edit', 2 => 'in-commerce/products/products_files', 3 => 'in-commerce/products/products_categories', 4 => 'in-commerce/products/products_images', 5 => 'in-commerce/products/products_reviews', 6 => 'in-commerce/products/products_custom', ); } } /** * Return type clauses for list bulding on front * * @param kEvent $event * @return Array */ function getTypeClauses(&$event) { $types=$event->getEventParam('types'); $except_types=$event->getEventParam('except'); $object =& $event->getObject(); $type_clauses = parent::getTypeClauses($event); $type_clauses['featured']['include']='%1$s.Featured=1 AND '.TABLE_PREFIX.'CategoryItems.PrimaryCat = 1'; $type_clauses['featured']['except']='%1$s.Featured!=1 AND '.TABLE_PREFIX.'CategoryItems.PrimaryCat = 1'; $type_clauses['featured']['having_filter']=false; $type_clauses['onsale']['include']='%1$s.OnSale=1 AND '.TABLE_PREFIX.'CategoryItems.PrimaryCat = 1'; $type_clauses['onsale']['except']='%1$s.OnSale!=1 AND '.TABLE_PREFIX.'CategoryItems.PrimaryCat = 1'; $type_clauses['onsale']['having_filter']=false; // products from selected manufacturer: begin $manufacturer = $event->getEventParam('manufacturer'); if ( !$manufacturer ) { $manufacturer = $this->Application->GetVar('manuf_id'); } if ( $manufacturer ) { $type_clauses['manufacturer']['include'] = '%1$s.ManufacturerId='.$manufacturer.' AND PrimaryCat = 1'; $type_clauses['manufacturer']['except'] = '%1$s.ManufacturerId!='.$manufacturer.' AND PrimaryCat = 1'; $type_clauses['manufacturer']['having_filter'] = false; } // products from selected manufacturer: end // recent products: begin $recent = $this->Application->RecallVar('recent_products'); if ($recent) { $recent = unserialize($recent); $type_clauses['recent']['include'] = '%1$s.ProductId IN ('.implode(',', $recent).') AND PrimaryCat = 1'; $type_clauses['recent']['except'] = '%1$s.ProductId NOT IN ('.implode(',', $recent).') AND PrimaryCat = 1'; } else { $type_clauses['recent']['include']='0'; $type_clauses['recent']['except']='1'; } $type_clauses['recent']['having_filter']=false; // recent products: end // products already in shopping cart: begin if (strpos($types, 'in_cart') !== false || strpos($except_types, 'in_cart') !== false) { $order_id = $this->Application->RecallVar('ord_id'); if ($order_id) { $in_cart = $this->Conn->GetCol('SELECT ProductId FROM '.TABLE_PREFIX.'OrderItems WHERE OrderId = '.$order_id); if ($in_cart) { $type_clauses['in_cart']['include'] = '%1$s.ProductId IN ('.implode(',', $in_cart).') AND PrimaryCat = 1'; $type_clauses['in_cart']['except'] = '%1$s.ProductId NOT IN ('.implode(',', $in_cart).') AND PrimaryCat = 1'; } else { $type_clauses['in_cart']['include']='0'; $type_clauses['in_cart']['except']='1'; } } else { $type_clauses['in_cart']['include']='0'; $type_clauses['in_cart']['except']='1'; } $type_clauses['in_cart']['having_filter']=false; } // products already in shopping cart: end // my downloadable products: begin if (strpos($types, 'my_downloads') !== false || strpos($except_types, 'my_downloads') !== false) { $user_id = $this->Application->RecallVar('user_id'); $my_downloads = ($user_id > 0) ? $this->Conn->GetCol('SELECT ProductId FROM '.TABLE_PREFIX.'UserFileAccess WHERE PortalUserId = '.$user_id) : false; if ($my_downloads) { $type_clauses['my_downloads']['include'] = '%1$s.ProductId IN ('.implode(',', $my_downloads).') AND PrimaryCat = 1'; $type_clauses['my_downloads']['except'] = '%1$s.ProductId NOT IN ('.implode(',', $my_downloads).') AND PrimaryCat = 1'; } else { $type_clauses['my_downloads']['include'] = '0'; $type_clauses['my_downloads']['except'] = '1'; } $type_clauses['my_downloads']['having_filter'] = false; } // my downloadable products: end // my favorite products: begin if (strpos($types, 'wish_list') !== false || strpos($except_types, 'wish_list') !== false) { $sql = 'SELECT ResourceId FROM '.$this->Application->getUnitOption('fav', 'TableName').' WHERE PortalUserId = '.(int)$this->Application->RecallVar('user_id'); $wishlist_ids = $this->Conn->GetCol($sql); if ($wishlist_ids) { $type_clauses['wish_list']['include'] = '%1$s.ResourceId IN ('.implode(',', $wishlist_ids).') AND PrimaryCat = 1'; $type_clauses['wish_list']['except'] = '%1$s.ResourceId NOT IN ('.implode(',', $wishlist_ids).') AND PrimaryCat = 1'; } else { $type_clauses['wish_list']['include']='0'; $type_clauses['wish_list']['except']='1'; } $type_clauses['wish_list']['having_filter']=false; } // my favorite products: end // products from package: begin if (strpos($types, 'content') !== false) { $object->removeFilter('category_filter'); $object->AddGroupByField('%1$s.ProductId'); $item_type = $this->Application->getUnitOption('p', 'ItemType'); $object_product = &$this->Application->recallObject($event->Prefix); $content_ids_array = $object_product->GetPackageContentIds(); if (sizeof($content_ids_array)==0) { $content_ids_array = array('-1'); } if (sizeof($content_ids_array)>0) { $type_clauses['content']['include'] = '%1$s.ProductId IN ('.implode(',', $content_ids_array).')'; } else { $type_clauses['content']['include']='0'; } $type_clauses['related']['having_filter']=false; } // products from package: end $object->addFilter('not_virtual', '%1$s.Virtual = 0'); if (!$this->Application->isAdminUser) { $object->addFilter('expire_filter', '%1$s.Expire IS NULL OR %1$s.Expire > UNIX_TIMESTAMP()'); } return $type_clauses; } function OnClearRecent(&$event) { $this->Application->RemoveVar('recent_products'); } /** * Occurs, when user rates a product * * @param kEvent $event */ function OnRateProduct(&$event) { $event->redirect_params = Array('pass' => 'all,p'); $event->redirect = $this->Application->GetVar('success_template'); $object =& $event->getObject(); $user_id = ($this->Application->RecallVar('user_id') == 0) ? -2 : $this->Application->RecallVar('user_id'); $sql = ' SELECT * FROM '.TABLE_PREFIX.'SpamControl WHERE ItemResourceId='.$object->GetDBField('ResourceId').' AND IPaddress="'.$_SERVER['REMOTE_ADDR'].'" AND PortalUserId='.$user_id.' AND DataType="Rating"'; $res = $this->Conn->GetRow($sql); if( $res && $res['Expire'] < adodb_mktime() ) { $sql = ' DELETE FROM '.TABLE_PREFIX.'SpamControl WHERE ItemResourceId='.$object->GetDBField('ResourceId').' AND IPaddress="'.$_SERVER['REMOTE_ADDR'].'" AND PortalUserId='.$user_id.' AND DataType="Rating"'; $this->Conn->Query($sql); unset($res); } $new_rating = $this->Application->GetVar('rating'); if($new_rating !== false && !$res) { $rating = $object->GetDBField('CachedRating'); $votes = $object->GetDBField('CachedVotesQty'); $new_votes = $votes + 1; $rating = (($rating * $votes) + $new_rating) / $new_votes; $object->SetDBField('CachedRating', $rating); $object->SetDBField('CachedVotesQty', $new_votes); $object->Update(); $expire = adodb_mktime() + $this->Application->ConfigValue('product_ReviewDelay_Value') * $this->Application->ConfigValue('product_ReviewDelay_Interval'); $sql = ' INSERT INTO '.TABLE_PREFIX.'SpamControl (ItemResourceId, IPaddress, PortalUserId, DataType, Expire) VALUES ('.$object->GetDBField('ResourceId').', "'.$_SERVER['REMOTE_ADDR'].'", '.$user_id.', "Rating", '.$expire.')'; $this->Conn->Query($sql); } else { $event->status == erFAIL; $event->redirect=false; $object->FieldErrors['CachedRating']['pseudo'] = 'too_frequent'; $object->ErrorMsgs['too_frequent'] = $this->Application->Phrase('lu_ferror_rate_duplicate'); } } function OnCancelAction(&$event) { $event->redirect_params = Array('pass' => 'all,p'); $event->redirect = $this->Application->GetVar('cancel_template'); } function OnRecommendProduct(&$event) { // used for error reporting only -> rewrite code + theme (by Alex) $object =& $this->Application->recallObject('u', null, Array('skip_autoload' => true)); // TODO: change theme too $friend_email = $this->Application->GetVar('friend_email'); $friend_name = $this->Application->GetVar('friend_name'); $my_email = $this->Application->GetVar('your_email'); $my_name = $this->Application->GetVar('your_name'); $my_message = $this->Application->GetVar('your_message'); $send_params = array(); $send_params['to_email']=$friend_email; $send_params['to_name']=$friend_name; $send_params['from_email']=$my_email; $send_params['from_name']=$my_name; $send_params['message']=$my_message; if (preg_match('/'.REGEX_EMAIL_USER.'@'.REGEX_EMAIL_DOMAIN.'/', $friend_email)) { $user_id = $this->Application->RecallVar('user_id'); $email_event = &$this->Application->EmailEventUser('PRODUCT.SUGGEST', $user_id, $send_params); $email_event = &$this->Application->EmailEventAdmin('PRODUCT.SUGGEST'); if ($email_event->status == erSUCCESS){ $event->redirect_params = array('opener' => 's', 'pass' => 'all'); $event->redirect = $this->Application->GetVar('template_success'); } else { // $event->redirect_params = array('opener' => 's', 'pass' => 'all'); // $event->redirect = $this->Application->GetVar('template_fail'); $object->ErrorMsgs['send_error'] = $this->Application->Phrase('lu_email_send_error'); $object->FieldErrors['Email']['pseudo'] = 'send_error'; $event->status = erFAIL; } } else { $object->ErrorMsgs['invalid_email'] = $this->Application->Phrase('lu_InvalidEmail'); $object->FieldErrors['Email']['pseudo'] = 'invalid_email'; $event->status = erFAIL; } } /** * Creates/updates virtual product based on listing type data * * @param kEvent $event */ function OnSaveVirtualProduct(&$event) { $object =& $event->getObject( Array('skip_autoload' => true) ); $listing_type =& $this->Application->recallObject('lst', null, Array('skip_autoload' => true)); $listing_type->Load($event->MasterEvent->getEventParam('id')); $product_id = $listing_type->GetDBField('VirtualProductId'); if ($product_id) { $object->Load($product_id); } if (!$listing_type->GetDBField('EnableBuying')) { if ($product_id) { // delete virtual product here $temp_handler =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler'); $temp_handler->DeleteItems($event->Prefix, $event->Special, Array($product_id)); $listing_type->SetDBField('VirtualProductId', 0); $listing_type->Update(); } return true; } $ml_formatter =& $this->Application->recallObject('kMultiLanguage'); $object->SetDBField($ml_formatter->LangFieldName('Name'), $listing_type->GetDBField('ShopCartName') ); $object->SetDBField($ml_formatter->LangFieldName('Description'), $listing_type->GetDBField('Description')); $object->SetDBField('SKU', 'ENHANCE_LINK_'.abs( crc32( $listing_type->GetDBField('Name') ) ) ); if ($product_id) { $object->Update(); } else { $object->SetDBField('Type', 2); $object->SetDBField('Status', 1); $object->SetDBField('HotItem', 0); $object->SetDBField('PopItem', 0); $object->SetDBField('NewItem', 0); $object->SetDBField('Virtual', 1); // $processing_data = Array('ApproveEvent' => 'ls:EnhanceLinkAfterOrderApprove', 'ExpireEvent' => 'ls:ExpireLink'); $processing_data = Array( 'ApproveEvent' => 'ls:EnhanceLinkAfterOrderApprove', 'DenyEvent' => 'ls:EnhanceLinkAfterOrderDeny', 'CompleteOrderEvent' => 'ls:EnhancedLinkOnCompleteOrder', 'ExpireEvent' => 'ls:ExpireLink', 'HasNewProcessing' => 1); $object->SetDBField('ProcessingData', serialize($processing_data)); $object->Create(); $listing_type->SetDBField('VirtualProductId', $object->GetID()); $listing_type->Update(); } $additiona_fields = Array( 'AccessDuration' => $listing_type->GetDBField('Duration'), 'AccessUnit' => $listing_type->GetDBField('DurationType'), ); $this->setPrimaryPrice($object->GetID(), (double)$listing_type->GetDBField('Price'), $additiona_fields); } /** * [HOOK] Deletes virtual product when listing type is deleted * * @param kEvent $event */ function OnDeleteListingType(&$event) { $listing_type = $event->MasterEvent->getObject(); $product_id = $listing_type->GetDBField('VirtualProductId'); if ($product_id) { $temp_handler =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler'); $temp_handler->DeleteItems($event->Prefix, $event->Special, Array($product_id)); } } /** * Extends user membership in group when his order is approved * * @param kEvent $event */ function OnSubscriptionApprove(&$event) { $field_values = $event->getEventParam('field_values'); $item_data = unserialize($field_values['ItemData']); if (!getArrayValue($item_data,'PortalGroupId')) { // is subscription product, but no group defined in it's properties trigger_error('Invalid product <b>'.$field_values['ProductName'].'</b> (id: '.$field_values['ProductId'].')'); return false; } $order_table = $this->Application->getUnitOption('ord', 'TableName'); $order_idfield = $this->Application->getUnitOption('ord', 'IDField'); $sql = 'SELECT PortalUserId FROM %s WHERE %s = %s'; $user_id = $this->Conn->GetOne( sprintf($sql, $order_table, $order_idfield, $field_values['OrderId']) ); $group_id = $item_data['PortalGroupId']; $duration = $item_data['Duration']; $sql = 'SELECT * FROM '.TABLE_PREFIX.'UserGroup WHERE PortalUserId = %s'; $user_groups = $this->Conn->Query( sprintf($sql, $user_id), 'GroupId' ); $sql = 'REPLACE INTO '.TABLE_PREFIX.'UserGroup (PortalUserId,GroupId,MembershipExpires,PrimaryGroup) VALUES (%s,%s,%s,%s)'; if( !isset($user_groups[$group_id]) ) { $primary_group = count($user_groups) == 0 ? 1 : 0; $expire = adodb_mktime() + $duration; } else { $primary_group = $user_groups[$group_id]['PrimaryGroup']; $expire = $user_groups[$group_id]['MembershipExpires']; $expire = $expire < adodb_mktime() ? adodb_mktime() + $duration : $expire + $duration; } /* // Customization healtheconomics.org if ($item_data['DurationType'] == 2) { $expire = $item_data['AccessExpiration']; } // Customization healtheconomics.org -- */ $this->Conn->Query( sprintf($sql, $user_id, $group_id, $expire, $primary_group) ); $sub_order =& $this->Application->recallObject('ord.-sub'.$event->getEventParam('next_sub_number'), 'ord'); $sub_order->SetDBField('IsRecurringBilling', getArrayValue($item_data, 'IsRecurringBilling') ? 1 : 0); $sub_order->SetDBField('GroupId', $group_id); $sub_order->SetDBField('NextCharge_date', $expire); $sub_order->SetDBField('NextCharge_time', $expire); } function OnDownloadableApprove(&$event) { $field_values = $event->getEventParam('field_values'); $product_id = $field_values['ProductId']; $sql = 'SELECT PortalUserId FROM '.$this->Application->getUnitOption('ord', 'TableName').' WHERE OrderId = '.$field_values['OrderId']; $user_id = $this->Conn->GetOne($sql); $sql = 'INSERT INTO '.TABLE_PREFIX.'UserFileAccess VALUES("", '.$product_id.', '.$user_id.')'; $this->Conn->Query($sql); } function OnPackageApprove(&$event){ $field_values = $event->getEventParam('field_values'); $item_data = unserialize($field_values['ItemData']); $package_content_ids = $item_data['PackageContent']; $object_item = &$this->Application->recallObject('p.packageitem', null, array('skip_autoload'=>true)); foreach ($package_content_ids as $package_item_id) { $object_field_values = array(); // query processing data from product and run approve event $sql = 'SELECT ProcessingData FROM '.TABLE_PREFIX.'Products WHERE ProductId = '.$package_item_id; $processing_data = $this->Conn->GetOne($sql); if($processing_data) { $processing_data = unserialize($processing_data); $approve_event = new kEvent($processing_data['ApproveEvent']); //$order_item_fields = $this->Conn->GetRow('SELECT * FROM '.TABLE_PREFIX.'OrderItems WHERE OrderItemId = '.$grouping_data[1]); $object_item->Load($package_item_id); $object_field_values['OrderId'] = $field_values['OrderId']; $object_field_values['ProductId'] = $package_item_id; $object_field_values['ItemData'] = serialize($item_data['PackageItemsItemData'][$package_item_id]); $approve_event->setEventParam('field_values', $object_field_values); $this->Application->HandleEvent($approve_event); } } } /** * Checks, that all required product options are filled in before product is saved * * @param kEvent $event */ function OnPreSave(&$event) { $this->CheckRequiredOptions($event); parent::OnPreSave($event); } /** * Set new price to ProductsPricing * * @param kEvent $event */ function OnAfterItemCreate(&$event) { parent::OnAfterItemCreate($event); $this->_updateProductPrice($event); } /** * Set new price to ProductsPricing * * @param kEvent $event */ function OnAfterItemUpdate(&$event) { parent::OnAfterItemUpdate($event); $this->_updateProductPrice($event); } /** * Updates product's primary price based on Price virtual field value * * @param kEvent $event */ function _updateProductPrice(&$event) { $object =& $event->getObject(); /* @var $object kDBItem */ $price = $object->GetDBField('Price'); // always create primary pricing, to show on Pricing tab (in admin) for tangible products $force_create = ($object->GetDBField('Type') == PRODUCT_TYPE_TANGIBLE) && is_null($price); if ($force_create || ($price != $object->GetOriginalField('Price'))) { // new product OR price was changed in virtual field $this->setPrimaryPrice($object->GetID(), (float)$price); } } function CheckRequiredOptions(&$event) { $object =& $event->getObject(); if ($object->GetDBField('ProductId') == '') return ; // if product does not have ID - it's not yet created $opt_object =& $this->Application->recallObject('po', null, Array('skip_autoload' => true) ); $has_required = $this->Conn->GetOne('SELECT COUNT(*) FROM '.$opt_object->TableName.' WHERE Required = 1 AND ProductId = '.$object->GetDBField('ProductId')); //we need to imitate data sumbit, as parent' PreSave sets object values from $items_info $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); $items_info[$object->GetDBField('ProductId')]['HasRequiredOptions'] = $has_required ? 1:0; $this->Application->SetVar($event->getPrefixSpecial(true), $items_info); $object->SetDBField('HasRequiredOptions', $has_required ? 1:0); } /** * Sets required price in primary price backed, if it's missing, then create it * * @param int $product_id * @param double $price * @param Array $additional_fields * @return bool */ function setPrimaryPrice($product_id, $price, $additional_fields = Array()) { $pr_object =& $this->Application->recallObject('pr.-item', null, Array('skip_autoload' => true) ); /* @var $pr_object kDBItem */ $pr_object->Load( Array('ProductId' => $product_id, 'IsPrimary' => 1) ); $sql = 'SELECT COUNT(*) FROM '.$pr_object->TableName.' WHERE ProductId = '.$product_id; $has_pricings = $this->Conn->GetOne($sql); if ($additional_fields) { $pr_object->SetDBFieldsFromHash($additional_fields); } if( ($price === false) && $has_pricings ) return false; if( $pr_object->isLoaded() ) { $pr_object->SetField('Price', $price); return $pr_object->Update(); } else { $group_id = $this->Application->ConfigValue('User_LoggedInGroup'); $field_values = Array('ProductId' => $product_id, 'IsPrimary' => 1, 'MinQty' => 1, 'MaxQty' => -1, 'GroupId'=>$group_id); $pr_object->SetDBFieldsFromHash($field_values); $pr_object->SetField('Price', $price); $ret = $pr_object->Create(); if ($pr_object->mode == 't') { $pr_object->setTempID(); } return $ret; } } /** * Enter description here... * * @param kEvent $event */ function OnAfterItemDelete(&$event) { $product_id = $event->getEventParam('id'); if(!$product_id) { return; } $sql = 'DELETE FROM '.TABLE_PREFIX.'UserFileAccess WHERE ProductId = '.$product_id; $this->Conn->Query($sql); } /** * Load price from temp table if product mode is temp table * * @param kEvent $event */ function OnAfterItemLoad(&$event) { parent::OnAfterItemLoad($event); $object =& $event->getObject(); /* @var $object kDBItem */ $a_pricing = $object->getPrimaryPricing(); if (!$a_pricing) { // pricing doesn't exist for new products $price = $cost = null; } else { $price = (float)$a_pricing['Price']; $cost = (float)$a_pricing['Cost']; } // set original fields to use them in OnAfterItemCreate/OnAfterItemUpdate later $object->SetDBField('Price', $price); $object->SetOriginalField('Price', $price); $object->SetDBField('Cost', $cost); $object->SetOriginalField('Cost', $cost); } /** * Allows to add products to package besides all that parent method does * * @param kEvent $event */ function OnProcessSelected(&$event) { $dst_field = $this->Application->RecallVar('dst_field'); if ($dst_field == 'PackageContent') { $this->OnAddToPackage($event); } elseif ($dst_field == 'AssignedCoupon') { $coupon_id = $this->Application->GetVar('selected_ids'); $object =& $event->getObject(); $object->SetDBField('AssignedCoupon', $coupon_id); $this->RemoveRequiredFields($object); $object->Update(); } else { parent::OnProcessSelected($event); } $this->finalizePopup($event); } /** * Called when some products are selected in products selector for this prefix * * @param kEvent $event */ function OnAddToPackage(&$event) { $selected_ids = $this->Application->GetVar('selected_ids'); // update current package content with selected products $object =& $event->getObject(); /* @var $object ProductsItem */ $product_ids = $selected_ids['p'] ? explode(',', $selected_ids['p']) : Array(); if ($product_ids) { $current_ids = $object->GetPackageContentIds(); $current_ids = array_unique(array_merge($current_ids, $product_ids)); // remove package product from selected list $this_product = array_search($object->GetID(), $current_ids); if ($this_product !== false) { unset($current_ids[$this_product]); } $dst_field = $this->Application->RecallVar('dst_field'); $object->SetDBField($dst_field, '|'.implode('|', $current_ids).'|'); $object->Update(); $this->ProcessPackageItems($event); } $this->finalizePopup($event); } function ProcessPackageItems(&$event) { //$this->Application->SetVar('p_mode', 't'); $object =& $event->getObject(); /* @var $object ProductsItem */ $content_ids = $object->GetPackageContentIds(); if (sizeof($content_ids) > 0) { $total_weight = $this->Conn->GetOne('SELECT SUM(Weight) FROM '.TABLE_PREFIX.'Products WHERE ProductId IN ('.implode(', ', $content_ids).') AND Type=1'); if (!$total_weight) $total_weight = 0; $this->Conn->Query('UPDATE '.$object->TableName.' SET Weight='.$total_weight.' WHERE ProductId='.$object->GetID()); } /* $this->Application->SetVar('p_mode', false); $list = &$this->Application->recallObject('p.content', 'p_List', array('types'=>'content')); $this->Application->SetVar('p_mode', 't'); $list->Query(); $total_weight_a = 0; $total_weight_b = 0; $list->GoFirst(); while (!$list->EOL()) { if ($list->GetDBField('Type')==1){ $total_weight_a += $list->GetField('Weight_a'); $total_weight_b += $list->GetField('Weight_b'); } $list->GoNext(); } $object->SetField('Weight_a', $total_weight_a); $object->SetField('Weight_b', $total_weight_b); */ //$object->Update(); } /** * Enter description here... * * @param kEvent $event */ function OnSaveItems(&$event) { //$event->CallSubEvent('OnUpdate'); $event->redirect = false; //$event->redirect_params = Array('opener'=>'s','pass'=>'all,p'); } /** * Removes product from package * * @param kEvent $event */ function OnRemovePackageItem(&$event) { $this->Application->SetVar('p_mode', 't'); $object =& $event->getObject(); $items_info = $this->Application->GetVar('p_content'); if($items_info) { $product_ids = array_keys($items_info); $current_ids = $object->GetPackageContentIds(); $current_ids_flip = array_flip($current_ids); foreach($product_ids as $key=>$val){ unset($current_ids_flip[$val]); } $current_ids = array_keys($current_ids_flip); $current_ids_str = '|'.implode('|', array_unique($current_ids)).'|'; $object->SetDBField('PackageContent', $current_ids_str); } $object->Update(); $this->ProcessPackageItems($event); } /** * Enter description here... * * @param kEvent $event */ function OnBeforeItemDelete(&$event){ $object = &$event->getObject(); $product_includes_in = $this->Conn->GetOne('SELECT COUNT(*) FROM '.TABLE_PREFIX.'Products WHERE PackageContent LIKE "%|'.$object->GetID().'%"'); if ($product_includes_in > 0){ $event->status=erFAIL; } } /** * Returns specific to each item type columns only * * @param kEvent $event * @return Array */ function getCustomExportColumns(&$event) { $columns = parent::getCustomExportColumns($event); $new_columns = Array( '__VIRTUAL__Price' => 'Price', '__VIRTUAL__Cost' => 'Cost', ); return array_merge_recursive2($columns, $new_columns); } /** * Sets non standart virtual fields (e.g. to other tables) * * @param kEvent $event */ function setCustomExportColumns(&$event) { parent::setCustomExportColumns($event); $object =& $event->getObject(); $this->setPrimaryPrice($object->GetID(), (double)$object->GetDBField('Price'), Array('Cost' => (double)$object->GetDBField('Cost')) ); } function OnPreSaveAndOpenPopup(&$event) { $object =& $event->getObject(); $this->RemoveRequiredFields($object); $event->CallSubEvent('OnPreSave'); $event->redirect = $this->Application->GetVar('t'); // pass ID too, in case if product is created by OnPreSave call to ensure proper editing $event->redirect_params = Array( 'pass' => 'all', $event->getPrefixSpecial(true).'_id' => $object->GetID(), ); } function getPassedID(&$event) { $event->setEventParam('raise_warnings', 0); $passed = parent::getPassedID($event); if ($passed) { return $passed; } if ($this->Application->isAdminUser) { // we may get product id out of OrderItem, if it exists $ord_item =& $this->Application->recallObject('orditems', null, Array ('raise_warnings' => 0)); if ($ord_item->GetDBField('ProductId')) { $passed = $ord_item->GetDBField('ProductId'); } } return $passed; } function OnAfterConfigRead(&$event) { parent::OnAfterConfigRead($event); if (!$this->Application->LoggedIn()) { return ; } $user_id = $this->Application->RecallVar('user_id'); $calculated_fields = $this->Application->getUnitOption($event->Prefix, 'CalculatedFields'); $sql = 'SELECT GroupId FROM ' . TABLE_PREFIX . 'UserGroup WHERE PrimaryGroup = 1 AND PortalUserId = ' . $user_id; $primary_group = $this->Conn->GetOne($sql); if (!$primary_group) { return; } $sub_select = ' SELECT pp.Price FROM ' . TABLE_PREFIX . 'ProductsPricing AS pp WHERE pp.ProductId = %1$s.ProductId AND GroupId = ' . $primary_group . ' ORDER BY MinQty LIMIT 0,1'; $calculated_fields['']['Price'] = 'IFNULL((' . $sub_select . '), ' . $calculated_fields['']['Price'] . ')'; $this->Application->setUnitOption($event->Prefix, 'CalculatedFields', $calculated_fields); } /** * Starts product editing, remove any pending inventory actions * * @param kEvent $event */ function OnEdit(&$event) { $this->Application->RemoveVar('inventory_actions'); parent::OnEdit($event); } /** * Adds "Shop Cart" tab on paid listing type editing tab * * @param kEvent $event */ function OnModifyPaidListingConfig(&$event) { $edit_tab_presets = $this->Application->getUnitOption($event->MasterEvent->Prefix, 'EditTabPresets'); $edit_tab_presets['Default']['shopping_cart'] = Array ('title' => 'la_tab_ShopCartEntry', 't' => 'in-commerce/paid_listings/paid_listing_type_shopcart', 'priority' => 2); $this->Application->setUnitOption($event->MasterEvent->Prefix, 'EditTabPresets', $edit_tab_presets); } + + /** + * [HOOK] Allows to add cloned subitem to given prefix + * + * @param kEvent $event + */ + function OnCloneSubItem(&$event) + { + parent::OnCloneSubItem($event); + + if ($event->MasterEvent->Prefix == 'rev') { + $clones = $this->Application->getUnitOption($event->MasterEvent->Prefix, 'Clones'); + $subitem_prefix = $event->Prefix . '-' . $event->MasterEvent->Prefix; + + $clones[$subitem_prefix]['ConfigMapping'] = Array ( + 'PerPage' => 'Comm_Perpage_Reviews', + + 'ReviewDelayInterval' => 'product_ReviewDelay_Value', + 'ReviewDelayValue' => 'product_ReviewDelay_Interval', + ); + + $this->Application->setUnitOption($event->MasterEvent->Prefix, 'Clones', $clones); + } + } } \ No newline at end of file Index: branches/5.0.x/units/products/products_config.php =================================================================== --- branches/5.0.x/units/products/products_config.php (revision 13285) +++ branches/5.0.x/units/products/products_config.php (revision 13286) @@ -1,654 +1,687 @@ <?php /** * @version $Id$ * @package In-Commerce * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license Commercial License * This software is protected by copyright law and international treaties. * Unauthorized reproduction or unlicensed usage of the code of this program, * or any portion of it may result in severe civil and criminal penalties, * and will be prosecuted to the maximum extent possible under the law * See http://www.in-portal.org/commercial-license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); $config = Array ( 'Prefix' => 'p', 'ItemClass' => Array ('class' => 'ProductsItem', 'file' => 'products_item.php', 'require_classes' => Array ('kCatDBItem'), 'build_event' => 'OnItemBuild'), 'ListClass' => Array ('class' => 'kCatDBList', 'file' => '', 'build_event' => 'OnListBuild'), 'EventHandlerClass' => Array ('class' => 'ProductsEventHandler', 'file' => 'products_event_handler.php', 'require_classes' => Array ('kCatDBEventHandler'), 'build_event' => 'OnBuild'), 'TagProcessorClass' => Array ('class' => 'ProductsTagProcessor', 'file' => 'products_tag_processor.php', 'require_classes' => Array ('kCatDBTagProcessor'), 'build_event' => 'OnBuild'), 'AutoLoad' => true, 'QueryString' => Array ( 1 => 'id', 2 => 'Page', 3 => 'event', 4 => 'mode', ), 'CatalogItem' => true, 'AdminTemplatePath' => 'products', 'AdminTemplatePrefix' => 'products_', 'SearchConfigPostfix' => 'products', 'ConfigPriority' => 0, 'RewritePriority' => 104, 'RewriteListener' => 'ModRewriteHelper:CategoryItemRewriteListener', 'Hooks' => Array ( // for subscription products: access group is saved before changing pricings Array ( 'Mode' => hAFTER, 'Conditional' => true, 'HookToPrefix' => 'pr', 'HookToSpecial' => '*', 'HookToEvent' => Array ('OnNew', 'OnAfterItemLoad'), 'DoPrefix' => '', 'DoSpecial' => '*', 'DoEvent' => 'OnPreSave', ), Array ( 'Mode' => hAFTER, 'Conditional' => false, 'HookToPrefix' => 'lst', 'HookToSpecial' => '', 'HookToEvent' => Array ( 'OnBeforeCopyToLive' ), 'DoPrefix' => '', 'DoSpecial' => '', 'DoEvent' => 'OnSaveVirtualProduct', ), Array ( 'Mode' => hAFTER, 'Conditional' => false, 'HookToPrefix' => 'lst', 'HookToSpecial' => '*', 'HookToEvent' => Array ('OnAfterItemDelete'), 'DoPrefix' => '', 'DoSpecial' => '', 'DoEvent' => 'OnDeleteListingType', ), Array ( 'Mode' => hAFTER, 'Conditional' => false, 'HookToPrefix' => 'lst', 'HookToSpecial' => '*', 'HookToEvent' => Array ('OnAfterConfigRead'), 'DoPrefix' => '', 'DoSpecial' => '', 'DoEvent' => 'OnModifyPaidListingConfig', ), Array ( 'Mode' => hBEFORE, 'Conditional' => false, 'HookToPrefix' => 'file', 'HookToSpecial' => '', 'HookToEvent' => Array ( 'OnNew', 'OnEdit' ), 'DoPrefix' => '', 'DoSpecial' => '', 'DoEvent' => 'OnPreSave', ), Array ( 'Mode' => hBEFORE, 'Conditional' => false, 'HookToPrefix' => '', 'HookToSpecial' => '*', 'HookToEvent' => Array ('OnAfterConfigRead'), 'DoPrefix' => 'cdata', 'DoSpecial' => '*', 'DoEvent' => 'OnDefineCustomFields', ), + + Array ( + 'Mode' => hBEFORE, + 'Conditional' => false, + 'HookToPrefix' => 'rev', + 'HookToSpecial' => '*', + 'HookToEvent' => Array ('OnAfterConfigRead'), + 'DoPrefix' => '', + 'DoSpecial' => '*', + 'DoEvent' => 'OnCloneSubItem', + ), + + Array ( + 'Mode' => hBEFORE, + 'Conditional' => false, + 'HookToPrefix' => 'fav', + 'HookToSpecial' => '*', + 'HookToEvent' => Array ('OnAfterConfigRead'), + 'DoPrefix' => '', + 'DoSpecial' => '*', + 'DoEvent' => 'OnCloneSubItem', + ), + + Array ( + 'Mode' => hBEFORE, + 'Conditional' => false, + 'HookToPrefix' => 'ci', + 'HookToSpecial' => '*', + 'HookToEvent' => Array ('OnAfterConfigRead'), + 'DoPrefix' => '', + 'DoSpecial' => '*', + 'DoEvent' => 'OnCloneSubItem', + ), ), 'IDField' => 'ProductId', 'StatusField' => Array ('Status'), // field, that is affected by Approve/Decline events 'TitleField' => 'Name', // field, used in bluebar when editing existing item 'ItemType' => 11, // this is used when relation to product is added from in-portal and via-versa 'ViewMenuPhrase' => 'la_text_Products', 'CatalogTabIcon' => 'in-commerce:icon16_products.png', 'ItemPropertyMappings' => Array ( 'NewDays' => 'Product_NewDays', // number of days item to be NEW 'MinPopVotes' => 'Product_MinPopVotes', // minimum number of votes for an item to be POP 'MinPopRating' => 'Product_MinPopRating', // minimum rating for an item to be POP 'MaxHotNumber' => 'Product_MaxHotNumber', // maximum number of HOT (top seller) items 'HotLimit' => 'Product_HotLimit', // variable name in inp_Cache table 'ClickField' => 'Hits', // item click count is stored here (in item table) ), 'TitlePhrase' => 'la_text_Product', 'TitlePresets' => Array ( 'default' => Array ( 'new_status_labels' => Array ('p' => '!la_title_Adding_Product!'), 'edit_status_labels' => Array ('p' => '!la_title_Editing_Product!'), 'new_titlefield' => Array ('p' => '!la_title_NewProduct!'), ), 'product_list' =>Array ( 'prefixes' => Array ('c_List', 'p_List'), 'tag_params' => Array ('c' => Array ('per_page' =>-1)), 'format' => "!la_title_Categories! (#c_recordcount#) - !la_title_Products! (#p_recordcount#)", ), 'products_edit' =>Array ( 'prefixes' => Array ('p'), 'new_titlefield' => Array ('p' => '!la_title_NewProduct!'), 'format' => "#p_status# '#p_titlefield#' - !la_title_General!", ), 'inventory' => Array ('prefixes' => Array ('p'), 'format' => "#p_status# - '#p_titlefield#' - !la_title_Product_Inventory!"), 'pricing' => Array ('prefixes' => Array ('p'), 'format' => "#p_status# '#p_titlefield#' - !la_title_Product_Pricing!"), 'access_pricing' => Array ('prefixes' => Array ('p'), 'format' => "#p_status# '#p_titlefield#' - !la_title_Product_AccessPricing!"), 'access' => Array ('prefixes' => Array ('p'), 'format' => "#p_status# '#p_titlefield#' - !la_title_Product_Access!"), 'files' => Array ('prefixes' => Array ('p'), 'format' => "#p_status# '#p_titlefield#' - !la_title_Product_Files!"), 'options' => Array ('prefixes' => Array ('p'), 'format' => "#p_status# '#p_titlefield#' - !la_title_Product_Options!"), 'categories' => Array ('prefixes' => Array ('p', 'p-ci_List'), 'format' => "#p_status# '#p_titlefield#' - !la_title_Categories!"), 'relations' => Array ('prefixes' => Array ('p'), 'format' => "#p_status# '#p_titlefield#' - !la_title_Relations!"), 'content' => Array ('prefixes' => Array ('p', 'p.content_List'), 'tag_params' => Array ('p.content' => Array ('types' => 'content', 'live_table' =>true)), 'format' => "#p_status# '#p_titlefield#' - !la_title_Product_PackageContent!"), 'images' => Array ('prefixes' => Array ('p'), 'format' => "#p_status# '#p_titlefield#' - !la_title_Images!"), 'reviews' => Array ('prefixes' => Array ('p'), 'format' => "#p_status# '#p_titlefield#' - !la_title_Reviews!"), 'products_custom' => Array ('prefixes' => Array ('p'), 'format' => "#p_status# '#p_titlefield#' - !la_title_Custom!"), 'images_edit' => Array ( 'prefixes' => Array ('p', 'img'), 'new_status_labels' => Array ('img' => '!la_title_Adding_Image!'), 'edit_status_labels' => Array ('img' => '!la_title_Editing_Image!'), 'new_titlefield' => Array ('img' => '!la_title_New_Image!'), 'format' => "#p_status# '#p_titlefield#' - #img_status# '#img_titlefield#'", ), 'pricing_edit' => Array ( 'prefixes' => Array ('p', 'pr'), 'new_status_labels' => Array ('pr' =>"!la_title_Adding_PriceBracket! '!la_title_New_PriceBracket!'"), 'edit_status_labels' => Array ('pr' => '!la_title_Editing_PriceBracket!'), 'format' => "#p_status# '#p_titlefield#' - #pr_status#", ), 'options_edit' => Array ( 'prefixes' => Array ('p', 'po'), 'new_status_labels' => Array ('po' =>"!la_title_Adding_Option!"), 'edit_status_labels' => Array ('po' => '!la_title_Editing_Option!'), 'new_titlefield' => Array ('po' => '!la_title_New_Option!'), 'format' => "#p_status# '#p_titlefield#' - #po_status# '#po_titlefield#'", ), 'options_combinations' => Array ('prefixes' => Array ('p'), 'format' => "#p_status# '#p_titlefield#' - !la_title_ManagingOptionCombinations!"), 'shipping_options' => Array ('prefixes' => Array ('p'), 'format' => "#p_status# '#p_titlefield#' - !la_title_ManagingShippingOptions!"), 'file_edit' => Array ( 'prefixes' => Array ('p', 'file'), 'new_status_labels' => Array ('file' =>"!la_title_Adding_File!"), 'edit_status_labels' => Array ('file' => '!la_title_Editing_File!'), 'new_titlefield' => Array ('file' => '!la_title_New_File!'), 'format' => "#p_status# '#p_titlefield#' - #file_status# '#file_titlefield#'", ), 'relations_edit' => Array ( 'prefixes' => Array ('p', 'rel'), 'new_status_labels' => Array ('rel' =>"!la_title_Adding_Relationship! '!la_title_New_Relationship!'"), 'edit_status_labels' => Array ('rel' => '!la_title_Editing_Relationship!'), 'format' => "#p_status# '#p_titlefield#' - #rel_status#", ), 'reviews_edit' => Array ( 'prefixes' => Array ('p', 'rev'), 'new_status_labels' => Array ('rev' =>"!la_title_Adding_Review! '!la_title_New_Review!'"), 'edit_status_labels' => Array ('rev' => '!la_title_Editing_Review!'), 'format' => "#p_status# '#p_titlefield#' - #rev_status#", ), 'products_export' => Array ('format' => '!la_title_ProductsExport!'), 'products_import' => Array ('format' => '!la_title_ImportProducts!'), 'tree_in-commerce' => Array ('format' => '!la_Text_Version! '.$this->Application->findModule('Name', 'In-Commerce', 'Version')), ), 'EditTabPresets' => Array ( 'Default' => Array ( 'general' => Array ('title' => 'la_tab_General', 't' => 'in-commerce/products/products_edit', 'priority' => 1), 'inventory' => Array ('title' => 'la_tab_Inventory', 't' => 'in-commerce/products/products_inventory', 'priority' => 2), 'access_and_pricing' => Array ('title' => 'la_tab_AccessAndPricing', 't' => 'in-commerce/products/products_access', 'priority' => 3), 'pricing' => Array ('title' => 'la_tab_Pricing', 't' => 'in-commerce/products/products_pricing', 'priority' => 4), // 'pricing2' => Array ('title' => 'la_tab_Pricing', 't' => 'in-commerce/products/products_access_pricing', 'priority' => 5), 'files_and_pricing' => Array ('title' => 'la_tab_FilesAndPricing', 't' => 'in-commerce/products/products_files', 'priority' => 6), 'options' => Array ('title' => 'la_tab_Options', 't' => 'in-commerce/products/products_options', 'priority' => 7), 'categories' => Array ('title' => 'la_tab_Categories', 't' => 'in-commerce/products/products_categories', 'priority' => 8), 'relations' => Array ('title' => 'la_tab_Relations', 't' => 'in-commerce/products/products_relations', 'priority' => 9), 'package_content' => Array ('title' => 'la_tab_PackageContent', 't' => 'in-commerce/products/products_packagecontent', 'priority' => 10), 'images' => Array ('title' => 'la_tab_Images', 't' => 'in-commerce/products/products_images', 'priority' => 11), 'reviews' => Array ('title' => 'la_tab_Reviews', 't' => 'in-commerce/products/products_reviews', 'priority' => 12), 'custom' => Array ('title' => 'la_tab_Custom', 't' => 'in-commerce/products/products_custom', 'priority' => 13), ), ), 'PermItemPrefix' => 'PRODUCT', 'PermTabText' => 'In-Commerce', 'PermSection' => Array ('main' => 'CATEGORY:in-commerce:products_list', 'search' => 'in-commerce:search', 'email' => 'in-commerce:incommerce_configemail', 'custom' => 'in-commerce:configuration_custom'), 'Sections' => Array ( 'in-commerce' => Array ( 'parent' => 'in-portal:root', 'icon' => 'ecommerce', 'label' => 'la_title_In-Commerce', 'url' => Array ('t' => 'index', 'pass_section' => true, 'pass' => 'm'), 'permissions' => Array ('view'), 'priority' => 2.1, 'container' => true, 'type' => stTREE, ), 'in-commerce:products' => Array ( 'parent' => 'in-portal:site', 'icon' => 'products', 'label' => 'la_tab_Products', 'url' => Array ('t' => 'catalog/advanced_view', 'anchor' => 'tab-p.showall', 'pass' => 'm'), 'onclick' => 'setCatalogTab(\'p.showall\')', 'permissions' => Array ('view'), 'priority' => 3.2, 'type' => stTREE, ), // product settings 'in-commerce:setting_folder' => Array ( 'parent' => 'in-portal:system', 'icon' => 'conf_ecommerce', 'label' => 'la_title_In-Commerce', 'use_parent_header' => 1, 'url' => Array ('t' => 'index', 'pass_section' => true, 'pass' => 'm'), 'permissions' => Array ('view'), 'priority' => 3.1, 'container' => true, 'type' => stTREE, ), 'in-commerce:general' => Array ( 'parent' => 'in-commerce:setting_folder', 'icon' => 'conf_ecommerce_general', 'label' => 'la_tab_GeneralSettings', 'url' => Array ('t' => 'config/config_general', 'pass_section' => true, 'pass' => 'm'), 'permissions' => Array ('view', 'edit'), 'priority' => 1, 'type' => stTREE, ), 'in-commerce:output' => Array ( 'parent' => 'in-commerce:setting_folder', 'icon' => 'core:conf_output', 'label' => 'la_tab_ConfigOutput', 'url' => Array ('t' => 'config/config_universal', 'pass_section' => true, 'pass' => 'm'), 'permissions' => Array ('view', 'edit'), 'priority' => 2, 'type' => stTREE, ), 'in-commerce:search' => Array ( 'parent' => 'in-commerce:setting_folder', 'icon' => 'core:conf_search', 'label' => 'la_tab_ConfigSearch', 'url' => Array ('t' => 'config/config_search', 'module_key' => 'products', 'pass_section' => true, 'pass' => 'm'), 'permissions' => Array ('view', 'edit'), 'priority' => 7, 'type' => stTREE, ), 'in-commerce:incommerce_configemail' => Array ( 'parent' => 'in-commerce:setting_folder', 'icon' => 'core:conf_email', 'label' => 'la_tab_ConfigE-mail', 'url' => Array ('t' => 'config/config_email', 'pass_section' => true, 'pass' => 'm'), 'permissions' => Array ('view', 'edit'), 'priority' => 8, 'type' => stTREE, ), 'in-commerce:configuration_custom' => Array ( 'parent' => 'in-commerce:setting_folder', 'icon' => 'core:conf_customfields', 'label' => 'la_tab_ConfigCustom', 'url' => Array ('t' => 'custom_fields/custom_fields_list', 'cf_type' => 11, 'pass_section' => true, 'pass' => 'm,cf'), 'permissions' => Array ('view', 'add', 'edit', 'delete'), 'priority' => 9, 'type' => stTREE, ), 'in-commerce:contacts' => Array ( 'parent' => 'in-commerce:setting_folder', 'icon' => 'conf_contact_info', 'label' => 'la_tab_ConfigContacts', 'url' => Array ('t' => 'config/config_universal', 'pass_section' => true, 'pass' => 'm'), 'permissions' => Array ('view', 'edit'), 'priority' => 10, 'type' => stTREE, ), ), 'FilterMenu' => Array ( 'Groups' => Array ( Array ('mode' => 'AND', 'filters' => Array ('show_active', 'show_pending', 'show_disabled'), 'type' => WHERE_FILTER), Array ('mode' => 'AND', 'filters' => Array ('show_tang', 'show_sub', 'show_serv', 'show_download', 'show_package'), 'type' => WHERE_FILTER), Array ('mode' => 'AND', 'filters' => Array ('show_new'), 'type' => HAVING_FILTER), Array ('mode' => 'AND', 'filters' => Array ('show_hot'), 'type' => HAVING_FILTER), Array ('mode' => 'AND', 'filters' => Array ('show_pop'), 'type' => HAVING_FILTER), Array ('mode' => 'AND', 'filters' => Array ('show_pick'), 'type' => WHERE_FILTER), ), 'Filters' => Array ( 'show_active' => Array ('label' => 'la_Active', 'on_sql' => '', 'off_sql' => '%1$s.Status != 1' ), 'show_pending' => Array ('label' => 'la_Pending', 'on_sql' => '', 'off_sql' => '%1$s.Status != 2' ), 'show_disabled' => Array ('label' => 'la_Disabled', 'on_sql' => '', 'off_sql' => '%1$s.Status != 0' ), 's1' => Array (), 'show_tang' => Array ('label' => 'la_product_tangible', 'on_sql' => '', 'off_sql' => '%1$s.Type != 1' ), 'show_sub' => Array ('label' => 'la_product_subscription', 'on_sql' => '', 'off_sql' => '%1$s.Type != 2' ), 'show_serv' => Array ('label' => 'la_product_service', 'on_sql' => '', 'off_sql' => '%1$s.Type != 3' ), 'show_download' => Array ('label' => 'la_product_downloadable', 'on_sql' => '', 'off_sql' => '%1$s.Type != 4' ), 'show_package' => Array ('label' => 'la_product_package', 'on_sql' => '', 'off_sql' => '%1$s.Type != 5' ), 's2' => Array (), 'show_new' => Array ('label' => 'la_Text_New', 'on_sql' => '', 'off_sql' => '`IsNew` != 1' ), 'show_hot' => Array ('label' => 'la_Text_TopSellers', 'on_sql' => '', 'off_sql' => '`IsHot` != 1' ), 'show_pop' => Array ('label' => 'la_Text_Pop', 'on_sql' => '', 'off_sql' => '`IsPop` != 1' ), 'show_pick' => Array ('label' => 'la_prompt_EditorsPick', 'on_sql' => '', 'off_sql' => '%1$s.`EditorsPick` != 1' ), ) ), 'TableName' => TABLE_PREFIX.'Products', 'CalculatedFields' => Array ( '' => Array ( 'SameImages' => 'img.SameImages', 'LocalThumb' => 'img.LocalThumb', 'ThumbPath' => 'img.ThumbPath', 'ThumbUrl' => 'img.ThumbUrl', 'LocalImage' => 'img.LocalImage', 'LocalPath' => 'img.LocalPath', 'FullUrl' => 'img.Url', 'Price' => 'COALESCE(pricing.Price, 0)', 'Cost' => 'COALESCE(pricing.Cost, 0)', 'PrimaryCat' => TABLE_PREFIX.'%3$sCategoryItems.PrimaryCat', 'CategoryId' => TABLE_PREFIX.'%3$sCategoryItems.CategoryId', 'ParentPath' => TABLE_PREFIX.'Category.ParentPath', 'Manufacturer' => TABLE_PREFIX.'Manufacturers.Name', 'Filename' => TABLE_PREFIX.'%3$sCategoryItems.Filename', 'FileSize' => 'files.Size', 'FilePath' => 'files.FilePath', 'FileVersion' => 'files.Version', ), 'showall' => Array ( 'Price' => 'COALESCE(pricing.Price, 0)', 'Manufacturer' => TABLE_PREFIX.'Manufacturers.Name', 'PrimaryCat' => TABLE_PREFIX.'%3$sCategoryItems.PrimaryCat', 'CategoryId' => TABLE_PREFIX.'%3$sCategoryItems.CategoryId', 'FileSize' => 'files.Size', 'FilePath' => 'files.FilePath', 'FileVersion' => 'files.Version', 'Filename' => TABLE_PREFIX.'%3$sCategoryItems.Filename', ), ), 'ListSQLs' => Array ( '' => ' SELECT %1$s.* %2$s FROM %1$s LEFT JOIN '.TABLE_PREFIX.'PortalGroup ON '.TABLE_PREFIX.'PortalGroup.GroupId = %1$s.AccessGroupId LEFT JOIN '.TABLE_PREFIX.'%3$sCategoryItems ON '.TABLE_PREFIX.'%3$sCategoryItems.ItemResourceId = %1$s.ResourceId LEFT JOIN '.TABLE_PREFIX.'Category ON '.TABLE_PREFIX.'Category.CategoryId = '.TABLE_PREFIX.'%3$sCategoryItems.CategoryId LEFT JOIN '.TABLE_PREFIX.'Images img ON img.ResourceId = %1$s.ResourceId AND img.DefaultImg = 1 LEFT JOIN '.TABLE_PREFIX.'ProductFiles files ON files.ProductId = %1$s.ProductId AND files.IsPrimary = 1 LEFT JOIN '.TABLE_PREFIX.'ProductsPricing pricing ON pricing.ProductId = %1$s.ProductId AND pricing.IsPrimary = 1 LEFT JOIN '.TABLE_PREFIX.'Manufacturers ON '.TABLE_PREFIX.'Manufacturers.ManufacturerId = %1$s.ManufacturerId LEFT JOIN '.TABLE_PREFIX.'PermCache perm ON perm.CategoryId = '.TABLE_PREFIX.'%3$sCategoryItems.CategoryId LEFT JOIN '.TABLE_PREFIX.'%3$sProductsCustomData cust ON %1$s.ResourceId = cust.ResourceId', 'showall' => 'SELECT %1$s.* %2$s FROM %1$s LEFT JOIN '.TABLE_PREFIX.'ProductsPricing pricing ON pricing.ProductId = %1$s.ProductId AND pricing.IsPrimary = 1 LEFT JOIN '.TABLE_PREFIX.'ProductFiles files ON files.ProductId = %1$s.ProductId AND files.IsPrimary = 1 LEFT JOIN '.TABLE_PREFIX.'Manufacturers ON '.TABLE_PREFIX.'Manufacturers.ManufacturerId = %1$s.ManufacturerId LEFT JOIN '.TABLE_PREFIX.'%3$sCategoryItems ON '.TABLE_PREFIX.'%3$sCategoryItems.ItemResourceId = %1$s.ResourceId LEFT JOIN '.TABLE_PREFIX.'Category ON '.TABLE_PREFIX.'Category.CategoryId = '.TABLE_PREFIX.'%3$sCategoryItems.CategoryId LEFT JOIN '.TABLE_PREFIX.'PermCache perm ON perm.CategoryId = '.TABLE_PREFIX.'%3$sCategoryItems.CategoryId LEFT JOIN '.TABLE_PREFIX.'%3$sProductsCustomData cust ON %1$s.ResourceId = cust.ResourceId', ), // key - special, value - list select sql 'ListSortings' => Array ( '' => Array ( 'ForcedSorting' => Array ('Priority' => 'desc'), 'Sorting' => Array ('Name' => 'asc'), ) ), 'ItemSQLs' => Array ( '' => ' SELECT %1$s.* %2$s FROM %1$s LEFT JOIN '.TABLE_PREFIX.'PortalGroup pg ON pg.GroupId = %1$s.AccessGroupId LEFT JOIN '.TABLE_PREFIX.'%3$sCategoryItems ON '.TABLE_PREFIX.'%3$sCategoryItems.ItemResourceId = %1$s.ResourceId LEFT JOIN '.TABLE_PREFIX.'Category ON '.TABLE_PREFIX.'Category.CategoryId = '.TABLE_PREFIX.'%3$sCategoryItems.CategoryId LEFT JOIN '.TABLE_PREFIX.'Images img ON img.ResourceId = %1$s.ResourceId AND img.DefaultImg = 1 LEFT JOIN '.TABLE_PREFIX.'ProductFiles files ON files.ProductId = %1$s.ProductId AND files.IsPrimary = 1 LEFT JOIN '.TABLE_PREFIX.'ProductsPricing pricing ON pricing.ProductId = %1$s.ProductId AND pricing.IsPrimary = 1 LEFT JOIN '.TABLE_PREFIX.'Manufacturers ON '.TABLE_PREFIX.'Manufacturers.ManufacturerId = %1$s.ManufacturerId LEFT JOIN '.TABLE_PREFIX.'%3$sProductsCustomData cust ON %1$s.ResourceId = cust.ResourceId', ), 'SubItems' => Array ('pr', 'rev', 'img', 'po', 'poc', 'p-ci', 'rel', 'file', 'p-cdata', 'p-fav'), 'Fields' => Array ( 'ProductId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0,), 'Name' => Array ('type' => 'string', 'formatter' => 'kMultiLanguage', 'required' => 1, 'max_len' =>255, 'default' => ''), 'AutomaticFilename' => Array ( - 'type' => 'int', - 'formatter' => 'kOptionsFormatter', + 'type' => 'int', + 'formatter' => 'kOptionsFormatter', 'options' => Array (0 => 'la_No', 1 => 'la_Yes'), - 'use_phrases' => 1, 'not_null' => 1, 'default' => 1, + 'use_phrases' => 1, 'not_null' => 1, 'default' => 1, ), 'SKU' => Array ('type' => 'string', 'required' => 1, 'max_len' =>255, 'error_msgs' => Array ('required' => 'Please fill in'), 'default' => NULL), 'Description' => Array ('type' => 'string', 'formatter' => 'kMultiLanguage', 'using_fck' => 1, 'default' => NULL), 'DescriptionExcerpt' => Array ('type' => 'string', 'formatter' => 'kMultiLanguage', 'using_fck' => 1, 'default' => NULL), 'Weight' => Array ('type' => 'float', 'min_value_exc' => 0, 'formatter' => 'kUnitFormatter', 'format' => '%0.2f', 'default' => NULL), 'MSRP' => Array ('type' => 'float', 'min_value_inc' => 0, 'formatter' => 'kFormatter', 'format' => '%0.2f', 'default' => NULL), 'ManufacturerId' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' =>Array (0 => ''), 'options_sql' => 'SELECT %s FROM '.TABLE_PREFIX.'Manufacturers ORDER BY Name', 'option_key_field' => 'ManufacturerId', 'option_title_field' => 'Name', 'not_null' => 1, 'default' => 0), 'Status' => Array ( 'type' => 'int', - 'formatter' => 'kOptionsFormatter', + 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Active', 2 => 'la_Pending', 0 => 'la_Disabled'), 'use_phrases' => 1, 'default' => 2, 'not_null' => 1, ), 'BackOrder' => Array ('type' => 'int', 'not_null' => 1, 'options' => Array ( 2 => 'la_Auto', 1 => 'la_Always', 0 => 'la_Never' ), 'use_phrases' => 1, 'default' => 2 ), 'BackOrderDate' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'error_msgs' => Array ('bad_date_format' => 'Please use the following date format: %s'), 'default' => NULL), 'NewItem' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array ( 2 => 'la_Auto', 1 => 'la_Always', 0 => 'la_Never' ), 'use_phrases' => 1, 'not_null' => 1, 'default' => 2 ), 'HotItem' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array ( 2 => 'la_Auto', 1 => 'la_Always', 0 => 'la_Never' ), 'use_phrases' => 1, 'not_null' => 1, 'default' => 2 ), 'PopItem' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array ( 2 => 'la_Auto', 1 => 'la_Always', 0 => 'la_Never' ), 'use_phrases' => 1, 'not_null' => 1, 'default' => 2 ), 'EditorsPick' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0, ), 'Featured' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0, ), 'OnSale' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0, ), 'Priority' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'CachedRating' => Array ('not_null' => 1, 'default' => 0, 'type' => 'float', 'formatter' => 'kFormatter', 'format' => '%0.2f'), 'CachedVotesQty' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'Hits' => Array ('type' => 'double', 'formatter' => 'kFormatter', 'format' => '%d', 'not_null' => 1, 'default' => 0), 'CreatedOn' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => '#NOW#'), 'Expire' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' =>null), 'Type' => Array ('type' => 'int', 'not_null' => 1, 'formatter' => 'kOptionsFormatter', 'use_phrases' => 1, 'options' =>Array (1=> 'la_product_tangible', 2=> 'la_product_subscription', 4=> 'la_product_downloadable', 3=> 'la_product_service'/*, 6=> 'la_gift_certificate', 5=> 'la_product_package'*/), 'not_null' => 1, 'default' => 1 ), 'Modified' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => '#NOW#'), 'ModifiedById' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'CreatedById' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'ResourceId' => Array ('type' => 'int', 'default' => 0), 'CachedReviewsQty' => Array ('type' => 'int', 'formatter' => 'kFormatter', 'format' => '%d', 'not_null' => 1, 'default' => 0), 'InventoryStatus' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (0 => 'la_Disabled', 1 => 'la_by_product', 2 => 'la_by_options'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0), 'QtyInStock' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'QtyInStockMin' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'QtyReserved' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'QtyBackOrdered' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'QtyOnOrder' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'InventoryComment' => Array ('type' => 'string', 'formatter' => 'kFormatter', 'using_fck' => 1, 'default' => null), 'Qty' => Array ('type' => 'int', 'formatter' => 'kFormatter', 'regexp' => '/^[\d]+$/', 'error_msgs' => Array ('invalid_format' => '!la_invalid_integer!')), 'AccessGroupId' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options_sql' => 'SELECT %s FROM '.TABLE_PREFIX.'PortalGroup WHERE System!=1 AND Personal !=1 ORDER BY Name', 'option_key_field' => 'GroupId', 'option_title_field' => 'Name', 'default' => NULL), 'AccessDuration' => Array ('type' => 'int', 'default' => NULL), 'AccessDurationType' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'use_phrases' => 1, 'options' =>Array (1 => 'la_opt_sec', 2 => 'la_opt_min', 3 => 'la_opt_hour', 4 => 'la_opt_day', 5 => 'la_opt_week', 6 => 'la_opt_month', 7 => 'la_opt_year' ), 'default' => NULL,), 'AccessStart' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => NULL), 'AccessEnd' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => NULL,), 'OptionsSelectionMode' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'use_phrases' => 1, 'options' =>Array (0 => 'la_opt_Selection', 1 => 'la_opt_List'), 'default' => 0), 'HasRequiredOptions' => Array ('type' => 'int', 'default' => 0, 'not_null' => 1), 'Virtual' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'ProcessingData' => Array ('type' => 'string', 'default' => ''), 'PackageContent' => Array ('type' => 'string', 'default' => NULL), 'IsRecurringBilling' => Array ( - 'type' => 'int', - 'formatter' => 'kOptionsFormatter', + 'type' => 'int', + 'formatter' => 'kOptionsFormatter', 'options' => Array (0 => 'la_No', 1 => 'la_Yes'), - 'use_phrases' => 1, 'not_null' => 1, 'default' => 0, + 'use_phrases' => 1, 'not_null' => 1, 'default' => 0, ), //'PayPalRecurring' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1, 'not_null' => '1', 'default' => '0'), 'ShippingMode' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'use_phrases' => 1, 'options' =>Array (0 => 'la_shipping_AnyAndSelected', 1 => 'la_shipping_Limited'), 'not_null' => 1, 'default' =>0), 'ProcessingData' => Array ('type' => 'string', 'default' => null), 'ShippingLimitation' => Array ('type' => 'string', 'default' => NULL), 'AssignedCoupon' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0, 'formatter' => 'kLEFTFormatter', 'options' => Array (0 => 'None'), 'left_sql' => 'SELECT %s FROM '.TABLE_PREFIX.'ProductsCoupons WHERE `%s` = \'%s\'', 'left_key_field' => 'CouponId', 'left_title_field' => 'Name'), 'MinQtyFreePromoShipping' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'MetaKeywords' => Array ('type' => 'string', 'default' => null), 'MetaDescription' => Array ('type' => 'string', 'formatter' => 'kFormatter', 'using_fck' => 1, 'default' => null), ), - + 'VirtualFields' => Array ( 'Qty' => 1, 'Price' => Array ('type' => 'float', 'formatter' => 'kFormatter', 'format' => '%.2f', 'default' => NULL), 'Cost' => Array ('type' => 'float', 'formatter' => 'kFormatter', 'format' => '%.2f', 'default' => NULL), 'IsHot' => Array ('type' => 'int'), 'IsNew' => Array ('type' => 'int'), 'IsPop' => Array ('type' => 'int'), 'Manufacturer' => Array (), // export related fields: begin 'CategoryId' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (), 'default' => 0), 'ExportFormat' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'CSV', /*2 => 'XML'*/), 'default' => 1), 'ExportFilename' => Array ('type' => 'string', 'default' => ''), 'FieldsSeparatedBy' => Array ('type' => 'string', 'default' => ', '), 'FieldsEnclosedBy' => Array ('type' => 'string', 'default' => '"'), 'LineEndings' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'Windows', 2 => 'UNIX'), 'default' => 1), 'LineEndingsInside' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'CRLF', 2 => 'LF'), 'default' => 2), 'IncludeFieldTitles' => Array ( - 'type' => 'int', - 'formatter' => 'kOptionsFormatter', + 'type' => 'int', + 'formatter' => 'kOptionsFormatter', 'options' => Array (0 => 'la_No', 1 => 'la_Yes'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 1, ), 'ExportColumns' => Array ('type' => 'string', 'formatter' => 'kOptionsFormatter', 'options' => Array ()), 'AvailableColumns' => Array ('type' => 'string', 'formatter' => 'kOptionsFormatter', 'options' => Array ()), 'CategoryFormat' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_MixedCategoryPath', 2 => 'la_SeparatedCategoryPath'), 'use_phrases' => 1, 'default' => 1), 'CategorySeparator' => Array ('type' => 'string', 'default' => ':'), 'IsBaseCategory' => Array ( - 'type' => 'int', - 'formatter' => 'kOptionsFormatter', + 'type' => 'int', + 'formatter' => 'kOptionsFormatter', 'options' => Array (0 => 'la_No', 1 => 'la_Yes'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0, ), // export related fields: end // import related fields: begin 'FieldTitles' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Automatic', 2 => 'la_Manual'), 'use_phrases' => 1, 'default' => 1), 'ImportSource' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Upload', 2 => 'la_Local'), 'use_phrases' => 1, 'default' => 2), 'ImportFilename' => Array ('type' => 'string', 'formatter' => 'kUploadFormatter', 'max_size' => MAX_UPLOAD_SIZE, 'upload_dir' => (defined('EXPORT_BASE_PATH') ? EXPORT_BASE_PATH : '/admin/export') . '/'), 'ImportLocalFilename' => Array ('type' => 'string', 'formatter' => 'kOptionsFormatter', 'default' => ''), 'CheckDuplicatesMethod' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_IDField', 2 => 'la_OtherFields'), 'use_phrases' => 1, 'default' => 1), 'ReplaceDuplicates' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (0 => 'la_No', 1 => 'la_Yes'), 'use_phrases' => 1, 'default' => 0), 'DuplicateCheckFields' => Array ('type' => 'string', 'formatter' => 'kOptionsFormatter', 'options' => Array ('Name' => 'NAME'), 'default' => '|Name|'), 'SkipFirstRow' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1, 'default' => 1), // import related fields: end 'ThumbnailImage' => Array ('type' => 'string', 'default' => ''), 'FullImage' => Array ('type' => 'string', 'default' => ''), 'ImageAlt' => Array ('type' => 'string', 'default' => ''), 'Filename' => Array ('type' => 'string', 'not_null' => '1', 'default' => ''), 'CachedNavbar' => Array ('type' => 'string', 'default' => ''), 'ParentPath' => Array ('type' => 'string', 'default' => ''), 'FileSize' => Array ('type' => 'int', 'formatter' => 'kFilesizeFormatter', 'not_null' => 1, 'default' => 0), 'FilePath' => Array (), 'FileVersion' => Array (), // for primary image 'SameImages' => Array ('type' => 'string', 'default' => ''), 'LocalThumb' => Array ('type' => 'string', 'default' => ''), 'ThumbPath' => Array ('type' => 'string', 'default' => ''), 'ThumbUrl' => Array ('type' => 'string', 'default' => ''), 'LocalImage' => Array ('type' => 'string', 'default' => ''), 'LocalPath' => Array ('type' => 'string', 'default' => ''), 'FullUrl' => Array ('type' => 'string', 'default' => ''), ), - + 'Grids' => Array ( 'Default' => Array ( 'Icons' => Array ( 'default' => 'icon16_product.png', 0 => 'icon16_product_disabled.png', 1 => 'icon16_product.png', 2 => 'icon16_product_pending.png', 'NEW' => 'icon16_product_new.png', ), 'Fields' => Array ( 'ProductId' => Array ( 'title' => 'la_col_Id', 'data_block' => 'grid_checkbox_td', 'filter_block' => 'grid_range_filter', 'width' => 60, ), 'SKU' => Array ( 'title' => 'la_col_ProductSKU', 'filter_block' => 'grid_like_filter', 'width' => 100, ), 'Name' => Array ( 'title' => 'la_col_ProductName', 'data_block' => 'grid_catitem_td', 'filter_block' => 'grid_like_filter', 'width' => 150, ), 'Type' => Array ('title' => 'la_col_ProductType', 'filter_block' => 'grid_options_filter', 'width' => 80, ), 'Manufacturer' => Array ('title' => 'la_col_Manufacturer', 'filter_block' => 'grid_like_filter', 'width' => 100, ), 'Price' => Array ('title' => 'la_col_Price', 'filter_block' => 'grid_range_filter', 'width' => 70, ), 'Status' => Array ('title' => 'la_col_Status', 'filter_block' => 'grid_options_filter', 'width' => 70, ), 'QtyInStock' => Array ('title' => 'la_col_Qty', 'data_block' => 'qty_td', 'filter_block' => 'grid_range_filter', 'width' => 80, ), 'QtyBackOrdered' => Array ('title' => 'la_col_QtyBackOrdered', 'filter_block' => 'grid_range_filter', 'width' => 80, ), 'OnSale' => Array ('title' => 'la_col_OnSale', 'filter_block' => 'grid_options_filter', 'width' => 70, ), /* 'Weight' => Array ( 'title' => 'la_col_ProductWeight', 'filter_block' => 'grid_float_range_filter', 'width' => 150, ), 'CreatedOn' => Array ( 'title' => 'la_col_ProductCreatedOn', 'filter_block' => 'grid_date_range_filter', 'width' => 150, ), 'BackOrderDate' => Array ( 'title' => 'la_col_ProductBackOrderDate', 'filter_block' => 'grid_date_range_filter', 'width' => 150, ), */ ), ), 'Radio' => Array ( 'Icons' => Array ( 'default' => 'icon16_product.png', 0 => 'icon16_product_disabled.png', 1 => 'icon16_product.png', 2 => 'icon16_product_pending.png', 'NEW' => 'icon16_product_new.png', ), 'Selector' => 'radio', 'Fields' => Array ( 'ProductId' => Array ( 'title' => 'la_col_Id', 'data_block' => 'grid_radio_td', 'filter_block' => 'grid_range_filter', 'width' => 60, ), 'SKU' => Array ( 'title' => 'la_col_ProductSKU', 'filter_block' => 'grid_like_filter', 'width' => 100, ), 'Name' => Array ( 'title' => 'la_col_ProductName', 'data_block' => 'grid_catitem_td', 'filter_block' => 'grid_like_filter', 'width' => 150, ), 'Type' => Array ('title' => 'la_col_ProductType', 'filter_block' => 'grid_options_filter', 'width' => 80, ), 'Manufacturer' => Array ('title' => 'la_col_Manufacturer', 'filter_block' => 'grid_like_filter', 'width' => 100, ), 'Price' => Array ('title' => 'la_col_Price', 'filter_block' => 'grid_range_filter', 'width' => 70, ), 'Status' => Array ('title' => 'la_col_Status', 'filter_block' => 'grid_options_filter', 'width' => 70, ), 'QtyInStock' => Array ('title' => 'la_col_Qty', 'data_block' => 'qty_td', 'filter_block' => 'grid_range_filter', 'width' => 80, ), 'QtyBackOrdered' => Array ('title' => 'la_col_QtyBackOrdered', 'filter_block' => 'grid_range_filter', 'width' => 80, ), ), ), ), 'ConfigMapping' => Array ( 'PerPage' => 'Comm_Perpage_Products', 'ShortListPerPage' => 'Comm_Perpage_Products_Short', 'ForceEditorPick' => 'products_EditorPicksAboveRegular', 'DefaultSorting1Field' => 'product_OrderProductsBy', 'DefaultSorting2Field' => 'product_OrderProductsThenBy', 'DefaultSorting1Dir' => 'product_OrderProductsByDir', 'DefaultSorting2Dir' => 'product_OrderProductsThenByDir', 'RatingDelayValue' => 'product_RatingDelay_Value', 'RatingDelayInterval' => 'product_RatingDelay_Interval', ), );