From 07ac99f5de1bc48cb42e755353ecc7eb5e76759e Mon Sep 17 00:00:00 2001 From: vChabot Date: Tue, 20 Dec 2011 17:36:08 +0000 Subject: [PATCH] [*] BO : New category management with multi shop --- .../themes/template/helper/form/form.tpl | 2 + admin-dev/themes/template/products/form.tpl | 11 +- classes/Category.php | 18 ++++ classes/Search.php | 4 + classes/helper/Helper.php | 2 +- classes/shop/Shop.php | 100 ++++++++++++++++++ .../admin/AdminCategoriesController.php | 6 +- controllers/admin/AdminProductsController.php | 19 +++- controllers/admin/AdminShopController.php | 71 ++++++++++++- controllers/front/CategoryController.php | 5 + install-dev/sql/db.sql | 7 ++ install-dev/sql/db_settings_extends.sql | 4 + install-dev/sql/db_settings_lite.sql | 3 + modules/blockcategories/blockcategories.php | 22 ++-- 14 files changed, 260 insertions(+), 14 deletions(-) diff --git a/admin-dev/themes/template/helper/form/form.tpl b/admin-dev/themes/template/helper/form/form.tpl index 7e5bbe2a9..e0de94288 100644 --- a/admin-dev/themes/template/helper/form/form.tpl +++ b/admin-dev/themes/template/helper/form/form.tpl @@ -261,6 +261,8 @@ {include file='helper/assoshop.tpl' input=$input fields_value=$fields_value} {elseif $input.type == 'categories'} {include file='helper/form/form_category.tpl' categories=$input.values} + {elseif $input.type == 'categories_select'} + {$input.category_tree} {elseif $input.type == 'asso_shop' && isset($asso_shop) && $asso_shop} {$asso_shop} {elseif $input.type == 'color'} diff --git a/admin-dev/themes/template/products/form.tpl b/admin-dev/themes/template/products/form.tpl index 795d625da..b85493033 100644 --- a/admin-dev/themes/template/products/form.tpl +++ b/admin-dev/themes/template/products/form.tpl @@ -206,7 +206,16 @@

- + + {/if} + {if isset($warning_unavailable_product)} +
+

+ + {l s='This product is active in this shop but it doesn\'t belong to any active category for this shop.'} + +

+
{/if} {* all input are here *} diff --git a/classes/Category.php b/classes/Category.php index 2943ddc9d..0ef2585bd 100644 --- a/classes/Category.php +++ b/classes/Category.php @@ -663,8 +663,10 @@ class CategoryCore extends ObjectModel SELECT c.`id_category`, cl.`name`, cl.`link_rewrite` FROM `'._DB_PREFIX_.'category` c LEFT JOIN `'._DB_PREFIX_.'category_lang` cl ON c.`id_category` = cl.`id_category`'.Context::getContext()->shop->addSqlRestrictionOnLang('cl').' + LEFT JOIN `'._DB_PREFIX_.'category_shop` cs ON c.`id_category` = cs.`id_category` WHERE `id_lang` = '.(int)$id_lang.' AND c.`id_parent` = '.(int)$id_parent.' + AND cs.`id_shop` = '.(int)Context::getContext()->shop->getID(true).' '.($active ? 'AND `active` = 1' : '').' ORDER BY `position` ASC'); } @@ -713,8 +715,10 @@ class CategoryCore extends ObjectModel )' : '0').' AS nbSelectedSubCat FROM `'._DB_PREFIX_.'category` c LEFT JOIN `'._DB_PREFIX_.'category_lang` cl ON c.`id_category` = cl.`id_category`'.$shop->addSqlRestrictionOnLang('cl').' + LEFT JOIN `'._DB_PREFIX_.'category_shop` cs ON c.`id_category` = cs.`id_category` WHERE `id_lang` = '.(int)$id_lang.' AND c.`id_parent` = '.(int)$id_parent.' + AND cs.`id_shop` = '.(int)$shop->getID(true).' ORDER BY `position` ASC'; return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql); } @@ -883,7 +887,10 @@ class CategoryCore extends ObjectModel LEFT JOIN `'._DB_PREFIX_.'category_lang` cl ON (c.`id_category` = cl.`id_category` AND `id_lang` = '.(int)$id_lang.Context::getContext()->shop->addSqlRestrictionOnLang('cl').') + LEFT JOIN `'._DB_PREFIX_.'category_shop` cs + ON c.`id_category` = cs.`id_category` WHERE c.`id_category` = '.(int)$id_current.' + AND cs.`id_shop` = '.(int)Context::getContext()->shop->getID(true).' AND c.`id_parent` != 0 '); @@ -1197,5 +1204,16 @@ class CategoryCore extends ObjectModel return $categories; } + + public function isParentCategoryAvailable($id_shop) + { + return Db::getInstance()->getValue(' + SELECT c.`id_category` + FROM `'._DB_PREFIX_.'category` c + LEFT JOIN `'._DB_PREFIX_.'category_shop` cs + ON c.`id_category` = cs.`id_category` + WHERE cs.`id_shop` = '.(int)$id_shop.' + AND c.`id_parent` = '.(int)$this->id_parent); + } } diff --git a/classes/Search.php b/classes/Search.php index 83eee8dc2..ccd902044 100644 --- a/classes/Search.php +++ b/classes/Search.php @@ -595,7 +595,9 @@ class SearchCore LEFT JOIN `'._DB_PREFIX_.'tag` t ON (pt.`id_tag` = t.`id_tag` AND t.`id_lang` = '.(int)$id_lang.') LEFT JOIN `'._DB_PREFIX_.'category_product` cp ON (cp.`id_product` = p.`id_product`) LEFT JOIN `'._DB_PREFIX_.'category_group` cg ON (cg.`id_category` = cp.`id_category`) + LEFT JOIN `'._DB_PREFIX_.'category_shop` cs ON (cg.`id_category` = cs.`id_category`) WHERE p.`active` = 1 + AND cs.`id_shop` = '.(int)Context::getContext()->shop->getID().' AND cg.`id_group` '.(!$id_customer ? '= 1' : 'IN ( SELECT id_group FROM '._DB_PREFIX_.'customer_group WHERE id_customer = '.(int)$id_customer.')').' @@ -629,8 +631,10 @@ class SearchCore LEFT JOIN `'._DB_PREFIX_.'tag` t ON (pt.`id_tag` = t.`id_tag` AND t.`id_lang` = '.(int)$id_lang.') LEFT JOIN `'._DB_PREFIX_.'category_product` cp ON (cp.`id_product` = p.`id_product`) LEFT JOIN `'._DB_PREFIX_.'category_group` cg ON (cg.`id_category` = cp.`id_category`) + LEFT JOIN `'._DB_PREFIX_.'category_shop` cs ON (cg.`id_category` = cs.`id_category`) '.Product::sqlStock('p', 0).' WHERE p.`active` = 1 + AND cs.`id_shop` = '.(int)Context::getContext()->shop->getID().' AND cg.`id_group` '.(!$id_customer ? '= 1' : 'IN ( SELECT id_group FROM '._DB_PREFIX_.'customer_group WHERE id_customer = '.(int)$id_customer.')').' diff --git a/classes/helper/Helper.php b/classes/helper/Helper.php index df2c41fea..83382785c 100755 --- a/classes/helper/Helper.php +++ b/classes/helper/Helper.php @@ -172,7 +172,7 @@ class HelperCore '.$trads['Check All'].' | '.$trads['Uncheck All'].'| - ' : '').($use_search ? '
'.$trads['search'].' :
' : '').' + ' : '').($use_search ? ''.$trads['search'].' : ' : '').' '; diff --git a/classes/shop/Shop.php b/classes/shop/Shop.php index 8dcd80cf0..2fcde7d3e 100644 --- a/classes/shop/Shop.php +++ b/classes/shop/Shop.php @@ -889,4 +889,104 @@ class ShopCore extends ObjectModel $without[] = $shop; return $without; } + + public static function getCategories($id = 0) + { + // build query + $query = new DbQuery(); + $query->select('id_category'); + $query->from('category_shop'); + $query->where('id_shop = '.(int)$id + ); + $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($query); + + $array = array(); + foreach ($result as $row) + $array[] = $row['id_category']; + + return $array; + } + + /** + * Update categories for a shop + * + * @param string $productCategories Categories list to associate a shop + * @return array Update/insertion result + */ + public function updateCategories($categories) + { + // if array is empty or if the default category is not selected, return false + if (empty($categories) || !in_array($this->id_category, $categories)) + return false; + + // delete categories for this shop + $this->deleteCategories(); + + // and add $categories to this shop + return $this->addToCategories($categories); + } + + /** + * Delete shop from category $id_category + * @param int $id_category + * @return bool + */ + public function deleteCategory($id_category) + { + return Db::getInstance()->execute( + 'DELETE FROM `'._DB_PREFIX_.'category_shop` + WHERE `id_shop` = '.(int)$this->id.' + AND id_category = '.(int)$id_category.'' + ); + } + + /** + * Delete every categories + * @return bool + */ + public function deleteCategories() + { + return Db::getInstance()->execute(' + DELETE FROM `'._DB_PREFIX_.'category_shop` WHERE `id_shop` = '.(int)$this->id.' + '); + } + + /** + * Add some categories to a shop + * @param array $categories + * @return bool + */ + public function addToCategories($categories) + { + $sql = ' + INSERT INTO `'._DB_PREFIX_.'category_shop` (`id_category`, `id_shop`) VALUES'; + foreach ($categories as $c) + $sql .= '("'.(int)$c.'", "'.(int)$this->id.'"),'; + // removing last comma to avoid SQL error + $sql = substr($sql, 0, strlen($sql) - 1); + + return Db::getInstance()->execute($sql); + } + + public static function isCategoryAvailable($id_category) + { + return (bool)Db::getInstance()->getValue(' + SELECT `id_category` + FROM `'._DB_PREFIX_.'category_shop` + WHERE `id_category` = '.(int)$id_category.' + AND `id_shop` = '.(int)Context::getContext()->shop->getID(true)); + } + + public static function isProductAvailable($id_product) + { + return (bool)Db::getInstance()->getValue(' + SELECT p.`id_product` + FROM `'._DB_PREFIX_.'product` p + LEFT JOIN `'._DB_PREFIX_.'category_product` cp + ON p.`id_product` = cp.`id_product` + LEFT JOIN `'._DB_PREFIX_.'category_shop` cs + ON cp.`id_category` = cs.`id_category` + WHERE p.`id_product` = '.(int)$id_product.' + AND cs.`id_shop` = '.(int)Context::getContext()->shop->getID(true)); + } } diff --git a/controllers/admin/AdminCategoriesController.php b/controllers/admin/AdminCategoriesController.php index ce7279d00..c1208c5b3 100644 --- a/controllers/admin/AdminCategoriesController.php +++ b/controllers/admin/AdminCategoriesController.php @@ -118,6 +118,9 @@ class AdminCategoriesControllerCore extends AdminController public function getList($id_lang, $order_by = null, $order_way = null, $start = 0, $limit = null, $id_lang_shop = false) { + // we add restriction for shop + $this->_join = 'LEFT JOIN `'._DB_PREFIX_.'category_shop` cs ON a.`id_category` = cs.`id_category`'; + $this->_where = ' AND cs.`id_shop` = '.(int)Context::getContext()->shop->getID(true); parent::getList($id_lang, 'position', $order_way, $start, $limit, Context::getContext()->shop->getID(true)); // Check each row to see if there are combinations and get the correct action in consequence @@ -170,7 +173,8 @@ class AdminCategoriesControllerCore extends AdminController { $this->initToolbar(); $obj = $this->loadObject(true); - $selected_cat = array(isset($obj->id_parent) ? $obj->id_parent : Tools::getValue('id_parent', 1)); + $id_shop = Context::getContext()->shop->getID(true); + $selected_cat = array((isset($obj->id_parent) && $obj->isParentCategoryAvailable($id_shop))? $obj->id_parent : Tools::getValue('id_parent', 1)); $this->fields_form = array( 'tinymce' => true, diff --git a/controllers/admin/AdminProductsController.php b/controllers/admin/AdminProductsController.php index 4981da5a8..76037622d 100644 --- a/controllers/admin/AdminProductsController.php +++ b/controllers/admin/AdminProductsController.php @@ -2101,6 +2101,8 @@ class AdminProductsControllerCore extends AdminController $this->_errors[] = 'Unable to load object'; else { + if (!Shop::isProductAvailable($this->object->id)) + $this->_displayUnavailableProductWarning(); $this->_displayDraftWarning($this->object->active); $this->{'initForm'.$this->tab_display}($this->object, $languages, $default_language); $this->tpl_form_vars['product'] = $this->object; @@ -3836,7 +3838,7 @@ class AdminProductsControllerCore extends AdminController Pack::deleteItems($product->id); // lines format: QTY x ID-QTY x ID - if (Tools::getValue('type_product') == 1) + if (Tools::getValue('ppack')) { $items = Tools::getValue('inputPackItems'); $lines = array_unique(explode('-', $items)); @@ -3906,4 +3908,19 @@ class AdminProductsControllerCore extends AdminController $this->addCSS(_PS_JS_DIR_.'jquery/plugins/treeview/jquery.treeview.css'); } } + + protected function _displayUnavailableProductWarning() + { + $content = '
+

+ + '.$this->l('Your product will be saved as draft').' + + '.$this->l('Save and preview').' + +
+

+
'; + $this->tpl_form_vars['warning_unavailable_product'] = $content; + } } diff --git a/controllers/admin/AdminShopController.php b/controllers/admin/AdminShopController.php index e1d1db38b..b9e234614 100755 --- a/controllers/admin/AdminShopController.php +++ b/controllers/admin/AdminShopController.php @@ -213,6 +213,12 @@ class AdminShopControllerCore extends AdminController ) ); } + $this->fields_form['input'][] = array( + 'type' => 'categories_select', + 'name' => 'categoryBox', + 'label' => $this->l('Associated categories :'), + 'category_tree' => $this->initCategoriesAssociation($this) + ); $categories = Category::getCategories($this->context->language->id, false, false); $this->fields_form['input'][] = array( @@ -335,10 +341,73 @@ class AdminShopControllerCore extends AdminController 'checked' => (Tools::getValue('addshop') !== false) ? true : false, 'defaultShop' => (int)Configuration::get('PS_SHOP_DEFAULT'), ); - if (isset($this->fields_import_form)) $this->tpl_form_vars = array_merge($this->tpl_form_vars, array('form_import' => $this->fields_import_form)); return parent::renderForm(); } + + public function initCategoriesAssociation() + { + $selected_cat = Shop::getCategories(Tools::getValue('id_shop')); + + $translations = array( + 'Home' => $this->l('Home'), + 'selected' => $this->l('selected'), + 'Collapse All' => $this->l('Collapse All'), + 'Expand All' => $this->l('Expand All'), + 'Check All' => $this->l('Check All'), + 'Uncheck All' => $this->l('Uncheck All'), + 'search' => $this->l('Search a category') + ); + + return Helper::renderAdminCategorieTree($translations, $selected_cat, 'categoryBox', false, true); + } + + /** + * Object creation + * + * @param string $token + */ + public function processAdd($token) + { + /* Checking fields validity */ + $this->validateRules(); + + if (!count($this->_errors)) + { + $object = new $this->className(); + $this->copyFromPost($object, $this->table); + $this->beforeAdd($object); + if (!$object->add()) + { + $this->_errors[] = Tools::displayError('An error occurred while creating object.'). + ' '.$this->table.' ('.Db::getInstance()->getMsgError().')'; + } + /* voluntary do affectation here */ + else if (($_POST[$this->identifier] = $object->id) && $this->postImage($object->id) && !count($this->_errors) && $this->_redirect) + { + $parent_id = (int)Tools::getValue('id_parent', 1); + $this->afterAdd($object); + $this->updateAssoShop($object->id); + // Save and stay on same form + if (Tools::isSubmit('submitAdd'.$this->table.'AndStay')) + $this->redirect_after = self::$currentIndex.'&'.$this->identifier.'='.$object->id.'&conf=3&update'.$this->table.'&token='.$token; + // Save and back to parent + if (Tools::isSubmit('submitAdd'.$this->table.'AndBackToParent')) + $this->redirect_after = self::$currentIndex.'&'.$this->identifier.'='.$parent_id.'&conf=3&token='.$token; + // Default behavior (save and back) + if (empty($this->redirect_after)) + $this->redirect_after = self::$currentIndex.($parent_id ? '&'.$this->identifier.'='.$object->id : '').'&conf=3&token='.$token; + } + } + + $this->_errors = array_unique($this->_errors); + if (count($this->_errors) > 0) + return; + + $shop = new Shop($object->id); + $shop->updateCategories(Tools::getValue('categoryBox')); + return $object; + } } diff --git a/controllers/front/CategoryController.php b/controllers/front/CategoryController.php index c88f79e61..4f069e080 100644 --- a/controllers/front/CategoryController.php +++ b/controllers/front/CategoryController.php @@ -49,6 +49,11 @@ class CategoryControllerCore extends FrontController public function canonicalRedirection($canonicalURL = '') { + if (!Shop::isCategoryAvailable(Tools::getValue('id_category'))) + { + $this->redirect_after = '404'; + $this->redirect(); + } if (!Tools::getValue('noredirect') && Validate::isLoadedObject($this->category)) parent::canonicalRedirection($this->context->link->getCategoryLink($this->category)); } diff --git a/install-dev/sql/db.sql b/install-dev/sql/db.sql index dcec54073..ca6d5190b 100644 --- a/install-dev/sql/db.sql +++ b/install-dev/sql/db.sql @@ -2334,3 +2334,10 @@ CREATE TABLE `PREFIX_specific_price_rule_condition` ( PRIMARY KEY (`id_specific_price_rule_condition`), INDEX (`id_specific_price_rule_condition_group`) ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8; + +CREATE TABLE `PREFIX_category_shop` ( + `id_category` int(11) NOT NULL, + `id_shop` int(11) NOT NULL, + PRIMARY KEY (`id_category`, `id_shop`), + UNIQUE KEY `id_category_shop` (`id_category`,`id_shop`) +) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/install-dev/sql/db_settings_extends.sql b/install-dev/sql/db_settings_extends.sql index 4a97612c5..33e5a6acf 100644 --- a/install-dev/sql/db_settings_extends.sql +++ b/install-dev/sql/db_settings_extends.sql @@ -1719,3 +1719,7 @@ INSERT INTO `PREFIX_homeslider_slides_lang` (id_slide, id_lang, title, descripti (4, 5, "PHP.net", "PrestaShop use PHP, the well-known open-source technology", "php", "http://www.php.net", "sample-4.jpg"), (5, 5, "Smarty.net", "PrestaShop use the template engine Smarty (V3)", "Smarty", "http://www.smarty.net", "sample-5.jpg"); +INSERT INTO `PREFIX_category_shop` (`id_category`, `id_shop`) VALUES +(2, 1), +(3, 1), +(4, 1); \ No newline at end of file diff --git a/install-dev/sql/db_settings_lite.sql b/install-dev/sql/db_settings_lite.sql index 81fea7129..4d29789e2 100644 --- a/install-dev/sql/db_settings_lite.sql +++ b/install-dev/sql/db_settings_lite.sql @@ -1605,3 +1605,6 @@ INSERT INTO `PREFIX_supply_order_state_lang` (`id_supply_order_state`, `id_lang` (6, 3, 'order fenced'), (6, 4, 'order fenced'), (6, 5, 'order fenced'); + +INSERT INTO `PREFIX_category_shop` (`id_category`, `id_shop`) VALUES +(1, 1); \ No newline at end of file diff --git a/modules/blockcategories/blockcategories.php b/modules/blockcategories/blockcategories.php index 7b7a57488..fc89441ea 100644 --- a/modules/blockcategories/blockcategories.php +++ b/modules/blockcategories/blockcategories.php @@ -172,23 +172,27 @@ class BlockCategories extends Module {*/ $maxdepth = Configuration::get('BLOCK_CATEG_MAX_DEPTH'); /*p(' - SELECT c.id_parent, c.id_category, cl.name, cl.description, cl.link_rewrite - FROM `'._DB_PREFIX_.'category` c - LEFT JOIN `'._DB_PREFIX_.'category_lang` cl ON (c.`id_category` = cl.`id_category` AND cl.`id_lang` = '.$id_lang.$this->context->shop->addSqlRestrictionOnLang('cl').') - LEFT JOIN `'._DB_PREFIX_.'category_group` cg ON (cg.`id_category` = c.`id_category`) - WHERE (c.`active` = 1 OR c.`id_category` = 1) - '.((int)($maxdepth) != 0 ? ' AND `level_depth` <= '.(int)($maxdepth) : '').' - AND cg.`id_group` IN ('.pSQL($groups).') - GROUP BY id_category - ORDER BY `level_depth` ASC, '.(Configuration::get('BLOCK_CATEG_SORT') ? 'cl.`name`' : 'c.`position`').' '.(Configuration::get('BLOCK_CATEG_SORT_WAY') ? 'DESC' : 'ASC'));*/ + SELECT c.id_parent, c.id_category, cl.name, cl.description, cl.link_rewrite + FROM `'._DB_PREFIX_.'category` c + LEFT JOIN `'._DB_PREFIX_.'category_lang` cl ON (c.`id_category` = cl.`id_category` AND cl.`id_lang` = '.$id_lang.$this->context->shop->addSqlRestrictionOnLang('cl').') + LEFT JOIN `'._DB_PREFIX_.'category_group` cg ON (cg.`id_category` = c.`id_category`) + LEFT JOIN `'._DB_PREFIX_.'category_shop` cs ON c.`id_category` = cs.`id_category` + WHERE (c.`active` = 1 OR c.`id_category` = 1) + '.((int)($maxdepth) != 0 ? ' AND `level_depth` <= '.(int)($maxdepth) : '').' + AND cg.`id_group` IN ('.pSQL($groups).') + AND cs.`id_shop` = '.(int)$id_current_shop.' + GROUP BY id_category + ORDER BY `level_depth` ASC, '.(Configuration::get('BLOCK_CATEG_SORT') ? 'cl.`name`' : 'c.`position`').' '.(Configuration::get('BLOCK_CATEG_SORT_WAY') ? 'DESC' : 'ASC'));*/ if (!$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' SELECT c.id_parent, c.id_category, cl.name, cl.description, cl.link_rewrite FROM `'._DB_PREFIX_.'category` c LEFT JOIN `'._DB_PREFIX_.'category_lang` cl ON (c.`id_category` = cl.`id_category` AND cl.`id_lang` = '.$id_lang.$this->context->shop->addSqlRestrictionOnLang('cl').') LEFT JOIN `'._DB_PREFIX_.'category_group` cg ON (cg.`id_category` = c.`id_category`) + LEFT JOIN `'._DB_PREFIX_.'category_shop` cs ON c.`id_category` = cs.`id_category` WHERE (c.`active` = 1 OR c.`id_category` = 1) '.((int)($maxdepth) != 0 ? ' AND `level_depth` <= '.(int)($maxdepth) : '').' AND cg.`id_group` IN ('.pSQL($groups).') + AND cs.`id_shop` = '.(int)$id_current_shop.' GROUP BY id_category ORDER BY `level_depth` ASC, '.(Configuration::get('BLOCK_CATEG_SORT') ? 'cl.`name`' : 'c.`position`').' '.(Configuration::get('BLOCK_CATEG_SORT_WAY') ? 'DESC' : 'ASC')) )