PageRenderTime 47ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/controllers/admin/AdminCategoriesController.php

https://github.com/netplayer/PrestaShop
PHP | 815 lines | 687 code | 83 blank | 45 comment | 153 complexity | 877f020db2b1b5751c151e5c280990e8 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-2.1, LGPL-3.0
  1. <?php
  2. /*
  3. * 2007-2014 PrestaShop
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@prestashop.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
  18. * versions in the future. If you wish to customize PrestaShop for your
  19. * needs please refer to http://www.prestashop.com for more information.
  20. *
  21. * @author PrestaShop SA <contact@prestashop.com>
  22. * @copyright 2007-2014 PrestaShop SA
  23. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  24. * International Registered Trademark & Property of PrestaShop SA
  25. */
  26. class AdminCategoriesControllerCore extends AdminController
  27. {
  28. /**
  29. * @var object Category() instance for navigation
  30. */
  31. protected $_category = null;
  32. protected $position_identifier = 'id_category_to_move';
  33. /** @var boolean does the product have to be removed during the delete process */
  34. public $remove_products = true;
  35. /** @var boolean does the product have to be disable during the delete process */
  36. public $disable_products = false;
  37. private $original_filter = '';
  38. public function __construct()
  39. {
  40. $this->bootstrap = true;
  41. $this->table = 'category';
  42. $this->className = 'Category';
  43. $this->lang = true;
  44. $this->deleted = false;
  45. $this->explicitSelect = true;
  46. $this->_defaultOrderBy = 'position';
  47. $this->allow_export = true;
  48. $this->context = Context::getContext();
  49. $this->fieldImageSettings = array(
  50. 'name' => 'image',
  51. 'dir' => 'c'
  52. );
  53. $this->fields_list = array(
  54. 'id_category' => array(
  55. 'title' => $this->l('ID'),
  56. 'align' => 'center',
  57. 'class' => 'fixed-width-xs'
  58. ),
  59. 'name' => array(
  60. 'title' => $this->l('Name')
  61. ),
  62. 'description' => array(
  63. 'title' => $this->l('Description'),
  64. 'callback' => 'getDescriptionClean',
  65. 'orderby' => false
  66. ),
  67. 'position' => array(
  68. 'title' => $this->l('Position'),
  69. 'filter_key' => 'sa!position',
  70. 'position' => 'position',
  71. 'align' => 'center'
  72. ),
  73. 'active' => array(
  74. 'title' => $this->l('Displayed'),
  75. 'active' => 'status',
  76. 'type' => 'bool',
  77. 'class' => 'fixed-width-xs',
  78. 'align' => 'center',
  79. 'orderby' => false
  80. )
  81. );
  82. $this->bulk_actions = array(
  83. 'delete' => array(
  84. 'text' => $this->l('Delete selected'),
  85. 'icon' => 'icon-trash',
  86. 'confirm' => $this->l('Delete selected items?')
  87. )
  88. );
  89. $this->specificConfirmDelete = false;
  90. parent::__construct();
  91. }
  92. public function init()
  93. {
  94. parent::init();
  95. // context->shop is set in the init() function, so we move the _category instanciation after that
  96. if (($id_category = Tools::getvalue('id_category')) && $this->action != 'select_delete')
  97. $this->_category = new Category($id_category);
  98. else
  99. {
  100. if (Shop::isFeatureActive() && Shop::getContext() == Shop::CONTEXT_SHOP)
  101. $this->_category = new Category($this->context->shop->id_category);
  102. elseif (count(Category::getCategoriesWithoutParent()) > 1 && Configuration::get('PS_MULTISHOP_FEATURE_ACTIVE') && count(Shop::getShops(true, null, true)) != 1)
  103. $this->_category = Category::getTopCategory();
  104. else
  105. $this->_category = new Category(Configuration::get('PS_HOME_CATEGORY'));
  106. }
  107. $count_categories_without_parent = count(Category::getCategoriesWithoutParent());
  108. $top_category = Category::getTopCategory();
  109. if (Tools::isSubmit('id_category'))
  110. $id_parent = $this->_category->id;
  111. elseif (!Shop::isFeatureActive() && $count_categories_without_parent > 1)
  112. $id_parent = $top_category->id;
  113. elseif (Shop::isFeatureActive() && $count_categories_without_parent == 1)
  114. $id_parent = Configuration::get('PS_HOME_CATEGORY');
  115. elseif (Shop::isFeatureActive() && $count_categories_without_parent > 1 && Shop::getContext() != Shop::CONTEXT_SHOP)
  116. {
  117. if (Configuration::get('PS_MULTISHOP_FEATURE_ACTIVE') && count(Shop::getShops(true, null, true)) == 1)
  118. $id_parent = $this->context->shop->id_category;
  119. else
  120. $id_parent = $top_category->id;
  121. }
  122. else
  123. $id_parent = $this->context->shop->id_category;
  124. $this->_select = 'sa.position position';
  125. $this->original_filter = $this->_filter .= ' AND `id_parent` = '.(int)$id_parent.' ';
  126. if (Shop::getContext() == Shop::CONTEXT_SHOP)
  127. $this->_join .= ' LEFT JOIN `'._DB_PREFIX_.'category_shop` sa ON (a.`id_category` = sa.`id_category` AND sa.id_shop = '.(int)$this->context->shop->id.') ';
  128. else
  129. $this->_join .= ' LEFT JOIN `'._DB_PREFIX_.'category_shop` sa ON (a.`id_category` = sa.`id_category` AND sa.id_shop = a.id_shop_default) ';
  130. // we add restriction for shop
  131. if (Shop::getContext() == Shop::CONTEXT_SHOP && Shop::isFeatureActive())
  132. $this->_where = ' AND sa.`id_shop` = '.(int)Context::getContext()->shop->id;
  133. // if we are not in a shop context, we remove the position column
  134. if (Shop::isFeatureActive() && Shop::getContext() != Shop::CONTEXT_SHOP)
  135. unset($this->fields_list['position']);
  136. // shop restriction : if category is not available for current shop, we redirect to the list from default category
  137. if (Validate::isLoadedObject($this->_category) && !$this->_category->isAssociatedToShop() && Shop::getContext() == Shop::CONTEXT_SHOP)
  138. {
  139. $this->redirect_after = self::$currentIndex.'&id_category='.(int)$this->context->shop->getCategory().'&token='.$this->token;
  140. $this->redirect();
  141. }
  142. }
  143. public function initPageHeaderToolbar()
  144. {
  145. parent::initPageHeaderToolbar();
  146. if ($this->display != 'edit' && $this->display != 'add')
  147. {
  148. if (Configuration::get('PS_MULTISHOP_FEATURE_ACTIVE'))
  149. $this->page_header_toolbar_btn['new-url'] = array(
  150. 'href' => self::$currentIndex.'&add'.$this->table.'root&token='.$this->token,
  151. 'desc' => $this->l('Add new root category', null, null, false)
  152. );
  153. $this->page_header_toolbar_btn['new_category'] = array(
  154. 'href' => self::$currentIndex.'&addcategory&token='.$this->token,
  155. 'desc' => $this->l('Add new category', null, null, false),
  156. 'icon' => 'process-icon-new'
  157. );
  158. }
  159. }
  160. public function initContent()
  161. {
  162. if ($this->action == 'select_delete')
  163. $this->context->smarty->assign(array(
  164. 'delete_form' => true,
  165. 'url_delete' => htmlentities($_SERVER['REQUEST_URI']),
  166. 'boxes' => $this->boxes,
  167. ));
  168. parent::initContent();
  169. }
  170. public function setMedia()
  171. {
  172. parent::setMedia();
  173. $this->addJqueryUi('ui.widget');
  174. $this->addJqueryPlugin('tagify');
  175. }
  176. public function renderList()
  177. {
  178. if (isset($this->_filter) && trim($this->_filter) == '')
  179. $this->_filter = $this->original_filter;
  180. $this->addRowAction('view');
  181. $this->addRowAction('add');
  182. $this->addRowAction('edit');
  183. $this->addRowAction('delete');
  184. $count_categories_without_parent = count(Category::getCategoriesWithoutParent());
  185. $categories_tree = $this->_category->getParentsCategories();
  186. if (empty($categories_tree)
  187. && ($this->_category->id != 1 || Tools::isSubmit('id_category'))
  188. && (Shop::getContext() == Shop::CONTEXT_SHOP && !Shop::isFeatureActive() && $count_categories_without_parent > 1))
  189. $categories_tree = array(array('name' => $this->_category->name[$this->context->language->id]));
  190. $categories_tree = array_reverse($categories_tree);
  191. $this->tpl_list_vars['categories_tree'] = $categories_tree;
  192. $this->tpl_list_vars['categories_tree_current_id'] = $this->_category->id;
  193. if (Tools::isSubmit('submitBulkdelete'.$this->table) || Tools::isSubmit('delete'.$this->table))
  194. {
  195. $category = new Category(Tools::getValue('id_category'));
  196. if ($category->is_root_category)
  197. $this->tpl_list_vars['need_delete_mode'] = false;
  198. else
  199. $this->tpl_list_vars['need_delete_mode'] = true;
  200. $this->tpl_list_vars['delete_category'] = true;
  201. $this->tpl_list_vars['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
  202. $this->tpl_list_vars['POST'] = $_POST;
  203. }
  204. return parent::renderList();
  205. }
  206. public function getList($id_lang, $order_by = null, $order_way = null, $start = 0, $limit = null, $id_lang_shop = false)
  207. {
  208. parent::getList($id_lang, $order_by, $order_way, $start, $limit, Context::getContext()->shop->id);
  209. // Check each row to see if there are combinations and get the correct action in consequence
  210. $nb_items = count($this->_list);
  211. for ($i = 0; $i < $nb_items; $i++)
  212. {
  213. $item = &$this->_list[$i];
  214. $category_tree = Category::getChildren((int)$item['id_category'], $this->context->language->id);
  215. if (!count($category_tree))
  216. $this->addRowActionSkipList('view', array($item['id_category']));
  217. }
  218. }
  219. public function renderView()
  220. {
  221. $this->initToolbar();
  222. return $this->renderList();
  223. }
  224. public function initToolbar()
  225. {
  226. if (empty($this->display))
  227. {
  228. $this->toolbar_btn['new'] = array(
  229. 'href' => self::$currentIndex.'&amp;add'.$this->table.'&amp;token='.$this->token,
  230. 'desc' => $this->l('Add New')
  231. );
  232. $this->toolbar_btn['import'] = array(
  233. 'href' => $this->context->link->getAdminLink('AdminImport', true).'&import_type=categories',
  234. 'desc' => $this->l('Import')
  235. );
  236. }
  237. // be able to edit the Home category
  238. if (count(Category::getCategoriesWithoutParent()) == 1 && !Tools::isSubmit('id_category')
  239. && ($this->display == 'view' || empty($this->display)))
  240. $this->toolbar_btn['edit'] = array(
  241. 'href' => self::$currentIndex.'&amp;update'.$this->table.'&amp;id_category='.(int)$this->_category->id.'&amp;token='.$this->token,
  242. 'desc' => $this->l('Edit')
  243. );
  244. if (Tools::getValue('id_category') && !Tools::isSubmit('updatecategory'))
  245. {
  246. $this->toolbar_btn['edit'] = array(
  247. 'href' => self::$currentIndex.'&amp;update'.$this->table.'&amp;id_category='.(int)Tools::getValue('id_category').'&amp;token='.$this->token,
  248. 'desc' => $this->l('Edit')
  249. );
  250. }
  251. if ($this->display == 'view')
  252. $this->toolbar_btn['new'] = array(
  253. 'href' => self::$currentIndex.'&amp;add'.$this->table.'&amp;id_parent='.(int)Tools::getValue('id_category').'&amp;token='.$this->token,
  254. 'desc' => $this->l('Add New')
  255. );
  256. parent::initToolbar();
  257. if ($this->_category->id == Category::getTopCategory()->id && isset($this->toolbar_btn['new']))
  258. unset($this->toolbar_btn['new']);
  259. // after adding a category
  260. if (empty($this->display))
  261. {
  262. $id_category = (Tools::isSubmit('id_category')) ? '&amp;id_parent='.(int)Tools::getValue('id_category') : '';
  263. $this->toolbar_btn['new'] = array(
  264. 'href' => self::$currentIndex.'&amp;add'.$this->table.'&amp;token='.$this->token.$id_category,
  265. 'desc' => $this->l('Add New')
  266. );
  267. if (Tools::isSubmit('id_category'))
  268. {
  269. $back = Tools::safeOutput(Tools::getValue('back', ''));
  270. if (empty($back))
  271. $back = self::$currentIndex.'&token='.$this->token;
  272. $this->toolbar_btn['back'] = array(
  273. 'href' => $back,
  274. 'desc' => $this->l('Back to list')
  275. );
  276. }
  277. }
  278. }
  279. public function initProcess()
  280. {
  281. if (Tools::isSubmit('add'.$this->table.'root'))
  282. {
  283. if ($this->tabAccess['add'])
  284. {
  285. $this->action = 'add'.$this->table.'root';
  286. $obj = $this->loadObject(true);
  287. if (Validate::isLoadedObject($obj))
  288. $this->display = 'edit';
  289. else
  290. $this->display = 'add';
  291. }
  292. else
  293. $this->errors[] = Tools::displayError('You do not have permission to edit this.');
  294. }
  295. parent::initProcess();
  296. if ($this->action == 'delete' || $this->action == 'bulkdelete')
  297. if (Tools::getIsset('cancel'))
  298. Tools::redirectAdmin(self::$currentIndex.'&token='.Tools::getAdminTokenLite('AdminCategories'));
  299. elseif (Tools::getValue('deleteMode') == 'link' || Tools::getValue('deleteMode') == 'linkanddisable' || Tools::getValue('deleteMode') == 'delete')
  300. $this->delete_mode = Tools::getValue('deleteMode');
  301. else
  302. $this->action = 'select_delete';
  303. }
  304. public function renderKpis()
  305. {
  306. $time = time();
  307. $kpis = array();
  308. /* The data generation is located in AdminStatsControllerCore */
  309. $helper = new HelperKpi();
  310. $helper->id = 'box-disabled-categories';
  311. $helper->icon = 'icon-off';
  312. $helper->color = 'color1';
  313. $helper->title = $this->l('Disabled Categories', null, null, false);
  314. if (ConfigurationKPI::get('DISABLED_CATEGORIES') !== false)
  315. $helper->value = ConfigurationKPI::get('DISABLED_CATEGORIES');
  316. if (ConfigurationKPI::get('DISABLED_CATEGORIES_EXPIRE') < $time)
  317. $helper->source = $this->context->link->getAdminLink('AdminStats').'&ajax=1&action=getKpi&kpi=disabled_categories';
  318. $kpis[] = $helper->generate();
  319. $helper = new HelperKpi();
  320. $helper->id = 'box-empty-categories';
  321. $helper->icon = 'icon-bookmark-empty';
  322. $helper->color = 'color2';
  323. $helper->href = $this->context->link->getAdminLink('AdminTracking');
  324. $helper->title = $this->l('Empty Categories', null, null, false);
  325. if (ConfigurationKPI::get('EMPTY_CATEGORIES') !== false)
  326. $helper->value = ConfigurationKPI::get('EMPTY_CATEGORIES');
  327. if (ConfigurationKPI::get('EMPTY_CATEGORIES_EXPIRE') < $time)
  328. $helper->source = $this->context->link->getAdminLink('AdminStats').'&ajax=1&action=getKpi&kpi=empty_categories';
  329. $kpis[] = $helper->generate();
  330. $helper = new HelperKpi();
  331. $helper->id = 'box-top-category';
  332. $helper->icon = 'icon-money';
  333. $helper->color = 'color3';
  334. $helper->title = $this->l('Top Category', null, null, false);
  335. $helper->subtitle = $this->l('30 days', null, null, false);
  336. if (ConfigurationKPI::get('TOP_CATEGORY', $this->context->employee->id_lang) !== false)
  337. $helper->value = ConfigurationKPI::get('TOP_CATEGORY', $this->context->employee->id_lang);
  338. if (ConfigurationKPI::get('TOP_CATEGORY_EXPIRE', $this->context->employee->id_lang) < $time)
  339. $helper->source = $this->context->link->getAdminLink('AdminStats').'&ajax=1&action=getKpi&kpi=top_category';
  340. $kpis[] = $helper->generate();
  341. $helper = new HelperKpi();
  342. $helper->id = 'box-products-per-category';
  343. $helper->icon = 'icon-search';
  344. $helper->color = 'color4';
  345. $helper->title = $this->l('Average number of products per category', null, null, false);
  346. if (ConfigurationKPI::get('PRODUCTS_PER_CATEGORY') !== false)
  347. $helper->value = ConfigurationKPI::get('PRODUCTS_PER_CATEGORY');
  348. if (ConfigurationKPI::get('PRODUCTS_PER_CATEGORY_EXPIRE') < $time)
  349. $helper->source = $this->context->link->getAdminLink('AdminStats').'&ajax=1&action=getKpi&kpi=products_per_category';
  350. $kpis[] = $helper->generate();
  351. $helper = new HelperKpiRow();
  352. $helper->kpis = $kpis;
  353. return $helper->generate();
  354. }
  355. public function renderForm()
  356. {
  357. $this->initToolbar();
  358. $obj = $this->loadObject(true);
  359. $id_shop = Context::getContext()->shop->id;
  360. $selected_categories = array((isset($obj->id_parent) && $obj->isParentCategoryAvailable($id_shop))? (int)$obj->id_parent : (int)Tools::getValue('id_parent', Category::getRootCategory()->id));
  361. $unidentified = new Group(Configuration::get('PS_UNIDENTIFIED_GROUP'));
  362. $guest = new Group(Configuration::get('PS_GUEST_GROUP'));
  363. $default = new Group(Configuration::get('PS_CUSTOMER_GROUP'));
  364. $unidentified_group_information = sprintf($this->l('%s - All people without a valid customer account.'), '<b>'.$unidentified->name[$this->context->language->id].'</b>');
  365. $guest_group_information = sprintf($this->l('%s - Customer who placed an order with the guest checkout.'), '<b>'.$guest->name[$this->context->language->id].'</b>');
  366. $default_group_information = sprintf($this->l('%s - All people who have created an account on this site.'), '<b>'.$default->name[$this->context->language->id].'</b>');
  367. if (!($obj = $this->loadObject(true)))
  368. return;
  369. $image = _PS_CAT_IMG_DIR_.$obj->id.'.jpg';
  370. $image_url = ImageManager::thumbnail($image, $this->table.'_'.(int)$obj->id.'.'.$this->imageType, 350,
  371. $this->imageType, true, true);
  372. $image_size = file_exists($image) ? filesize($image) / 1000 : false;
  373. $this->fields_form = array(
  374. 'tinymce' => true,
  375. 'legend' => array(
  376. 'title' => $this->l('Category'),
  377. 'icon' => 'icon-tags'
  378. ),
  379. 'input' => array(
  380. array(
  381. 'type' => 'text',
  382. 'label' => $this->l('Name'),
  383. 'name' => 'name',
  384. 'lang' => true,
  385. 'required' => true,
  386. 'class' => 'copy2friendlyUrl',
  387. 'hint' => $this->l('Invalid characters:').' <>;=#{}',
  388. ),
  389. array(
  390. 'type' => 'switch',
  391. 'label' => $this->l('Displayed'),
  392. 'name' => 'active',
  393. 'required' => false,
  394. 'is_bool' => true,
  395. 'values' => array(
  396. array(
  397. 'id' => 'active_on',
  398. 'value' => 1,
  399. 'label' => $this->l('Enabled')
  400. ),
  401. array(
  402. 'id' => 'active_off',
  403. 'value' => 0,
  404. 'label' => $this->l('Disabled')
  405. )
  406. )
  407. ),
  408. array(
  409. 'type' => 'categories',
  410. 'label' => $this->l('Parent category'),
  411. 'name' => 'id_parent',
  412. 'tree' => array(
  413. 'id' => 'categories-tree',
  414. 'selected_categories' => $selected_categories,
  415. 'disabled_categories' => !Tools::isSubmit('add'.$this->table) ? array($this->_category->id) : null
  416. )
  417. ),
  418. array(
  419. 'type' => 'textarea',
  420. 'label' => $this->l('Description'),
  421. 'name' => 'description',
  422. 'autoload_rte' => true,
  423. 'lang' => true,
  424. 'hint' => $this->l('Invalid characters:').' <>;=#{}'
  425. ),
  426. array(
  427. 'type' => 'file',
  428. 'label' => $this->l('Image'),
  429. 'name' => 'image',
  430. 'display_image' => true,
  431. 'image' => $image_url ? $image_url : false,
  432. 'size' => $image_size,
  433. 'delete_url' => self::$currentIndex.'&'.$this->identifier.'='.$this->_category->id.'&token='.$this->token.'&deleteImage=1',
  434. 'hint' => $this->l('Upload a category logo from your computer.'),
  435. ),
  436. array(
  437. 'type' => 'text',
  438. 'label' => $this->l('Meta title'),
  439. 'name' => 'meta_title',
  440. 'lang' => true,
  441. 'hint' => $this->l('Forbidden characters:').' <>;=#{}'
  442. ),
  443. array(
  444. 'type' => 'text',
  445. 'label' => $this->l('Meta description'),
  446. 'name' => 'meta_description',
  447. 'lang' => true,
  448. 'hint' => $this->l('Forbidden characters:').' <>;=#{}'
  449. ),
  450. array(
  451. 'type' => 'tags',
  452. 'label' => $this->l('Meta keywords'),
  453. 'name' => 'meta_keywords',
  454. 'lang' => true,
  455. 'hint' => $this->l('To add "tags," click in the field, write something, and then press "Enter."').'&nbsp;'.$this->l('Forbidden characters:').' <>;=#{}'
  456. ),
  457. array(
  458. 'type' => 'text',
  459. 'label' => $this->l('Friendly URL'),
  460. 'name' => 'link_rewrite',
  461. 'lang' => true,
  462. 'required' => true,
  463. 'hint' => $this->l('Only letters, numbers, underscore (_) and the minus (-) character are allowed.')
  464. ),
  465. array(
  466. 'type' => 'group',
  467. 'label' => $this->l('Group access'),
  468. 'name' => 'groupBox',
  469. 'values' => Group::getGroups(Context::getContext()->language->id),
  470. 'info_introduction' => $this->l('You now have three default customer groups.'),
  471. 'unidentified' => $unidentified_group_information,
  472. 'guest' => $guest_group_information,
  473. 'customer' => $default_group_information,
  474. 'hint' => $this->l('Mark all of the customer groups which you would like to have access to this category.')
  475. )
  476. ),
  477. 'submit' => array(
  478. 'title' => $this->l('Save'),
  479. 'name' => 'submitAdd'.$this->table.'AndBackToParent'
  480. )
  481. );
  482. $this->tpl_form_vars['shared_category'] = Validate::isLoadedObject($obj) && $obj->hasMultishopEntries();
  483. $this->tpl_form_vars['PS_ALLOW_ACCENTED_CHARS_URL'] = (int)Configuration::get('PS_ALLOW_ACCENTED_CHARS_URL');
  484. $this->tpl_form_vars['displayBackOfficeCategory'] = Hook::exec('displayBackOfficeCategory');
  485. // Display this field only if multistore option is enabled
  486. if (Configuration::get('PS_MULTISHOP_FEATURE_ACTIVE') && Tools::isSubmit('add'.$this->table.'root'))
  487. {
  488. $this->fields_form['input'][] = array(
  489. 'type' => 'switch',
  490. 'label' => $this->l('Root Category'),
  491. 'name' => 'is_root_category',
  492. 'required' => false,
  493. 'is_bool' => true,
  494. 'values' => array(
  495. array(
  496. 'id' => 'is_root_on',
  497. 'value' => 1,
  498. 'label' => $this->l('Yes')
  499. ),
  500. array(
  501. 'id' => 'is_root_off',
  502. 'value' => 0,
  503. 'label' => $this->l('No')
  504. )
  505. )
  506. );
  507. unset($this->fields_form['input'][2],$this->fields_form['input'][3]);
  508. }
  509. // Display this field only if multistore option is enabled AND there are several stores configured
  510. if (Shop::isFeatureActive())
  511. $this->fields_form['input'][] = array(
  512. 'type' => 'shop',
  513. 'label' => $this->l('Shop association'),
  514. 'name' => 'checkBoxShopAsso',
  515. );
  516. // remove category tree and radio button "is_root_category" if this category has the root category as parent category to avoid any conflict
  517. if ($this->_category->id_parent == Category::getTopCategory()->id && Tools::isSubmit('updatecategory'))
  518. foreach ($this->fields_form['input'] as $k => $input)
  519. if (in_array($input['name'], array('id_parent', 'is_root_category')))
  520. unset($this->fields_form['input'][$k]);
  521. if (!($obj = $this->loadObject(true)))
  522. return;
  523. $image = ImageManager::thumbnail(_PS_CAT_IMG_DIR_.'/'.$obj->id.'.jpg', $this->table.'_'.(int)$obj->id.'.'.$this->imageType, 350, $this->imageType, true);
  524. $this->fields_value = array(
  525. 'image' => $image ? $image : false,
  526. 'size' => $image ? filesize(_PS_CAT_IMG_DIR_.'/'.$obj->id.'.jpg') / 1000 : false
  527. );
  528. // Added values of object Group
  529. $category_groups_ids = $obj->getGroups();
  530. $groups = Group::getGroups($this->context->language->id);
  531. // if empty $carrier_groups_ids : object creation : we set the default groups
  532. if (empty($category_groups_ids))
  533. {
  534. $preselected = array(Configuration::get('PS_UNIDENTIFIED_GROUP'), Configuration::get('PS_GUEST_GROUP'), Configuration::get('PS_CUSTOMER_GROUP'));
  535. $category_groups_ids = array_merge($category_groups_ids, $preselected);
  536. }
  537. foreach ($groups as $group)
  538. $this->fields_value['groupBox_'.$group['id_group']] = Tools::getValue('groupBox_'.$group['id_group'], (in_array($group['id_group'], $category_groups_ids)));
  539. $this->fields_value['is_root_category'] = (bool)Tools::isSubmit('add'.$this->table.'root');
  540. return parent::renderForm();
  541. }
  542. public function postProcess()
  543. {
  544. if (!in_array($this->display, array('edit', 'add')))
  545. $this->multishop_context_group = false;
  546. if (Tools::isSubmit('forcedeleteImage') || (isset($_FILES['image']) && $_FILES['image']['size'] > 0) || Tools::getValue('deleteImage'))
  547. {
  548. $this->processForceDeleteImage();
  549. if (Tools::isSubmit('forcedeleteImage'))
  550. Tools::redirectAdmin(self::$currentIndex.'&token='.Tools::getAdminTokenLite('AdminCategories').'&conf=7');
  551. }
  552. return parent::postProcess();
  553. }
  554. public function processForceDeleteImage()
  555. {
  556. $category = $this->loadObject(true);
  557. if (Validate::isLoadedObject($category))
  558. $category->deleteImage(true);
  559. }
  560. public function processAdd()
  561. {
  562. $id_category = (int)Tools::getValue('id_category');
  563. $id_parent = (int)Tools::getValue('id_parent');
  564. // if true, we are in a root category creation
  565. if (!$id_parent)
  566. {
  567. $_POST['is_root_category'] = $_POST['level_depth'] = 1;
  568. $_POST['id_parent'] = $id_parent = (int)Configuration::get('PS_ROOT_CATEGORY');
  569. }
  570. if ($id_category)
  571. {
  572. if ($id_category != $id_parent)
  573. {
  574. if (!Category::checkBeforeMove($id_category, $id_parent))
  575. $this->errors[] = Tools::displayError('The category cannot be moved here.');
  576. }
  577. else
  578. $this->errors[] = Tools::displayError('The category cannot be a parent of itself.');
  579. }
  580. $object = parent::processAdd();
  581. //if we create a you root category you have to associate to a shop before to add sub categories in. So we redirect to AdminCategories listing
  582. if ($object && Tools::getValue('is_root_category'))
  583. Tools::redirectAdmin(self::$currentIndex.'&token='.Tools::getAdminTokenLite('AdminCategories').'&conf=3');
  584. return $object;
  585. }
  586. protected function setDeleteMode()
  587. {
  588. if ($this->delete_mode == 'link' || $this->delete_mode == 'linkanddisable')
  589. {
  590. $this->remove_products = false;
  591. if ($this->delete_mode == 'linkanddisable')
  592. $this->disable_products = true;
  593. }
  594. elseif ($this->delete_mode != 'delete')
  595. $this->errors[] = Tools::displayError('Unknown delete mode:'.' '.$this->deleted);
  596. }
  597. protected function processBulkDelete()
  598. {
  599. if ($this->tabAccess['delete'] === '1')
  600. {
  601. $cats_ids = array();
  602. foreach (Tools::getValue($this->table.'Box') as $id_category)
  603. {
  604. $category = new Category((int)$id_category);
  605. if (!$category->isRootCategoryForAShop())
  606. $cats_ids[$category->id] = $category->id_parent;
  607. }
  608. if (parent::processBulkDelete())
  609. {
  610. $this->setDeleteMode();
  611. foreach ($cats_ids as $id => $id_parent)
  612. $this->processFatherlessProducts((int)$id_parent);
  613. return true;
  614. }
  615. else
  616. return false;
  617. }
  618. else
  619. $this->errors[] = Tools::displayError('You do not have permission to delete this.');
  620. }
  621. public function processDelete()
  622. {
  623. if ($this->tabAccess['delete'] === '1')
  624. {
  625. $category = $this->loadObject();
  626. if ($category->isRootCategoryForAShop())
  627. $this->errors[] = Tools::displayError('You cannot remove this category because one of your shops uses it as a root category.');
  628. elseif (parent::processDelete())
  629. {
  630. $this->setDeleteMode();
  631. $this->processFatherlessProducts((int)$category->id_parent);
  632. return true;
  633. }
  634. }
  635. else
  636. $this->errors[] = Tools::displayError('You do not have permission to delete this.');
  637. return false;
  638. }
  639. public function processFatherlessProducts($id_parent)
  640. {
  641. /* Delete or link products which were not in others categories */
  642. $fatherless_products = Db::getInstance()->executeS('
  643. SELECT p.`id_product` FROM `'._DB_PREFIX_.'product` p
  644. '.Shop::addSqlAssociation('product', 'p').'
  645. WHERE p.`id_product` NOT IN (SELECT DISTINCT(cp.`id_product`) FROM `'._DB_PREFIX_.'category_product` cp)');
  646. foreach ($fatherless_products as $id_poor_product)
  647. {
  648. $poor_product = new Product((int)$id_poor_product['id_product']);
  649. if (Validate::isLoadedObject($poor_product))
  650. {
  651. if ($this->remove_products || $id_parent == 0)
  652. $poor_product->delete();
  653. else
  654. {
  655. if ($this->disable_products)
  656. $poor_product->active = 0;
  657. $poor_product->id_category_default = (int)$id_parent;
  658. $poor_product->addToCategories((int)$id_parent);
  659. $poor_product->save();
  660. }
  661. }
  662. }
  663. }
  664. public function processPosition()
  665. {
  666. if ($this->tabAccess['edit'] !== '1')
  667. $this->errors[] = Tools::displayError('You do not have permission to edit this.');
  668. else if (!Validate::isLoadedObject($object = new Category((int)Tools::getValue($this->identifier, Tools::getValue('id_category_to_move', 1)))))
  669. $this->errors[] = Tools::displayError('An error occurred while updating the status for an object.').' <b>'.
  670. $this->table.'</b> '.Tools::displayError('(cannot load object)');
  671. if (!$object->updatePosition((int)Tools::getValue('way'), (int)Tools::getValue('position')))
  672. $this->errors[] = Tools::displayError('Failed to update the position.');
  673. else
  674. {
  675. $object->regenerateEntireNtree();
  676. Tools::redirectAdmin(self::$currentIndex.'&'.$this->table.'Orderby=position&'.$this->table.'Orderway=asc&conf=5'.(($id_category = (int)Tools::getValue($this->identifier, Tools::getValue('id_category_parent', 1))) ? ('&'.$this->identifier.'='.$id_category) : '').'&token='.Tools::getAdminTokenLite('AdminCategories'));
  677. }
  678. }
  679. protected function postImage($id)
  680. {
  681. $ret = parent::postImage($id);
  682. if (($id_category = (int)Tools::getValue('id_category')) &&
  683. isset($_FILES) && count($_FILES) && $_FILES['image']['name'] != null &&
  684. file_exists(_PS_CAT_IMG_DIR_.$id_category.'.jpg'))
  685. {
  686. $images_types = ImageType::getImagesTypes('categories');
  687. foreach ($images_types as $k => $image_type)
  688. {
  689. ImageManager::resize(
  690. _PS_CAT_IMG_DIR_.$id_category.'.jpg',
  691. _PS_CAT_IMG_DIR_.$id_category.'-'.stripslashes($image_type['name']).'.jpg',
  692. (int)$image_type['width'], (int)$image_type['height']
  693. );
  694. }
  695. }
  696. return $ret;
  697. }
  698. public static function getDescriptionClean($description)
  699. {
  700. return Tools::getDescriptionClean($description);
  701. }
  702. public function ajaxProcessUpdatePositions()
  703. {
  704. $id_category_to_move = (int)(Tools::getValue('id_category_to_move'));
  705. $id_category_parent = (int)(Tools::getValue('id_category_parent'));
  706. $way = (int)(Tools::getValue('way'));
  707. $positions = Tools::getValue('category');
  708. if (is_array($positions))
  709. foreach ($positions as $key => $value)
  710. {
  711. $pos = explode('_', $value);
  712. if ((isset($pos[1]) && isset($pos[2])) && ($pos[1] == $id_category_parent && $pos[2] == $id_category_to_move))
  713. {
  714. $position = $key + 1;
  715. break;
  716. }
  717. }
  718. $category = new Category($id_category_to_move);
  719. if (Validate::isLoadedObject($category))
  720. {
  721. if (isset($position) && $category->updatePosition($way, $position))
  722. {
  723. Hook::exec('actionCategoryUpdate');
  724. die(true);
  725. }
  726. else
  727. die('{"hasError" : true, errors : "Can not update categories position"}');
  728. }
  729. else
  730. die('{"hasError" : true, "errors" : "This category can not be loaded"}');
  731. }
  732. }