PageRenderTime 41ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 1ms

/classes/controller/AdminController.php

https://github.com/MyBB-Services/PrestaShop
PHP | 2998 lines | 2144 code | 357 blank | 497 comment | 591 complexity | 22cf7d021f3c320aa4def3e78288c487 MD5 | raw file
  1. <?php
  2. /*
  3. * 2007-2013 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-2013 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 AdminControllerCore extends Controller
  27. {
  28. public $path;
  29. public static $currentIndex;
  30. public $content;
  31. public $warnings = array();
  32. public $informations = array();
  33. public $confirmations = array();
  34. public $shopShareDatas = false;
  35. public $_languages = array();
  36. public $default_form_language;
  37. public $allow_employee_form_lang;
  38. public $layout = 'layout.tpl';
  39. protected $meta_title;
  40. public $template = 'content.tpl';
  41. /** @var string Associated table name */
  42. public $table;
  43. /** @var string Object identifier inside the associated table */
  44. protected $identifier = false;
  45. /** @var string Tab name */
  46. public $className;
  47. /** @var array tabAccess */
  48. public $tabAccess;
  49. /** @var integer Tab id */
  50. public $id = -1;
  51. public $required_database = false;
  52. /** @var string Security token */
  53. public $token;
  54. /** @var string shop | group_shop */
  55. public $shopLinkType;
  56. /** @var string Default ORDER BY clause when $_orderBy is not defined */
  57. protected $_defaultOrderBy = false;
  58. protected $_defaultOrderWay = 'ASC';
  59. public $tpl_form_vars = array();
  60. public $tpl_list_vars = array();
  61. public $tpl_delete_link_vars = array();
  62. public $tpl_option_vars = array();
  63. public $tpl_view_vars = array();
  64. public $tpl_required_fields_vars = array();
  65. public $base_tpl_view = null;
  66. public $base_tpl_form = null;
  67. /** @var bool if you want more fieldsets in the form */
  68. public $multiple_fieldsets = false;
  69. public $fields_value = false;
  70. /** @var array Errors displayed after post processing */
  71. public $errors = array();
  72. /** @var define if the header of the list contains filter and sorting links or not */
  73. protected $list_simple_header;
  74. /** @var array list to be generated */
  75. protected $fields_list;
  76. /** @var array modules list filters */
  77. protected $filter_modules_list = null;
  78. /** @var array modules list filters */
  79. protected $modules_list = array();
  80. /** @var array edit form to be generated */
  81. protected $fields_form;
  82. /** @var override of $fields_form */
  83. protected $fields_form_override;
  84. /** @var array list of option forms to be generated */
  85. protected $fields_options;
  86. protected $shopLink;
  87. /** @var string SQL query */
  88. protected $_listsql = '';
  89. /** @var array Cache for query results */
  90. protected $_list = array();
  91. /** @var define if the header of the list contains filter and sorting links or not */
  92. protected $toolbar_title;
  93. /** @var array list of toolbar buttons */
  94. protected $toolbar_btn = null;
  95. /** @var boolean scrolling toolbar */
  96. protected $toolbar_scroll = true;
  97. /** @var boolean set to false to hide toolbar and page title */
  98. protected $show_toolbar = true;
  99. /** @var boolean set to true to show toolbar and page title for options */
  100. protected $show_toolbar_options = false;
  101. /** @var integer Number of results in list */
  102. protected $_listTotal = 0;
  103. /** @var boolean Automatically join language table if true */
  104. public $lang = false;
  105. /** @var array WHERE clause determined by filter fields */
  106. protected $_filter;
  107. /** @var array Temporary SQL table WHERE clause determinated by filter fields */
  108. protected $_tmpTableFilter = '';
  109. /** @var array Number of results in list per page (used in select field) */
  110. protected $_pagination = array(20, 50, 100, 300);
  111. /** @var string ORDER BY clause determined by field/arrows in list header */
  112. protected $_orderBy;
  113. /** @var string Order way (ASC, DESC) determined by arrows in list header */
  114. protected $_orderWay;
  115. /** @var array list of available actions for each list row - default actions are view, edit, delete, duplicate */
  116. protected $actions_available = array('view', 'edit', 'delete', 'duplicate');
  117. /** @var array list of required actions for each list row */
  118. protected $actions = array();
  119. /** @var array list of row ids associated with a given action for witch this action have to not be available */
  120. protected $list_skip_actions = array();
  121. /* @var boolean don't show header & footer */
  122. protected $lite_display = false;
  123. /** @var bool boolean List content lines are clickable if true */
  124. protected $list_no_link = false;
  125. protected $allow_export = false;
  126. /** @var array $cache_lang cache for traduction */
  127. public static $cache_lang = array();
  128. /** @var array required_fields to display in the Required Fields form */
  129. public $required_fields = array();
  130. /** @var Helper */
  131. protected $helper;
  132. /**
  133. * @var array actions to execute on multiple selections
  134. * Usage:
  135. * array(
  136. * 'actionName' => array(
  137. * 'text' => $this->l('Message displayed on the submit button (mandatory)'),
  138. * 'confirm' => $this->l('If set, this confirmation message will pop-up (optional)')),
  139. * 'anotherAction' => array(...)
  140. * );
  141. *
  142. * If your action is named 'actionName', you need to have a method named bulkactionName() that will be executed when the button is clicked.
  143. */
  144. protected $bulk_actions;
  145. /**
  146. * @var array ids of the rows selected
  147. */
  148. protected $boxes;
  149. /** @var string Do not automatically select * anymore but select only what is necessary */
  150. protected $explicitSelect = false;
  151. /** @var string Add fields into data query to display list */
  152. protected $_select;
  153. /** @var string Join tables into data query to display list */
  154. protected $_join;
  155. /** @var string Add conditions into data query to display list */
  156. protected $_where;
  157. /** @var string Group rows into data query to display list */
  158. protected $_group;
  159. /** @var string Having rows into data query to display list */
  160. protected $_having;
  161. protected $is_cms = false;
  162. /** @var string identifier to use for changing positions in lists (can be omitted if positions cannot be changed) */
  163. protected $position_identifier;
  164. /** @var boolean Table records are not deleted but marked as deleted if set to true */
  165. protected $deleted = false;
  166. /**
  167. * @var bool is a list filter set
  168. */
  169. protected $filter;
  170. protected $noLink;
  171. protected $specificConfirmDelete = null;
  172. protected $colorOnBackground;
  173. /** @var bool If true, activates color on hover */
  174. protected $row_hover = true;
  175. /** @string Action to perform : 'edit', 'view', 'add', ... */
  176. protected $action;
  177. protected $display;
  178. protected $_includeContainer = true;
  179. protected $tab_modules_list = array('default_list' => array(), 'slider_list' => array());
  180. public $tpl_folder;
  181. protected $bo_theme;
  182. /** @var bool Redirect or not ater a creation */
  183. protected $_redirect = true;
  184. /** @var array Name and directory where class image are located */
  185. public $fieldImageSettings = array();
  186. /** @var string Image type */
  187. public $imageType = 'jpg';
  188. /** @var instanciation of the class associated with the AdminController */
  189. protected $object;
  190. /** @var current object ID */
  191. protected $id_object;
  192. /**
  193. * @var current controller name without suffix
  194. */
  195. public $controller_name;
  196. public $multishop_context = -1;
  197. public $multishop_context_group = true;
  198. /**
  199. * Current breadcrumb position as an array of tab names
  200. */
  201. protected $breadcrumbs;
  202. public function __construct()
  203. {
  204. global $timer_start;
  205. $this->timer_start = $timer_start;
  206. // Has to be remove for the next Prestashop version
  207. global $token;
  208. $this->controller_type = 'admin';
  209. $this->controller_name = get_class($this);
  210. if (strpos($this->controller_name, 'Controller'))
  211. $this->controller_name = substr($this->controller_name, 0, -10);
  212. parent::__construct();
  213. if ($this->multishop_context == -1)
  214. $this->multishop_context = Shop::CONTEXT_ALL | Shop::CONTEXT_GROUP | Shop::CONTEXT_SHOP;
  215. $this->bo_theme = ((Validate::isLoadedObject($this->context->employee) && $this->context->employee->bo_theme) ? $this->context->employee->bo_theme : 'default');
  216. if (!file_exists(_PS_BO_ALL_THEMES_DIR_.$this->bo_theme.DIRECTORY_SEPARATOR.'template'))
  217. $this->bo_theme = 'default';
  218. $this->context->smarty->setTemplateDir(array(
  219. _PS_BO_ALL_THEMES_DIR_.$this->bo_theme.DIRECTORY_SEPARATOR.'template',
  220. _PS_OVERRIDE_DIR_.'controllers'.DIRECTORY_SEPARATOR.'admin'.DIRECTORY_SEPARATOR.'templates'
  221. ));
  222. $this->id = Tab::getIdFromClassName($this->controller_name);
  223. $this->token = Tools::getAdminToken($this->controller_name.(int)$this->id.(int)$this->context->employee->id);
  224. $token = $this->token;
  225. $this->_conf = array(
  226. 1 => $this->l('Deletion successful'), 2 => $this->l('The selection has been successfully deleted.'),
  227. 3 => $this->l('Creation successful'), 4 => $this->l('Update successful'),
  228. 5 => $this->l('The status has been updated successfully.'), 6 => $this->l('The settings have been updated successfully.'),
  229. 7 => $this->l('The image was successfully deleted.'), 8 => $this->l('The module was downloaded successfully.'),
  230. 9 => $this->l('The thumbnails were successfully regenerated.'), 10 => $this->l('Message sent to the customer.'),
  231. 11 => $this->l('Comment added'), 12 => $this->l('Module(s) installed successfully.'),
  232. 13 => $this->l('Module(s) uninstalled successfully.'), 14 => $this->l('The translation was successfully copied.'),
  233. 15 => $this->l('The translations have been successfully added.'), 16 => $this->l('The module transplanted successfully to the hook.'),
  234. 17 => $this->l('The module was successfully removed from the hook.'), 18 => $this->l('Upload successful'),
  235. 19 => $this->l('Duplication was completed successfully.'), 20 => $this->l('The translation was added successfully, but the language has not been created.'),
  236. 21 => $this->l('Module reset successfully.'), 22 => $this->l('Module deleted successfully.'),
  237. 23 => $this->l('Localization pack imported successfully.'), 24 => $this->l('Localization pack imported successfully.'),
  238. 25 => $this->l('The selcted images have successfully been moved.'),
  239. 26 => $this->l('Your cover selection has been saved.'),
  240. 27 => $this->l('The image shop association has been modified.'),
  241. 28 => $this->l('A zone has been assigned to the selection successfully.'),
  242. 29 => $this->l('Upgrade successful'),
  243. 30 => $this->l('A partial refund was successfully created.'),
  244. 31 => $this->l('The discount was successfully generated.')
  245. );
  246. if (!$this->identifier) $this->identifier = 'id_'.$this->table;
  247. if (!$this->_defaultOrderBy) $this->_defaultOrderBy = $this->identifier;
  248. $this->tabAccess = Profile::getProfileAccess($this->context->employee->id_profile, $this->id);
  249. // Fix for AdminHome
  250. if ($this->controller_name == 'AdminHome')
  251. $_POST['token'] = $this->token;
  252. if (!Shop::isFeatureActive())
  253. $this->shopLinkType = '';
  254. //$this->base_template_folder = _PS_BO_ALL_THEMES_DIR_.$this->bo_theme.'/template';
  255. $this->override_folder = Tools::toUnderscoreCase(substr($this->controller_name, 5)).'/';
  256. // Get the name of the folder containing the custom tpl files
  257. $this->tpl_folder = Tools::toUnderscoreCase(substr($this->controller_name, 5)).'/';
  258. $this->initShopContext();
  259. $this->context->currency = new Currency(Configuration::get('PS_CURRENCY_DEFAULT'));
  260. }
  261. /**
  262. * Set breadcrumbs array for the controller page
  263. */
  264. public function initBreadcrumbs()
  265. {
  266. $tabs = array();
  267. $tabs = Tab::recursiveTab($this->id, $tabs);
  268. $tabs = array_reverse($tabs);
  269. foreach ($tabs as $tab)
  270. $this->breadcrumbs[] = $tab['name'];
  271. }
  272. /**
  273. * set default toolbar_title to admin breadcrumb
  274. *
  275. * @return void
  276. */
  277. public function initToolbarTitle()
  278. {
  279. $bread_extended = array_unique($this->breadcrumbs);
  280. switch ($this->display)
  281. {
  282. case 'edit':
  283. $bread_extended[] = $this->l('Edit');
  284. break;
  285. case 'add':
  286. $bread_extended[] = $this->l('Add new');
  287. break;
  288. case 'view':
  289. $bread_extended[] = $this->l('View');
  290. break;
  291. }
  292. $this->toolbar_title = $bread_extended;
  293. }
  294. /**
  295. * Check rights to view the current tab
  296. *
  297. * @param bool $disable
  298. * @return boolean
  299. */
  300. public function viewAccess($disable = false)
  301. {
  302. if ($disable)
  303. return true;
  304. if ($this->tabAccess['view'] === '1')
  305. return true;
  306. return false;
  307. }
  308. /**
  309. * Check for security token
  310. */
  311. public function checkToken()
  312. {
  313. $token = Tools::getValue('token');
  314. return (!empty($token) && $token === $this->token);
  315. }
  316. public function ajaxProcessHelpAccess()
  317. {
  318. $this->json = true;
  319. $item = Tools::getValue('item');
  320. $iso_user = Tools::getValue('isoUser');
  321. $country = Tools::getValue('country');
  322. $version = Tools::getValue('version');
  323. if (isset($item) && isset($iso_user) && isset($country))
  324. {
  325. $helper = new HelperHelpAccess($item, $iso_user, $country, $version);
  326. $this->content = $helper->generate();
  327. }
  328. else
  329. $this->content = 'none';
  330. $this->display = 'content';
  331. }
  332. /**
  333. * Set the filters used for the list display
  334. */
  335. public function processFilter()
  336. {
  337. $prefix = str_replace(array('admin', 'controller'), '', Tools::strtolower(get_class($this)));
  338. // Filter memorization
  339. if (isset($_POST) && !empty($_POST) && isset($this->table))
  340. foreach ($_POST as $key => $value)
  341. {
  342. if (stripos($key, $this->table.'Filter_') === 0)
  343. $this->context->cookie->{$prefix.$key} = !is_array($value) ? $value : serialize($value);
  344. elseif(stripos($key, 'submitFilter') === 0)
  345. $this->context->cookie->$key = !is_array($value) ? $value : serialize($value);
  346. }
  347. if (isset($_GET) && !empty($_GET) && isset($this->table))
  348. foreach ($_GET as $key => $value)
  349. if (stripos($key, $this->table.'OrderBy') === 0 || stripos($key, $this->table.'Orderway') === 0)
  350. $this->context->cookie->{$prefix.$key} = $value;
  351. $filters = $this->context->cookie->getFamily($prefix.$this->table.'Filter_');
  352. foreach ($filters as $key => $value)
  353. {
  354. /* Extracting filters from $_POST on key filter_ */
  355. if ($value != null && !strncmp($key, $prefix.$this->table.'Filter_', 7 + Tools::strlen($prefix.$this->table)))
  356. {
  357. $key = Tools::substr($key, 7 + Tools::strlen($prefix.$this->table));
  358. /* Table alias could be specified using a ! eg. alias!field */
  359. $tmp_tab = explode('!', $key);
  360. $filter = count($tmp_tab) > 1 ? $tmp_tab[1] : $tmp_tab[0];
  361. if ($field = $this->filterToField($key, $filter))
  362. {
  363. $type = (array_key_exists('filter_type', $field) ? $field['filter_type'] : (array_key_exists('type', $field) ? $field['type'] : false)); if (($type == 'date' || $type == 'datetime') && is_string($value))
  364. $value = Tools::unSerialize($value);
  365. $key = isset($tmp_tab[1]) ? $tmp_tab[0].'.`'.$tmp_tab[1].'`' : '`'.$tmp_tab[0].'`';
  366. // Assignement by reference
  367. if (array_key_exists('tmpTableFilter', $field))
  368. $sql_filter = & $this->_tmpTableFilter;
  369. elseif (array_key_exists('havingFilter', $field))
  370. $sql_filter = & $this->_filterHaving;
  371. else
  372. $sql_filter = & $this->_filter;
  373. /* Only for date filtering (from, to) */
  374. if (is_array($value))
  375. {
  376. if (isset($value[0]) && !empty($value[0]))
  377. {
  378. if (!Validate::isDate($value[0]))
  379. $this->errors[] = Tools::displayError('The \'From\' date format is invalid (YYYY-MM-DD)');
  380. else
  381. $sql_filter .= ' AND '.pSQL($key).' >= \''.pSQL(Tools::dateFrom($value[0])).'\'';
  382. }
  383. if (isset($value[1]) && !empty($value[1]))
  384. {
  385. if (!Validate::isDate($value[1]))
  386. $this->errors[] = Tools::displayError('The \'To\' date format is invalid (YYYY-MM-DD)');
  387. else
  388. $sql_filter .= ' AND '.pSQL($key).' <= \''.pSQL(Tools::dateTo($value[1])).'\'';
  389. }
  390. }
  391. else
  392. {
  393. $sql_filter .= ' AND ';
  394. $check_key = ($key == $this->identifier || $key == '`'.$this->identifier.'`');
  395. if ($type == 'int' || $type == 'bool')
  396. $sql_filter .= (($check_key || $key == '`active`') ? 'a.' : '').pSQL($key).' = '.(int)$value.' ';
  397. elseif ($type == 'decimal')
  398. $sql_filter .= ($check_key ? 'a.' : '').pSQL($key).' = '.(float)$value.' ';
  399. elseif ($type == 'select')
  400. $sql_filter .= ($check_key ? 'a.' : '').pSQL($key).' = \''.pSQL($value).'\' ';
  401. else
  402. $sql_filter .= ($check_key ? 'a.' : '').pSQL($key).' LIKE \'%'.pSQL($value).'%\' ';
  403. }
  404. }
  405. }
  406. }
  407. }
  408. /**
  409. * @todo uses redirectAdmin only if !$this->ajax
  410. */
  411. public function postProcess()
  412. {
  413. try {
  414. if ($this->ajax)
  415. {
  416. // from ajax-tab.php
  417. $action = Tools::getValue('action');
  418. // no need to use displayConf() here
  419. if (!empty($action) && method_exists($this, 'ajaxProcess'.Tools::toCamelCase($action)))
  420. return $this->{'ajaxProcess'.Tools::toCamelCase($action)}();
  421. elseif (method_exists($this, 'ajaxProcess'))
  422. return $this->ajaxProcess();
  423. }
  424. else
  425. {
  426. // Process list filtering
  427. if ($this->filter)
  428. $this->processFilter();
  429. // If the method named after the action exists, call "before" hooks, then call action method, then call "after" hooks
  430. if (!empty($this->action) && method_exists($this, 'process'.ucfirst(Tools::toCamelCase($this->action))))
  431. {
  432. // Hook before action
  433. Hook::exec('actionAdmin'.ucfirst($this->action).'Before', array('controller' => $this));
  434. Hook::exec('action'.get_class($this).ucfirst($this->action).'Before', array('controller' => $this));
  435. // Call process
  436. $return = $this->{'process'.Tools::toCamelCase($this->action)}();
  437. // Hook After Action
  438. Hook::exec('actionAdmin'.ucfirst($this->action).'After', array('controller' => $this, 'return' => $return));
  439. Hook::exec('action'.get_class($this).ucfirst($this->action).'After', array('controller' => $this, 'return' => $return));
  440. return $return;
  441. }
  442. }
  443. } catch (PrestaShopException $e) {
  444. $this->errors[] = $e->getMessage();
  445. };
  446. return false;
  447. }
  448. /**
  449. * Object Delete images
  450. */
  451. public function processDeleteImage()
  452. {
  453. if (Validate::isLoadedObject($object = $this->loadObject()))
  454. {
  455. if (($object->deleteImage()))
  456. {
  457. $redirect = self::$currentIndex.'&add'.$this->table.'&'.$this->identifier.'='.Tools::getValue($this->identifier).'&conf=7&token='.$this->token;
  458. if (!$this->ajax)
  459. $this->redirect_after = $redirect;
  460. else
  461. $this->content = 'ok';
  462. }
  463. }
  464. $this->errors[] = Tools::displayError('An error occurred while attempting to delet the image. (cannot load object).');
  465. return $object;
  466. }
  467. public function processExport()
  468. {
  469. // clean buffer
  470. if (ob_get_level() && ob_get_length() > 0)
  471. ob_clean();
  472. $this->getList($this->context->language->id);
  473. if (!count($this->_list))
  474. return;
  475. header('Content-type: text/csv');
  476. header('Content-Type: application/force-download; charset=UTF-8');
  477. header('Cache-Control: no-store, no-cache');
  478. header('Content-disposition: attachment; filename="'.$this->table.'_'.date('Y-m-d_His').'.csv"');
  479. $headers = array();
  480. foreach ($this->fields_list as $datas)
  481. $headers[] = Tools::htmlentitiesDecodeUTF8($datas['title']);
  482. $content = array();
  483. foreach ($this->_list as $i => $row)
  484. {
  485. $content[$i] = array();
  486. $path_to_image = false;
  487. foreach ($this->fields_list as $key => $params)
  488. {
  489. $field_value = isset($row[$key]) ? Tools::htmlentitiesDecodeUTF8($row[$key]) : '';
  490. if ($key == 'image')
  491. {
  492. if ($params['image'] != 'p' || Configuration::get('PS_LEGACY_IMAGES'))
  493. $path_to_image = Tools::getShopDomain(true)._PS_IMG_.$params['image'].'/'.$row['id_'.$this->table].(isset($row['id_image']) ? '-'.(int)$row['id_image'] : '').'.'.$this->imageType;
  494. else
  495. $path_to_image = Tools::getShopDomain(true)._PS_IMG_.$params['image'].'/'.Image::getImgFolderStatic($row['id_image']).(int)$row['id_image'].'.'.$this->imageType;
  496. if ($path_to_image)
  497. $field_value = $path_to_image;
  498. }
  499. $content[$i][] = $field_value;
  500. }
  501. }
  502. $this->context->smarty->assign(array(
  503. 'export_precontent' => "\xEF\xBB\xBF",
  504. 'export_headers' => $headers,
  505. 'export_content' => $content
  506. )
  507. );
  508. $this->layout = 'layout-export.tpl';
  509. }
  510. /**
  511. * Object Delete
  512. */
  513. public function processDelete()
  514. {
  515. if (Validate::isLoadedObject($object = $this->loadObject()))
  516. {
  517. $res = true;
  518. // check if request at least one object with noZeroObject
  519. if (isset($object->noZeroObject) && count(call_user_func(array($this->className, $object->noZeroObject))) <= 1)
  520. {
  521. $this->errors[] = Tools::displayError('You need at least one object.').
  522. ' <b>'.$this->table.'</b><br />'.
  523. Tools::displayError('You cannot delete all of the items.');
  524. }
  525. elseif (array_key_exists('delete', $this->list_skip_actions) && in_array($object->id, $this->list_skip_actions['delete'])) //check if some ids are in list_skip_actions and forbid deletion
  526. $this->errors[] = Tools::displayError('You cannot delete this item.');
  527. else
  528. {
  529. if ($this->deleted)
  530. {
  531. if (!empty($this->fieldImageSettings))
  532. $res = $object->deleteImage();
  533. if (!$res)
  534. $this->errors[] = Tools::displayError('Unable to delete associated images.');
  535. $object->deleted = 1;
  536. if ($object->update())
  537. $this->redirect_after = self::$currentIndex.'&conf=1&token='.$this->token;
  538. }
  539. elseif ($object->delete())
  540. $this->redirect_after = self::$currentIndex.'&conf=1&token='.$this->token;
  541. $this->errors[] = Tools::displayError('An error occurred during deletion.');
  542. }
  543. }
  544. else
  545. {
  546. $this->errors[] = Tools::displayError('An error occurred while deleting the object.').
  547. ' <b>'.$this->table.'</b> '.
  548. Tools::displayError('(cannot load object)');
  549. }
  550. return $object;
  551. }
  552. /**
  553. * Call the right method for creating or updating object
  554. *
  555. * @return mixed
  556. */
  557. public function processSave()
  558. {
  559. if ($this->id_object)
  560. {
  561. $this->object = $this->loadObject();
  562. return $this->processUpdate();
  563. }
  564. else
  565. return $this->processAdd();
  566. }
  567. /**
  568. * Object creation
  569. */
  570. public function processAdd()
  571. {
  572. /* Checking fields validity */
  573. $this->validateRules();
  574. if (count($this->errors) <= 0)
  575. {
  576. $this->object = new $this->className();
  577. $this->copyFromPost($this->object, $this->table);
  578. $this->beforeAdd($this->object);
  579. if (method_exists($this->object, 'add') && !$this->object->add())
  580. {
  581. $this->errors[] = Tools::displayError('An error occurred while creating an object.').
  582. ' <b>'.$this->table.' ('.Db::getInstance()->getMsgError().')</b>';
  583. }
  584. /* voluntary do affectation here */
  585. elseif (($_POST[$this->identifier] = $this->object->id) && $this->postImage($this->object->id) && !count($this->errors) && $this->_redirect)
  586. {
  587. $parent_id = (int)Tools::getValue('id_parent', 1);
  588. $this->afterAdd($this->object);
  589. $this->updateAssoShop($this->object->id);
  590. // Save and stay on same form
  591. if (empty($this->redirect_after) && $this->redirect_after !== false && Tools::isSubmit('submitAdd'.$this->table.'AndStay'))
  592. $this->redirect_after = self::$currentIndex.'&'.$this->identifier.'='.$this->object->id.'&conf=3&update'.$this->table.'&token='.$this->token;
  593. // Save and back to parent
  594. if (empty($this->redirect_after) && $this->redirect_after !== false && Tools::isSubmit('submitAdd'.$this->table.'AndBackToParent'))
  595. $this->redirect_after = self::$currentIndex.'&'.$this->identifier.'='.$parent_id.'&conf=3&token='.$this->token;
  596. // Default behavior (save and back)
  597. if (empty($this->redirect_after) && $this->redirect_after !== false)
  598. $this->redirect_after = self::$currentIndex.($parent_id ? '&'.$this->identifier.'='.$this->object->id : '').'&conf=3&token='.$this->token;
  599. }
  600. }
  601. $this->errors = array_unique($this->errors);
  602. if (!empty($this->errors))
  603. {
  604. // if we have errors, we stay on the form instead of going back to the list
  605. $this->display = 'edit';
  606. return false;
  607. }
  608. return $this->object;
  609. }
  610. /**
  611. * Object update
  612. */
  613. public function processUpdate()
  614. {
  615. /* Checking fields validity */
  616. $this->validateRules();
  617. if (empty($this->errors))
  618. {
  619. $id = (int)Tools::getValue($this->identifier);
  620. /* Object update */
  621. if (isset($id) && !empty($id))
  622. {
  623. $object = new $this->className($id);
  624. if (Validate::isLoadedObject($object))
  625. {
  626. /* Specific to objects which must not be deleted */
  627. if ($this->deleted && $this->beforeDelete($object))
  628. {
  629. // Create new one with old objet values
  630. $object_new = $object->duplicateObject();
  631. if (Validate::isLoadedObject($object_new))
  632. {
  633. // Update old object to deleted
  634. $object->deleted = 1;
  635. $object->update();
  636. // Update new object with post values
  637. $this->copyFromPost($object_new, $this->table);
  638. $result = $object_new->update();
  639. if (Validate::isLoadedObject($object_new))
  640. $this->afterDelete($object_new, $object->id);
  641. }
  642. }
  643. else
  644. {
  645. $this->copyFromPost($object, $this->table);
  646. $result = $object->update();
  647. $this->afterUpdate($object);
  648. }
  649. if ($object->id)
  650. $this->updateAssoShop($object->id);
  651. if (!$result)
  652. {
  653. $this->errors[] = Tools::displayError('An error occurred while updating an object.').
  654. ' <b>'.$this->table.'</b> ('.Db::getInstance()->getMsgError().')';
  655. }
  656. elseif ($this->postImage($object->id) && !count($this->errors) && $this->_redirect)
  657. {
  658. $parent_id = (int)Tools::getValue('id_parent', 1);
  659. // Specific back redirect
  660. if ($back = Tools::getValue('back'))
  661. $this->redirect_after = urldecode($back).'&conf=4';
  662. // Specific scene feature
  663. // @todo change stay_here submit name (not clear for redirect to scene ... )
  664. if (Tools::getValue('stay_here') == 'on' || Tools::getValue('stay_here') == 'true' || Tools::getValue('stay_here') == '1')
  665. $this->redirect_after = self::$currentIndex.'&'.$this->identifier.'='.$object->id.'&conf=4&updatescene&token='.$this->token;
  666. // Save and stay on same form
  667. // @todo on the to following if, we may prefer to avoid override redirect_after previous value
  668. if (Tools::isSubmit('submitAdd'.$this->table.'AndStay'))
  669. $this->redirect_after = self::$currentIndex.'&'.$this->identifier.'='.$object->id.'&conf=4&update'.$this->table.'&token='.$this->token;
  670. // Save and back to parent
  671. if (Tools::isSubmit('submitAdd'.$this->table.'AndBackToParent'))
  672. $this->redirect_after = self::$currentIndex.'&'.$this->identifier.'='.$parent_id.'&conf=4&token='.$this->token;
  673. // Default behavior (save and back)
  674. if (empty($this->redirect_after))
  675. $this->redirect_after = self::$currentIndex.($parent_id ? '&'.$this->identifier.'='.$object->id : '').'&conf=4&token='.$this->token;
  676. }
  677. }
  678. else
  679. $this->errors[] = Tools::displayError('An error occurred while updating an object.').
  680. ' <b>'.$this->table.'</b> '.Tools::displayError('(cannot load object)');
  681. }
  682. }
  683. $this->errors = array_unique($this->errors);
  684. if (!empty($this->errors))
  685. {
  686. // if we have errors, we stay on the form instead of going back to the list
  687. $this->display = 'edit';
  688. return false;
  689. }
  690. if (isset($object))
  691. return $object;
  692. return;
  693. }
  694. /**
  695. * Change object required fields
  696. */
  697. public function processUpdateFields()
  698. {
  699. if (!is_array($fields = Tools::getValue('fieldsBox')))
  700. $fields = array();
  701. $object = new $this->className();
  702. if (!$object->addFieldsRequiredDatabase($fields))
  703. $this->errors[] = Tools::displayError('An error occurred when attempting to update the required fields.');
  704. else
  705. $this->redirect_after = self::$currentIndex.'&conf=4&token='.$this->token;
  706. return $object;
  707. }
  708. /**
  709. * Change object status (active, inactive)
  710. */
  711. public function processStatus()
  712. {
  713. if (Validate::isLoadedObject($object = $this->loadObject()))
  714. {
  715. if ($object->toggleStatus())
  716. {
  717. $id_category = (($id_category = (int)Tools::getValue('id_category')) && Tools::getValue('id_product')) ? '&id_category='.$id_category : '';
  718. $this->redirect_after = self::$currentIndex.'&conf=5'.$id_category.'&token='.$this->token;
  719. }
  720. else
  721. $this->errors[] = Tools::displayError('An error occurred while updating the status.');
  722. }
  723. else
  724. $this->errors[] = Tools::displayError('An error occurred while updating the status for an object.').
  725. ' <b>'.$this->table.'</b> '.
  726. Tools::displayError('(cannot load object)');
  727. return $object;
  728. }
  729. /**
  730. * Change object position
  731. */
  732. public function processPosition()
  733. {
  734. if (!Validate::isLoadedObject($object = $this->loadObject()))
  735. {
  736. $this->errors[] = Tools::displayError('An error occurred while updating the status for an object.').
  737. ' <b>'.$this->table.'</b> '.Tools::displayError('(cannot load object)');
  738. }
  739. elseif (!$object->updatePosition((int)Tools::getValue('way'), (int)Tools::getValue('position')))
  740. $this->errors[] = Tools::displayError('Failed to update the position.');
  741. else
  742. {
  743. $id_identifier_str = ($id_identifier = (int)Tools::getValue($this->identifier)) ? '&'.$this->identifier.'='.$id_identifier : '';
  744. $redirect = self::$currentIndex.'&'.$this->table.'Orderby=position&'.$this->table.'Orderway=asc&conf=5'.$id_identifier_str.'&token='.$this->token;
  745. $this->redirect_after = $redirect;
  746. }
  747. return $object;
  748. }
  749. /**
  750. * Cancel all filters for this tab
  751. */
  752. public function processResetFilters()
  753. {
  754. $prefix = str_replace(array('admin', 'controller'), '', Tools::strtolower(get_class($this)));
  755. $filters = $this->context->cookie->getFamily($prefix.$this->table.'Filter_');
  756. foreach ($filters as $cookie_key => $filter)
  757. if (strncmp($cookie_key, $prefix.$this->table.'Filter_', 7 + Tools::strlen($prefix.$this->table)) == 0)
  758. {
  759. $key = substr($cookie_key, 7 + Tools::strlen($prefix.$this->table));
  760. /* Table alias could be specified using a ! eg. alias!field */
  761. $tmp_tab = explode('!', $key);
  762. $key = (count($tmp_tab) > 1 ? $tmp_tab[1] : $tmp_tab[0]);
  763. if (is_array($this->fields_list) && array_key_exists($key, $this->fields_list))
  764. unset($this->context->cookie->$cookie_key);
  765. }
  766. if (isset($this->context->cookie->{'submitFilter'.$this->table}))
  767. unset($this->context->cookie->{'submitFilter'.$this->table});
  768. if (isset($this->context->cookie->{$prefix.$this->table.'Orderby'}))
  769. unset($this->context->cookie->{$prefix.$this->table.'Orderby'});
  770. if (isset($this->context->cookie->{$prefix.$this->table.'Orderway'}))
  771. unset($this->context->cookie->{$prefix.$this->table.'Orderway'});
  772. unset($_POST);
  773. $this->_filter = false;
  774. unset($this->_filterHaving);
  775. unset($this->_having);
  776. }
  777. /**
  778. * Update options and preferences
  779. */
  780. protected function processUpdateOptions()
  781. {
  782. $this->beforeUpdateOptions();
  783. $languages = Language::getLanguages(false);
  784. $hide_multishop_checkbox = (Shop::getTotalShops(false, null) < 2) ? true : false;
  785. foreach ($this->fields_options as $category_data)
  786. {
  787. if (!isset($category_data['fields']))
  788. continue;
  789. $fields = $category_data['fields'];
  790. foreach ($fields as $field => $values)
  791. {
  792. if (isset($values['type']) && $values['type'] == 'selectLang')
  793. {
  794. foreach ($languages as $lang)
  795. if (Tools::getValue($field.'_'.strtoupper($lang['iso_code'])))
  796. $fields[$field.'_'.strtoupper($lang['iso_code'])] = array(
  797. 'type' => 'select',
  798. 'cast' => 'strval',
  799. 'identifier' => 'mode',
  800. 'list' => $values['list']
  801. );
  802. }
  803. }
  804. // Validate fields
  805. foreach ($fields as $field => $values)
  806. {
  807. // We don't validate fields with no visibility
  808. if (!$hide_multishop_checkbox && Shop::isFeatureActive() && isset($values['visibility']) && $values['visibility'] > Shop::getContext())
  809. continue;
  810. // Check if field is required
  811. if (isset($values['required']) && $values['required'] && !empty($_POST['multishopOverrideOption'][$field]))
  812. if (isset($values['type']) && $values['type'] == 'textLang')
  813. {
  814. foreach ($languages as $language)
  815. if (($value = Tools::getValue($field.'_'.$language['id_lang'])) == false && (string)$value != '0')
  816. $this->errors[] = sprintf(Tools::displayError('field %s is required.'), $values['title']);
  817. }
  818. elseif (($value = Tools::getValue($field)) == false && (string)$value != '0')
  819. $this->errors[] = sprintf(Tools::displayError('field %s is required.'), $values['title']);
  820. // Check field validator
  821. if (isset($values['type']) && $values['type'] == 'textLang')
  822. {
  823. foreach ($languages as $language)
  824. if (Tools::getValue($field.'_'.$language['id_lang']) && isset($values['validation']))
  825. if (!Validate::$values['validation'](Tools::getValue($field.'_'.$language['id_lang'])))
  826. $this->errors[] = sprintf(Tools::displayError('field %s is invalid.'), $values['title']);
  827. }
  828. elseif (Tools::getValue($field) && isset($values['validation']))
  829. if (!Validate::$values['validation'](Tools::getValue($field)))
  830. $this->errors[] = sprintf(Tools::displayError('field %s is invalid.'), $values['title']);
  831. // Set default value
  832. if (Tools::getValue($field) === false && isset($values['default']))
  833. $_POST[$field] = $values['default'];
  834. }
  835. if (!count($this->errors))
  836. {
  837. foreach ($fields as $key => $options)
  838. {
  839. if (!$hide_multishop_checkbox && Shop::isFeatureActive() && isset($options['visibility']) && $options['visibility'] > Shop::getContext())
  840. continue;
  841. if (!$hide_multishop_checkbox && Shop::isFeatureActive() && Shop::getContext() != Shop::CONTEXT_ALL && empty($options['no_multishop_checkbox']) && empty($_POST['multishopOverrideOption'][$key]))
  842. {
  843. Configuration::deleteFromContext($key);
  844. continue;
  845. }
  846. // check if a method updateOptionFieldName is available
  847. $method_name = 'updateOption'.Tools::toCamelCase($key, true);
  848. if (method_exists($this, $method_name))
  849. $this->$method_name(Tools::getValue($key));
  850. elseif (isset($options['type']) && in_array($options['type'], array('textLang', 'textareaLang')))
  851. {
  852. $list = array();
  853. foreach ($languages as $language)
  854. {
  855. $key_lang = Tools::getValue($key.'_'.$language['id_lang']);
  856. $val = (isset($options['cast']) ? $options['cast']($key_lang) : $key_lang);
  857. if ($this->validateField($val, $options))
  858. {
  859. if (Validate::isCleanHtml($val))
  860. $list[$language['id_lang']] = $val;
  861. else
  862. $this->errors[] = Tools::displayError('Can not add configuration '.$key.' for lang '.Language::getIsoById((int)$language['id_lang']));
  863. }
  864. }
  865. Configuration::updateValue($key, $list);
  866. }
  867. else
  868. {
  869. $val = (isset($options['cast']) ? $options['cast'](Tools::getValue($key)) : Tools::getValue($key));
  870. if ($this->validateField($val, $options))
  871. {
  872. if (Validate::isCleanHtml($val))
  873. Configuration::updateValue($key, $val);
  874. else
  875. $this->errors[] = Tools::displayError('Can not add configuration '.$key);
  876. }
  877. }
  878. }
  879. }
  880. }
  881. $this->display = 'list';
  882. if (empty($this->errors))
  883. $this->confirmations[] = $this->_conf[6];
  884. }
  885. /**
  886. * assign default action in toolbar_btn smarty var, if they are not set.
  887. * uses override to specifically add, modify or remove items
  888. *
  889. */
  890. public function initToolbar()
  891. {
  892. switch ($this->display)
  893. {
  894. case 'add':
  895. case 'edit':
  896. // Default save button - action dynamically handled in javascript
  897. $this->toolbar_btn['save'] = array(
  898. 'href' => '#',
  899. 'desc' => $this->l('Save')
  900. );
  901. //no break
  902. case 'view':
  903. // Default cancel button - like old back link
  904. $back = Tools::safeOutput(Tools::getValue('back', ''));
  905. if (empty($back))
  906. $back = self::$currentIndex.'&token='.$this->token;
  907. if (!Validate::isCleanHtml($back))
  908. die(Tools::displayError());
  909. if (!$this->lite_display)
  910. $this->toolbar_btn['back'] = array(
  911. 'href' => $back,
  912. 'desc' => $this->l('Back to list')
  913. );
  914. break;
  915. case 'options':
  916. $this->toolbar_btn['save'] = array(
  917. 'href' => '#',
  918. 'desc' => $this->l('Save')
  919. );
  920. break;
  921. case 'view':
  922. break;
  923. default: // list
  924. $this->toolbar_btn['new'] = array(
  925. 'href' => self::$currentIndex.'&amp;add'.$this->table.'&amp;token='.$this->token,
  926. 'desc' => $this->l('Add new')
  927. );
  928. if ($this->allow_export)
  929. $this->toolbar_btn['export'] = array(
  930. 'href' => self::$currentIndex.'&amp;export'.$this->table.'&amp;token='.$this->token,
  931. 'desc' => $this->l('Export')
  932. );
  933. }
  934. $this->addToolBarModulesListButton();
  935. }
  936. /**
  937. * Load class object using identifier in $_GET (if possible)
  938. * otherwise return an empty object, or die
  939. *
  940. * @param boolean $opt Return an empty object if load fail
  941. * @return object
  942. */
  943. protected function loadObject($opt = false)
  944. {
  945. $id = (int)Tools::getValue($this->identifier);
  946. if ($id && Validate::isUnsignedId($id))
  947. {
  948. if (!$this->object)
  949. $this->object = new $this->className($id);
  950. if (Validate::isLoadedObject($this->object))
  951. return $this->object;
  952. // throw exception
  953. $this->errors[] = Tools::displayError('The object cannot be loaded (or found)');
  954. return false;
  955. }
  956. elseif ($opt)
  957. {
  958. if (!$this->object)
  959. $this->object = new $this->className();
  960. return $this->object;
  961. }
  962. else
  963. {
  964. $this->errors[] = Tools::displayError('The object cannot be loaded (the dentifier is missing or invalid)');
  965. return false;
  966. }
  967. return $this->object;
  968. }
  969. /**
  970. * Check if the token is valid, else display a warning page
  971. */
  972. public function checkAccess()
  973. {
  974. if (!$this->checkToken())
  975. {
  976. // If this is an XSS attempt, then we should only display a simple, secure page
  977. // ${1} in the replacement string of the regexp is required,
  978. // because the token may begin with a number and mix up with it (e.g. $17)
  979. $url = preg_replace('/([&?]token=)[^&]*(&.*)?$/', '${1}'.$this->token.'$2', $_SERVER['REQUEST_URI']);
  980. if (false === strpos($url, '?token=') && false === strpos($url, '&token='))
  981. $url .= '&token='.$this->token;
  982. if (strpos($url, '?') === false)
  983. $url = str_replace('&token', '?controller=AdminHome&token', $url);
  984. $this->context->smarty->assign('url', htmlentities($url));
  985. return false;
  986. }
  987. return true;
  988. }
  989. protected function filterToField($key, $filter)
  990. {
  991. foreach ($this->fields_list as $field)
  992. if (array_key_exists('filter_key', $field) && $field['filter_key'] == $key)
  993. return $field;
  994. if (array_key_exists($filter, $this->fields_list))
  995. return $this->fields_list[$filter];
  996. return false;
  997. }
  998. public function displayNoSmarty()
  999. {
  1000. }
  1001. public function displayAjax()
  1002. {
  1003. if ($this->json)
  1004. {
  1005. $this->context->smarty->assign(array(
  1006. 'json' => true,
  1007. 'status' => $this->status,
  1008. ));
  1009. }
  1010. $this->layout = 'layout-ajax.tpl';
  1011. return $this->display();
  1012. }
  1013. protected function redirect()
  1014. {
  1015. header('Location: '.$this->redirect_after);
  1016. exit;
  1017. }
  1018. public function display()
  1019. {
  1020. $this->context->smarty->assign(array(
  1021. 'display_header' => $this->display_header,
  1022. 'display_footer' => $this->display_footer,
  1023. )
  1024. );
  1025. // Use page title from meta_title if it has been set else from the breadcrumbs array
  1026. if (!$this->meta_title)
  1027. $this->meta_title = isset($this->breadcrumbs[1]) ? $this->breadcrumbs[1] : $this->breadcrumbs[0];
  1028. $this->context->smarty->assign('meta_title', $this->meta_title);
  1029. $tpl_action = $this->tpl_folder.$this->display.'.tpl';
  1030. // Check if action template has been override
  1031. foreach ($this->context->smarty->getTemplateDir() as $template_dir)
  1032. if (file_exists($template_dir.DIRECTORY_SEPARATOR.$tpl_action) && $this->display != 'view' && $this->display != 'options')
  1033. {
  1034. if (method_exists($this, $this->display.Tools::toCamelCase($this->className)))
  1035. $this->{$this->display.Tools::toCamelCase($this->className)}();
  1036. $this->context->smarty->assign('content', $this->context->smarty->fetch($tpl_action));
  1037. break;
  1038. }
  1039. if (!$this->ajax)
  1040. {
  1041. $template = $this->createTemplate($this->template);
  1042. $page = $template->fetch();
  1043. }
  1044. else
  1045. $page = $this->content;
  1046. if ($conf = Tools::getValue('conf'))
  1047. if ($this->json)
  1048. $this->context->smarty->assign('conf', Tools::jsonEncode($this->_conf[(int)$conf]));
  1049. else
  1050. $this->context->smarty->assign('conf', $this->_conf[(int)$conf]);
  1051. $notifications_type = array('errors', 'warnings', 'informations', 'confirmations');
  1052. foreach($notifications_type as $type)
  1053. if ($this->json)
  1054. $this->context->smarty->assign($type, Tools::jsonEncode(array_unique($this->$type)));
  1055. else
  1056. $this->context->smarty->assign($type, array_unique($this->$type));
  1057. if ($this->json)
  1058. $this->context->smarty->assign('page', Tools::jsonEncode($page));
  1059. else
  1060. $this->context->smarty->assign('page', $page);
  1061. $this->smartyOutputContent($this->layout);
  1062. }
  1063. /**
  1064. * add a warning message to display at the top of the page
  1065. *
  1066. * @param string $msg
  1067. */
  1068. protected function displayWarning($msg)
  1069. {
  1070. $this->warnings[] = $msg;
  1071. }
  1072. /**
  1073. * add a info message to display at the top of the page
  1074. *
  1075. * @param string $msg
  1076. */
  1077. protected function displayInformation($msg)
  1078. {
  1079. $this->informations[] = $msg;
  1080. }
  1081. /**
  1082. * Assign smarty variables for the header
  1083. */
  1084. public function initHeader()
  1085. {
  1086. // Multishop
  1087. $is_multishop = Shop::isFeatureActive();
  1088. // Quick access
  1089. $quick_access = QuickAccess::getQuickAccesses($this->context->language->id);
  1090. foreach ($quick_access as $index => $quick)
  1091. {
  1092. if ($quick['link'] == '../' && Shop::getContext() == Shop::CONTEXT_SHOP)
  1093. {
  1094. $url = $this->context->shop->getBaseURL();
  1095. if (!$url)
  1096. {
  1097. unset($quick_access[$index]);
  1098. continue;
  1099. }
  1100. $quick_access[$index]['link'] = $url;
  1101. }
  1102. else
  1103. {
  1104. preg_match('/controller=(.+)(&.+)?$/', $quick['link'], $admin_tab);
  1105. if (isset($admin_tab[1]))
  1106. {
  1107. if (strpos($admin_tab[1], '&'))
  1108. $admin_tab[1] = substr($admin_tab[1], 0, strpos($admin_tab[1], '&'));
  1109. $token = Tools::getAdminToken($admin_tab[1].(int)Tab::getIdFromClassName($admin_tab[1]).(int)$this->context->employee->id);
  1110. $quick_access[$index]['link'] .= '&token='.$token;
  1111. }
  1112. }
  1113. }
  1114. // Tab list
  1115. $tabs = Tab::getTabs($this->context->language->id, 0);
  1116. $current_id = Tab::getCurrentParentId();
  1117. foreach ($tabs as $index => $tab)
  1118. {
  1119. if ($tab['class_name'] == 'AdminStock' && Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT') == 0)
  1120. {
  1121. unset($tabs[$index]);
  1122. continue;
  1123. }
  1124. $img_cache_url = 'themes/'.$this->context->employee->bo_theme.'/img/t/'.$tab['class_name'].'.png';
  1125. $img_exists_cache = Tools::file_exists_cache(_PS_ADMIN_DIR_.$img_cache_url);
  1126. // retrocompatibility : change png to gif if icon not exists
  1127. if (!$img_exists_cache)
  1128. $img_exists_cache = Tools::file_exists_cache(_PS_ADMIN_DIR_.str_replace('.png', '.gif', $img_cache_url));
  1129. if ($img_exists_cache)
  1130. $path_img = $img = $img_exists_cache;
  1131. else
  1132. {
  1133. $path_img = _PS_IMG_DIR_.'t/'.$tab['class_name'].'.png';
  1134. // Relative link will always work, whatever the base uri set in the admin
  1135. $img = '../img/t/'.$tab['class_name'].'.png';
  1136. }
  1137. if (trim($tab['module']) != '')
  1138. {
  1139. $path_img = _PS_MODULE_DIR_.$tab['module'].'/'.$tab['class_name'].'.png';
  1140. // Relative link will always work, whatever the base uri set in the admin
  1141. $img = '../modules/'.$tab['module'].'/'.$tab['class_name'].'.png';
  1142. }
  1143. // retrocompatibility
  1144. if (!file_exists($path_img))
  1145. $img = str_replace('png', 'gif', $img);
  1146. // tab[class_name] does not contains the "Controller" suffix
  1147. $tabs[$index]['current'] = ($tab['class_name'].'Controller' == get_class($this)) || ($current_id == $tab['id_tab']);
  1148. $tabs[$index]['img'] = $img;
  1149. $tabs[$index]['href'] = $this->context->link->getAdminLink($tab['class_name']);
  1150. $sub_tabs = Tab::getTabs($this->context->language->id, $tab['id_tab']);
  1151. foreach ($sub_tabs as $index2 => $sub_tab)
  1152. {
  1153. // class_name is the name of the class controller
  1154. if (Tab::checkTabRights($sub_tab['id_tab']) === true && (bool)$sub_tab['active'])
  1155. {
  1156. $sub_tabs[$index2]['href'] = $this->context->link->getAdminLink($sub_tab['class_name']);
  1157. $sub_tabs[$index2]['current'] = ($sub_tab['class_name'].'Controller' == get_class($this));
  1158. }
  1159. else
  1160. unset($sub_tabs[$index2]);
  1161. }
  1162. $tabs[$index]['sub_tabs'] = $sub_tabs;
  1163. // If there are no subtabs, we don't want to display the parent tab in menu
  1164. if (empty($sub_tabs))
  1165. unset($tabs[$index]);
  1166. }
  1167. if (Validate::isLoadedObject($this->context->employee))
  1168. {
  1169. $accesses = Profile::getProfileAccesses($this->context->employee->id_profile, 'class_name');
  1170. /* Hooks are volontary out the initialize array (need those variables already assigned) */
  1171. $bo_color = empty($this->context->employee->bo_color) ? '#FFFFFF' : $this->context->employee->bo_color;
  1172. $this->context->smarty->assign(array(
  1173. 'autorefresh_notifications' => Configuration::get('PS_ADMIN_REFRESH_NOTIFICATION'),
  1174. 'help_box' => Configuration::get('PS_HELPBOX'),
  1175. 'round_mode' => Configuration::get('PS_PRICE_ROUND_MODE'),
  1176. 'brightness' => Tools::getBrightness($bo_color) < 128 ? 'white' : '#383838',
  1177. 'bo_width' => (int)$this->context->employee->bo_width,
  1178. 'bo_color' => isset($this->context->employee->bo_color) ? Tools::htmlentitiesUTF8($this->context->employee->bo_color) : null,
  1179. 'show_new_orders' => Configuration::get('PS_SHOW_NEW_ORDERS') && $accesses['AdminOrders']['view'],
  1180. 'show_new_customers' => Configuration::get('PS_SHOW_NEW_CUSTOMERS') && $accesses['AdminCustomers']['view'],
  1181. 'show_new_messages' => Configuration::get('PS_SHOW_NEW_MESSAGES') && $accesses['AdminCustomerThreads']['view'],
  1182. 'first_name' => Tools::substr($this->context->employee->firstname, 0, 1),
  1183. 'last_name' => htmlentities($this->context->employee->lastname, ENT_COMPAT, 'UTF-8'),
  1184. 'employee' => $this->context->employee,
  1185. 'search_type' => Tools::getValue('bo_search_type'),
  1186. 'bo_query' => Tools::safeOutput(Tools::stripslashes(Tools::getValue('bo_query'))),
  1187. 'quick_access' => $quick_access,
  1188. 'multi_shop' => Shop::isFeatureActive(),
  1189. 'shop_list' => Helper::renderShopList(),
  1190. 'shop' => $this->context->shop,
  1191. 'shop_group' => new ShopGroup((int)Shop::getContextShopGroupID()),
  1192. 'current_parent_id' => (int)Tab::getCurrentParentId(),
  1193. 'tabs' => $tabs,
  1194. 'is_multishop' => $is_multishop,
  1195. 'multishop_context' => $this->multishop_context,
  1196. ));
  1197. }
  1198. $this->context->smarty->assign(array(
  1199. 'img_dir' => _PS_IMG_,
  1200. 'iso' => $this->context->language->iso_code,
  1201. 'class_name' => $this->className,
  1202. 'iso_user' => $this->context->language->iso_code,
  1203. 'country_iso_code' => $this->context->country->iso_code,
  1204. 'version' => _PS_VERSION_,
  1205. 'lang_iso' => $this->context->language->iso_code,
  1206. 'link' => $this->context->link,
  1207. 'shop_name' => Configuration::get('PS_SHOP_NAME'),
  1208. 'base_url' => $this->context->shop->getBaseURL(),
  1209. 'tab' => $tab, // Deprecated, this tab is declared in the foreach, so it's the last tab in the foreach
  1210. 'current_parent_id' => (int)Tab::getCurrentParentId(),
  1211. 'tabs' => $tabs,
  1212. 'install_dir_exists' => file_exists(_PS_ADMIN_DIR_.'/../install'),
  1213. 'pic_dir' => _THEME_PROD_PIC_DIR_,
  1214. 'controller_name' => htmlentities(Tools::getValue('controller')),
  1215. 'currentIndex' => self::$currentIndex
  1216. ));
  1217. }
  1218. /**
  1219. * Declare an action to use for each row in the list
  1220. */
  1221. public function addRowAction($action)
  1222. {
  1223. $action = strtolower($action);
  1224. $this->actions[] = $action;
  1225. }
  1226. /**
  1227. * Add an action to use for each row in the list
  1228. */
  1229. public function addRowActionSkipList($action, $list)
  1230. {
  1231. $action = strtolower($action);
  1232. $list = (array)$list;
  1233. if (array_key_exists($action, $this->list_skip_actions))
  1234. $this->list_skip_actions[$action] = array_merge($this->list_skip_actions[$action], $list);
  1235. else
  1236. $this->list_skip_actions[$action] = $list;
  1237. }
  1238. /**
  1239. * Assign smarty variables for all default views, list and form, then call other init functions
  1240. */
  1241. public function initContent()
  1242. {
  1243. if (!$this->viewAccess())
  1244. {
  1245. $this->errors[] = Tools::displayError('You do not have permission to view this.');
  1246. return;
  1247. }
  1248. $this->getLanguages();
  1249. // toolbar (save, cancel, new, ..)
  1250. $this->initToolbar();
  1251. $this->initTabModuleList();
  1252. if ($this->display == 'edit' || $this->display == 'add')
  1253. {
  1254. if (!$this->loadObject(true))
  1255. return;
  1256. $this->content .= $this->renderForm();
  1257. }
  1258. elseif ($this->display == 'view')
  1259. {
  1260. // Some controllers use the view action without an object
  1261. if ($this->className)
  1262. $this->loadObject(true);
  1263. $this->content .= $this->renderView();
  1264. }
  1265. elseif (!$this->ajax)
  1266. {
  1267. $this->content .= $this->renderModulesList();
  1268. $this->content .= $this->renderList();
  1269. $this->content .= $this->renderOptions();
  1270. // if we have to display the required fields form
  1271. if ($this->required_database)
  1272. $this->content .= $this->displayRequiredFields();
  1273. }
  1274. $this->context->smarty->assign(array(
  1275. 'content' => $this->content,
  1276. 'url_post' => self::$currentIndex.'&token='.$this->token,
  1277. ));
  1278. }
  1279. /**
  1280. * init tab modules list and add button in toolbar
  1281. */
  1282. protected function initTabModuleList()
  1283. {
  1284. if (!$this->isFresh(Module::CACHE_FILE_TAB_MODULES_LIST, 604800))
  1285. $this->refresh(Module::CACHE_FILE_TAB_MODULES_LIST, 'http://'.Tab::TAB_MODULE_LIST_URL);
  1286. $this->tab_modules_list = Tab::getTabModulesList($this->id);
  1287. if (is_array($this->tab_modules_list['default_list']) && count($this->tab_modules_list['default_list']))
  1288. $this->filter_modules_list = $this->tab_modules_list['default_list'];
  1289. elseif (is_array($this->tab_modules_list['slider_list']) && count($this->tab_modules_list['slider_list']))
  1290. {
  1291. $this->addToolBarModulesListButton();
  1292. $this->context->smarty->assign(array(
  1293. 'tab_modules_list' => implode(',', $this->tab_modules_list['slider_list']),
  1294. 'admin_module_ajax_url' => $this->context->link->getAdminLink('AdminModules'),
  1295. 'back_tab_modules_list' => $this->context->link->getAdminLink(Tools::getValue('controller')),
  1296. 'tab_modules_open' => (int)Tools::getValue('tab_modules_open')
  1297. ));
  1298. }
  1299. }
  1300. protected function addToolBarModulesListButton()
  1301. {
  1302. if (is_array($this->tab_modules_list['slider_list']) && count($this->tab_modules_list['slider_list']))
  1303. $this->toolbar_btn['modules-list'] = array(
  1304. 'href' => '#',
  1305. 'desc' => $this->l('Modules List')
  1306. );
  1307. }
  1308. /**
  1309. * initialize the invalid doom page of death
  1310. *
  1311. * @return void
  1312. */
  1313. public function initCursedPage()
  1314. {
  1315. $this->layout = 'invalid_token.tpl';
  1316. }
  1317. /**
  1318. * Assign smarty variables for the footer
  1319. */
  1320. public function initFooter()
  1321. {
  1322. // We assign js and css files on the last step before display template, because controller can add many js and css files
  1323. $this->context->smarty->assign('css_files', $this->css_files);
  1324. $this->context->smarty->assign('js_files', array_unique($this->js_files));
  1325. $this->context->smarty->assign(array(
  1326. 'ps_version' => _PS_VERSION_,
  1327. 'timer_start' => $this->timer_start,
  1328. 'iso_is_fr' => strtoupper($this->context->language->iso_code) == 'FR',
  1329. ));
  1330. }
  1331. public function renderModulesList()
  1332. {
  1333. if ($this->getModulesList($this->filter_modules_list))
  1334. {
  1335. $helper = new Helper();
  1336. return $helper->renderModulesList($this->modules_list);
  1337. }
  1338. }
  1339. /**
  1340. * Function used to render the list to display for this controller
  1341. */
  1342. public function renderList()
  1343. {
  1344. if (!($this->fields_list && is_array($this->fields_list)))
  1345. return false;
  1346. $this->getList($this->context->language->id);
  1347. $helper = new HelperList();
  1348. // Empty list is ok
  1349. if (!is_array($this->_list))
  1350. {
  1351. $this->displayWarning($this->l('Bad SQL query', 'Helper').'<br />'.htmlspecialchars($this->_list_error));
  1352. return false;
  1353. }
  1354. $this->setHelperDisplay($helper);
  1355. $helper->tpl_vars = $this->tpl_list_vars;
  1356. $helper->tpl_delete_link_vars = $this->tpl_delete_link_vars;
  1357. // For compatibility reasons, we have to check standard actions in class attributes
  1358. foreach ($this->actions_available as $action)
  1359. {
  1360. if (!in_array($action, $this->actions) && isset($this->$action) && $this->$action)
  1361. $this->actions[] = $action;
  1362. }
  1363. $helper->is_cms = $this->is_cms;
  1364. $list = $helper->generateList($this->_list, $this->fields_list);
  1365. return $list;
  1366. }
  1367. /**
  1368. * Override to render the view page
  1369. */
  1370. public function renderView()
  1371. {
  1372. $helper = new HelperView($this);
  1373. $this->setHelperDisplay($helper);
  1374. $helper->tpl_vars = $this->tpl_view_vars;
  1375. if (!is_null($this->base_tpl_view))
  1376. $helper->base_tpl = $this->base_tpl_view;
  1377. $view = $helper->generateView();
  1378. return $view;
  1379. }
  1380. /**
  1381. * Function used to render the form for this controller
  1382. */
  1383. public function renderForm()
  1384. {
  1385. if (!$this->default_form_language)
  1386. $this->getLanguages();
  1387. if (Tools::getValue('submitFormAjax'))
  1388. $this->content .= $this->context->smarty->fetch('form_submit_ajax.tpl');
  1389. if ($this->fields_form && is_array($this->fields_form))
  1390. {
  1391. if (!$this->multiple_fieldsets)
  1392. $this->fields_form = array(array('form' => $this->fields_form));
  1393. // For add a fields via an override of $fields_form, use $fields_form_override
  1394. if (is_array($this->fields_form_override) && !empty($this->fields_form_override))
  1395. $this->fields_form[0]['form']['input'][] = $this->fields_form_override;
  1396. $helper = new HelperForm($this);
  1397. $this->setHelperDisplay($helper);
  1398. $helper->fields_value = $this->getFieldsValue($this->object);
  1399. $helper->tpl_vars = $this->tpl_form_vars;
  1400. !is_null($this->base_tpl_form) ? $helper->base_tpl = $this->base_tpl_form : '';
  1401. if ($this->tabAccess['view'])
  1402. {
  1403. if (Tools::getValue('back'))
  1404. $helper->tpl_vars['back'] = Tools::safeOutput(Tools::getValue('back'));
  1405. else
  1406. $helper->tpl_vars['back'] = Tools::safeOutput(Tools::getValue(self::$currentIndex.'&token='.$this->token));
  1407. }
  1408. $form = $helper->generateForm($this->fields_form);
  1409. return $form;
  1410. }
  1411. }
  1412. /**
  1413. * Function used to render the options for this controller
  1414. */
  1415. public function renderOptions()
  1416. {
  1417. if ($this->fields_options && is_array($this->fields_options))
  1418. {
  1419. if (isset($this->display) && $this->display != 'options' && $this->display != 'list')
  1420. $this->show_toolbar = false;
  1421. else
  1422. $this->display = 'options';
  1423. unset($this->toolbar_btn);
  1424. $this->initToolbar();
  1425. $helper = new HelperOptions($this);
  1426. $this->setHelperDisplay($helper);
  1427. $helper->id = $this->id;
  1428. $helper->tpl_vars = $this->tpl_option_vars;
  1429. $options = $helper->generateOptions($this->fields_options);
  1430. return $options;
  1431. }
  1432. }
  1433. /**
  1434. * this function set various display option for helper list
  1435. *
  1436. * @param Helper $helper
  1437. * @return void
  1438. */
  1439. public function setHelperDisplay(Helper $helper)
  1440. {
  1441. if (empty($this->toolbar_title))
  1442. $this->initToolbarTitle();
  1443. // tocheck
  1444. if ($this->object && $this->object->id)
  1445. $helper->id = $this->object->id;
  1446. // @todo : move that in Helper
  1447. $helper->title = $this->toolbar_title;
  1448. $helper->toolbar_btn = $this->toolbar_btn;
  1449. $helper->show_toolbar = $this->show_toolbar;
  1450. $helper->toolbar_scroll = $this->toolbar_scroll;
  1451. $helper->override_folder = $this->tpl_folder;
  1452. $helper->actions = $this->actions;
  1453. $helper->simple_header = $this->list_simple_header;
  1454. $helper->bulk_actions = $this->bulk_actions;
  1455. $helper->currentIndex = self::$currentIndex;
  1456. $helper->className = $this->className;
  1457. $helper->table = $this->table;
  1458. $helper->name_controller = Tools::getValue('controller');
  1459. $helper->orderBy = $this->_orderBy;
  1460. $helper->orderWay = $this->_orderWay;
  1461. $helper->listTotal = $this->_listTotal;
  1462. $helper->shopLink = $this->shopLink;
  1463. $helper->shopLinkType = $this->shopLinkType;
  1464. $helper->identifier = $this->identifier;
  1465. $helper->token = $this->token;
  1466. $helper->languages = $this->_languages;
  1467. $helper->specificConfirmDelete = $this->specificConfirmDelete;
  1468. $helper->imageType = $this->imageType;
  1469. $helper->no_link = $this->list_no_link;
  1470. $helper->colorOnBackground = $this->colorOnBackground;
  1471. $helper->ajax_params = (isset($this->ajax_params) ? $this->ajax_params : null);
  1472. $helper->default_form_language = $this->default_form_language;
  1473. $helper->allow_employee_form_lang = $this->allow_employee_form_lang;
  1474. $helper->multiple_fieldsets = $this->multiple_fieldsets;
  1475. $helper->row_hover = $this->row_hover;
  1476. $helper->position_identifier = $this->position_identifier;
  1477. $helper->controller_name = $this->controller_name;
  1478. // For each action, try to add the corresponding skip elements list
  1479. $helper->list_skip_actions = $this->list_skip_actions;
  1480. $this->helper = $helper;
  1481. }
  1482. public function setMedia()
  1483. {
  1484. $this->addCSS(_PS_CSS_DIR_.'admin.css', 'all');
  1485. $admin_webpath = str_ireplace(_PS_ROOT_DIR_, '', _PS_ADMIN_DIR_);
  1486. $admin_webpath = preg_replace('/^'.preg_quote(DIRECTORY_SEPARATOR, '/').'/', '', $admin_webpath);
  1487. $this->addCSS(__PS_BASE_URI__.$admin_webpath.'/themes/'.$this->bo_theme.'/css/admin.css', 'all');
  1488. if ($this->context->language->is_rtl)
  1489. $this->addCSS(_THEME_CSS_DIR_.'rtl.css');
  1490. $this->addJquery();
  1491. $this->addjQueryPlugin(array('cluetip', 'hoverIntent', 'scrollTo', 'alerts', 'chosen'));
  1492. $this->addJS(array(
  1493. _PS_JS_DIR_.'admin.js',
  1494. _PS_JS_DIR_.'toggle.js',
  1495. _PS_JS_DIR_.'tools.js',
  1496. _PS_JS_DIR_.'ajax.js',
  1497. _PS_JS_DIR_.'toolbar.js'
  1498. ));
  1499. if (!Tools::getValue('submitFormAjax'))
  1500. {
  1501. $this->addJs(_PS_JS_DIR_.'notifications.js');
  1502. if (Configuration::get('PS_HELPBOX'))
  1503. $this->addJS(_PS_JS_DIR_.'helpAccess.js');
  1504. }
  1505. // Execute Hook AdminController SetMedia
  1506. Hook::exec('actionAdminControllerSetMedia', array());
  1507. }
  1508. /**
  1509. * non-static method which uses AdminController::translate()
  1510. *
  1511. * @param mixed $string term or expression in english
  1512. * @param string $class name of the class
  1513. * @param boolan $addslashes if set to true, the return value will pass through addslashes(). Otherwise, stripslashes().
  1514. * @param boolean $htmlentities if set to true(default), the return value will pass through htmlentities($string, ENT_QUOTES, 'utf-8')
  1515. * @return string the translation if available, or the english default text.
  1516. */
  1517. protected function l($string, $class = 'AdminTab', $addslashes = false, $htmlentities = true)
  1518. {
  1519. // classname has changed, from AdminXXX to AdminXXXController
  1520. // So we remove 10 characters and we keep same keys
  1521. if (strtolower(substr($class, -10)) == 'controller')
  1522. $class = substr($class, 0, -10);
  1523. elseif ($class == 'AdminTab')
  1524. $class = substr(get_class($this), 0, -10);
  1525. return Translate::getAdminTranslation($string, $class, $addslashes, $htmlentities);
  1526. }
  1527. /**
  1528. * Init context and dependencies, handles POST and GET
  1529. */
  1530. public function init()
  1531. {
  1532. // Has to be removed for the next Prestashop version
  1533. global $currentIndex;
  1534. parent::init();
  1535. if (Tools::getValue('ajax'))
  1536. $this->ajax = '1';
  1537. /* Server Params */
  1538. $protocol_link = (Tools::usingSecureMode() && Configuration::get('PS_SSL_ENABLED')) ? 'https://' : 'http://';
  1539. $protocol_content = (Tools::usingSecureMode() && Configuration::get('PS_SSL_ENABLED')) ? 'https://' : 'http://';
  1540. $this->context->link = new Link($protocol_link, $protocol_content);
  1541. if (isset($_GET['logout']))
  1542. $this->context->employee->logout();
  1543. if ($this->controller_name != 'AdminLogin' && (!isset($this->context->employee) || !$this->context->employee->isLoggedBack()))
  1544. Tools::redirectAdmin($this->context->link->getAdminLink('AdminLogin').((!isset($_GET['logout']) && $this->controller_name != 'AdminNotFound') ? '&redirect='.$this->controller_name : ''));
  1545. // Set current index
  1546. $current_index = 'index.php'.(($controller = Tools::getValue('controller')) ? '?controller='.$controller : '');
  1547. if ($back = Tools::getValue('back'))
  1548. $current_index .= '&back='.urlencode($back);
  1549. self::$currentIndex = $current_index;
  1550. $currentIndex = $current_index;
  1551. if ((int)Tools::getValue('liteDisplaying'))
  1552. {
  1553. $this->display_header = false;
  1554. $this->display_footer = false;
  1555. $this->content_only = false;
  1556. $this->lite_display = true;
  1557. }
  1558. if ($this->ajax && method_exists($this, 'ajaxPreprocess'))
  1559. $this->ajaxPreProcess();
  1560. $this->context->smarty->assign(array(
  1561. 'table' => $this->table,
  1562. 'current' => self::$currentIndex,
  1563. 'token' => $this->token,
  1564. 'stock_management' => (int)Configuration::get('PS_STOCK_MANAGEMENT')
  1565. ));
  1566. if ($this->display_header)
  1567. $this->context->smarty->assign('displayBackOfficeHeader', Hook::exec('displayBackOfficeHeader', array()));
  1568. $this->context->smarty->assign(
  1569. array(
  1570. 'displayBackOfficeTop' => Hook::exec('displayBackOfficeTop', array()),
  1571. 'submit_form_ajax' => (int)Tools::getValue('submitFormAjax')
  1572. )
  1573. );
  1574. $this->initProcess();
  1575. }
  1576. public function initShopContext()
  1577. {
  1578. if (!$this->context->employee->isLoggedBack())
  1579. return;
  1580. // Change shop context ?
  1581. if (Shop::isFeatureActive() && Tools::getValue('setShopContext') !== false)
  1582. {
  1583. $this->context->cookie->shopContext = Tools::getValue('setShopContext');
  1584. $url = parse_url($_SERVER['REQUEST_URI']);
  1585. $query = (isset($url['query'])) ? $url['query'] : '';
  1586. parse_str($query, $parse_query);
  1587. unset($parse_query['setShopContext'], $parse_query['conf']);
  1588. $this->redirect_after = $url['path'].'?'.http_build_query($parse_query, '', '&');
  1589. }
  1590. elseif (!Shop::isFeatureActive())
  1591. $this->context->cookie->shopContext = 's-'.Configuration::get('PS_SHOP_DEFAULT');
  1592. else if (Shop::getTotalShops(false, null) < 2)
  1593. $this->context->cookie->shopContext = 's-'.$this->context->employee->getDefaultShopID();
  1594. $shop_id = '';
  1595. Shop::setContext(Shop::CONTEXT_ALL);
  1596. if ($this->context->cookie->shopContext)
  1597. {
  1598. $split = explode('-', $this->context->cookie->shopContext);
  1599. if (count($split) == 2)
  1600. {
  1601. if ($split[0] == 'g')
  1602. {
  1603. if ($this->context->employee->hasAuthOnShopGroup($split[1]))
  1604. Shop::setContext(Shop::CONTEXT_GROUP, $split[1]);
  1605. else
  1606. {
  1607. $shop_id = $this->context->employee->getDefaultShopID();
  1608. Shop::setContext(Shop::CONTEXT_SHOP, $shop_id);
  1609. }
  1610. }
  1611. else if (Shop::getShop($split[1]) && $this->context->employee->hasAuthOnShop($split[1]))
  1612. {
  1613. $shop_id = $split[1];
  1614. Shop::setContext(Shop::CONTEXT_SHOP, $shop_id);
  1615. }
  1616. else
  1617. {
  1618. $shop_id = $this->context->employee->getDefaultShopID();
  1619. Shop::setContext(Shop::CONTEXT_SHOP, $shop_id);
  1620. }
  1621. }
  1622. }
  1623. // Check multishop context and set right context if need
  1624. if (!($this->multishop_context & Shop::getContext()))
  1625. {
  1626. if (Shop::getContext() == Shop::CONTEXT_SHOP && !($this->multishop_context & Shop::CONTEXT_SHOP))
  1627. Shop::setContext(Shop::CONTEXT_GROUP, Shop::getContextShopGroupID());
  1628. if (Shop::getContext() == Shop::CONTEXT_GROUP && !($this->multishop_context & Shop::CONTEXT_GROUP))
  1629. Shop::setContext(Shop::CONTEXT_ALL);
  1630. }
  1631. // Replace existing shop if necessary
  1632. if (!$shop_id)
  1633. $this->context->shop = new Shop(Configuration::get('PS_SHOP_DEFAULT'));
  1634. elseif ($this->context->shop->id != $shop_id)
  1635. $this->context->shop = new Shop($shop_id);
  1636. $this->initBreadcrumbs();
  1637. }
  1638. /**
  1639. * Retrieve GET and POST value and translate them to actions
  1640. */
  1641. public function initProcess()
  1642. {
  1643. // Manage list filtering
  1644. if (Tools::isSubmit('submitFilter'.$this->table)
  1645. || $this->context->cookie->{'submitFilter'.$this->table} !== false
  1646. || Tools::getValue($this->table.'Orderby')
  1647. || Tools::getValue($this->table.'Orderway'))
  1648. $this->filter = true;
  1649. $this->id_object = (int)Tools::getValue($this->identifier);
  1650. /* Delete object image */
  1651. if (isset($_GET['deleteImage']))
  1652. {
  1653. if ($this->tabAccess['delete'] === '1')
  1654. $this->action = 'delete_image';
  1655. else
  1656. $this->errors[] = Tools::displayError('You do not have permission to delete this.');
  1657. }
  1658. /* Delete object */
  1659. elseif (isset($_GET['delete'.$this->table]))
  1660. {
  1661. if ($this->tabAccess['delete'] === '1')
  1662. $this->action = 'delete';
  1663. else
  1664. $this->errors[] = Tools::displayError('You do not have permission to delete this.');
  1665. }
  1666. /* Change object statuts (active, inactive) */
  1667. elseif ((isset($_GET['status'.$this->table]) || isset($_GET['status'])) && Tools::getValue($this->identifier))
  1668. {
  1669. if ($this->tabAccess['edit'] === '1')
  1670. $this->action = 'status';
  1671. else
  1672. $this->errors[] = Tools::displayError('You do not have permission to edit this.');
  1673. }
  1674. /* Move an object */
  1675. elseif (isset($_GET['position']))
  1676. {
  1677. if ($this->tabAccess['edit'] == '1')
  1678. $this->action = 'position';
  1679. else
  1680. $this->errors[] = Tools::displayError('You do not have permission to edit this.');
  1681. }
  1682. elseif (Tools::getValue('submitAdd'.$this->table)
  1683. || Tools::getValue('submitAdd'.$this->table.'AndStay')
  1684. || Tools::getValue('submitAdd'.$this->table.'AndPreview'))
  1685. {
  1686. // case 1: updating existing entry
  1687. if ($this->id_object)
  1688. {
  1689. if ($this->tabAccess['edit'] === '1')
  1690. {
  1691. $this->action = 'save';
  1692. if (Tools::getValue('submitAdd'.$this->table.'AndStay'))
  1693. $this->display = 'edit';
  1694. else
  1695. $this->display = 'list';
  1696. }
  1697. else
  1698. $this->errors[] = Tools::displayError('You do not have permission to edit this.');
  1699. }
  1700. // case 2: creating new entry
  1701. else
  1702. {
  1703. if ($this->tabAccess['add'] === '1')
  1704. {
  1705. $this->action = 'save';
  1706. if (Tools::getValue('submitAdd'.$this->table.'AndStay'))
  1707. $this->display = 'edit';
  1708. else
  1709. $this->display = 'list';
  1710. }
  1711. else
  1712. $this->errors[] = Tools::displayError('You do not have permission to add this.');
  1713. }
  1714. }
  1715. elseif (isset($_GET['add'.$this->table]))
  1716. {
  1717. if ($this->tabAccess['add'] === '1')
  1718. {
  1719. $this->action = 'new';
  1720. $this->display = 'add';
  1721. }
  1722. else
  1723. $this->errors[] = Tools::displayError('You do not have permission to add this.');
  1724. }
  1725. elseif (isset($_GET['update'.$this->table]) && isset($_GET[$this->identifier]))
  1726. {
  1727. $this->display = 'edit';
  1728. if ($this->tabAccess['edit'] !== '1')
  1729. $this->errors[] = Tools::displayError('You do not have permission to edit this.');
  1730. }
  1731. elseif (isset($_GET['view'.$this->table]))
  1732. {
  1733. if ($this->tabAccess['view'] === '1')
  1734. {
  1735. $this->display = 'view';
  1736. $this->action = 'view';
  1737. }
  1738. else
  1739. $this->errors[] = Tools::displayError('You do not have permission to view this.');
  1740. }
  1741. elseif (isset($_GET['export'.$this->table]))
  1742. {
  1743. if ($this->tabAccess['view'] === '1')
  1744. $this->action = 'export';
  1745. }
  1746. /* Cancel all filters for this tab */
  1747. elseif (isset($_POST['submitReset'.$this->table]))
  1748. $this->action = 'reset_filters';
  1749. /* Submit options list */
  1750. elseif (Tools::getValue('submitOptions'.$this->table) || Tools::getValue('submitOptions'))
  1751. {
  1752. $this->display = 'options';
  1753. if ($this->tabAccess['edit'] === '1')
  1754. $this->action = 'update_options';
  1755. else
  1756. $this->errors[] = Tools::displayError('You do not have permission to edit this.');
  1757. }
  1758. elseif (Tools::isSubmit('submitFields') && $this->required_database && $this->tabAccess['add'] === '1' && $this->tabAccess['delete'] === '1')
  1759. $this->action = 'update_fields';
  1760. elseif (is_array($this->bulk_actions))
  1761. foreach ($this->bulk_actions as $bulk_action => $params)
  1762. {
  1763. if (Tools::isSubmit('submitBulk'.$bulk_action.$this->table) || Tools::isSubmit('submitBulk'.$bulk_action))
  1764. {
  1765. if ($this->tabAccess['edit'] === '1')
  1766. {
  1767. $this->action = 'bulk'.$bulk_action;
  1768. $this->boxes = Tools::getValue($this->table.'Box');
  1769. }
  1770. else
  1771. $this->errors[] = Tools::displayError('You do not have permission to edit this.');
  1772. break;
  1773. }
  1774. elseif (Tools::isSubmit('submitBulk'))
  1775. {
  1776. if ($this->tabAccess['edit'] === '1')
  1777. {
  1778. $this->action = 'bulk'.Tools::getValue('select_submitBulk');
  1779. $this->boxes = Tools::getValue($this->table.'Box');
  1780. }
  1781. else
  1782. $this->errors[] = Tools::displayError('You do not have permission to edit this.');
  1783. break;
  1784. }
  1785. }
  1786. elseif (!empty($this->fields_options) && empty($this->fields_list))
  1787. $this->display = 'options';
  1788. }
  1789. /**
  1790. * Get the current objects' list form the database
  1791. *
  1792. * @param integer $id_lang Language used for display
  1793. * @param string $order_by ORDER BY clause
  1794. * @param string $_orderWay Order way (ASC, DESC)
  1795. * @param integer $start Offset in LIMIT clause
  1796. * @param integer $limit Row count in LIMIT clause
  1797. */
  1798. public function getList($id_lang, $order_by = null, $order_way = null, $start = 0, $limit = null, $id_lang_shop = false)
  1799. {
  1800. /* Manage default params values */
  1801. $use_limit = true;
  1802. if ($limit === false)
  1803. $use_limit = false;
  1804. elseif (empty($limit))
  1805. {
  1806. if (isset($this->context->cookie->{$this->table.'_pagination'}) && $this->context->cookie->{$this->table.'_pagination'})
  1807. $limit = $this->context->cookie->{$this->table.'_pagination'};
  1808. else
  1809. $limit = $this->_pagination[1];
  1810. }
  1811. if (!Validate::isTableOrIdentifier($this->table))
  1812. throw new PrestaShopException(sprintf('Table name %s is invalid:', $this->table));
  1813. $prefix = str_replace(array('admin', 'controller'), '', Tools::strtolower(get_class($this)));
  1814. if (empty($order_by))
  1815. {
  1816. if ($this->context->cookie->{$prefix.$this->table.'Orderby'})
  1817. $order_by = $this->context->cookie->{$prefix.$this->table.'Orderby'};
  1818. elseif ($this->_orderBy)
  1819. $order_by = $this->_orderBy;
  1820. else
  1821. $order_by = $this->_defaultOrderBy;
  1822. }
  1823. if (empty($order_way))
  1824. {
  1825. if ($this->context->cookie->{$prefix.$this->table.'Orderway'})
  1826. $order_way = $this->context->cookie->{$prefix.$this->table.'Orderway'};
  1827. elseif ($this->_orderWay)
  1828. $order_way = $this->_orderWay;
  1829. else
  1830. $order_way = $this->_defaultOrderWay;
  1831. }
  1832. $limit = (int)Tools::getValue('pagination', $limit);
  1833. $this->context->cookie->{$this->table.'_pagination'} = $limit;
  1834. /* Check params validity */
  1835. if (!Validate::isOrderBy($order_by) || !Validate::isOrderWay($order_way)
  1836. || !is_numeric($start) || !is_numeric($limit)
  1837. || !Validate::isUnsignedId($id_lang))
  1838. throw new PrestaShopException('get list params is not valid');
  1839. if (isset($this->fields_list[$order_by]) && isset($this->fields_list[$order_by]['filter_key']))
  1840. $order_by = $this->fields_list[$order_by]['filter_key'];
  1841. /* Determine offset from current page */
  1842. if ((isset($_POST['submitFilter'.$this->table]) ||
  1843. isset($_POST['submitFilter'.$this->table.'_x']) ||
  1844. isset($_POST['submitFilter'.$this->table.'_y'])) &&
  1845. !empty($_POST['submitFilter'.$this->table]) &&
  1846. is_numeric($_POST['submitFilter'.$this->table]))
  1847. $start = ((int)$_POST['submitFilter'.$this->table] - 1) * $limit;
  1848. /* Cache */
  1849. $this->_lang = (int)$id_lang;
  1850. if (preg_match('/[.!]/', $order_by))
  1851. {
  1852. $order_by_split = preg_split('/[.!]/', $order_by);
  1853. $order_by = pSQL($order_by_split[0]).'.`'.pSQL($order_by_split[1]).'`';
  1854. $this->_orderBy = (isset($order_by_split) && isset($order_by_split[1])) ? $order_by_split[1] : $order_by;
  1855. }
  1856. else
  1857. $this->_orderBy = $order_by;
  1858. $this->_orderWay = Tools::strtoupper($order_way);
  1859. /* SQL table : orders, but class name is Order */
  1860. $sql_table = $this->table == 'order' ? 'orders' : $this->table;
  1861. // Add SQL shop restriction
  1862. $select_shop = $join_shop = $where_shop = '';
  1863. if ($this->shopLinkType)
  1864. {
  1865. $select_shop = ', shop.name as shop_name ';
  1866. $join_shop = ' LEFT JOIN '._DB_PREFIX_.$this->shopLinkType.' shop
  1867. ON a.id_'.$this->shopLinkType.' = shop.id_'.$this->shopLinkType;
  1868. $where_shop = Shop::addSqlRestriction($this->shopShareDatas, 'a', $this->shopLinkType);
  1869. }
  1870. if ($this->multishop_context && Shop::isTableAssociated($this->table) && !empty($this->className))
  1871. {
  1872. if (Shop::getContext() != Shop::CONTEXT_ALL || !$this->context->employee->isSuperAdmin())
  1873. {
  1874. $test_join = !preg_match('#`?'.preg_quote(_DB_PREFIX_.$this->table.'_shop').'`? *sa#', $this->_join);
  1875. if (Shop::isFeatureActive() && $test_join && Shop::isTableAssociated($this->table))
  1876. {
  1877. $this->_where .= ' AND a.'.$this->identifier.' IN (
  1878. SELECT sa.'.$this->identifier.'
  1879. FROM `'._DB_PREFIX_.$this->table.'_shop` sa
  1880. WHERE sa.id_shop IN ('.implode(', ', Shop::getContextListShopID()).')
  1881. )';
  1882. }
  1883. }
  1884. }
  1885. /* Query in order to get results with all fields */
  1886. $lang_join = '';
  1887. if ($this->lang)
  1888. {
  1889. $lang_join = 'LEFT JOIN `'._DB_PREFIX_.$this->table.'_lang` b ON (b.`'.$this->identifier.'` = a.`'.$this->identifier.'` AND b.`id_lang` = '.(int)$id_lang;
  1890. if ($id_lang_shop)
  1891. {
  1892. if (!Shop::isFeatureActive())
  1893. $lang_join .= ' AND b.`id_shop` = 1';
  1894. elseif (Shop::getContext() == Shop::CONTEXT_SHOP)
  1895. $lang_join .= ' AND b.`id_shop` = '.(int)$id_lang_shop;
  1896. else
  1897. $lang_join .= ' AND b.`id_shop` = a.id_shop_default';
  1898. }
  1899. $lang_join .= ')';
  1900. }
  1901. $having_clause = '';
  1902. if (isset($this->_filterHaving) || isset($this->_having))
  1903. {
  1904. $having_clause = ' HAVING ';
  1905. if (isset($this->_filterHaving))
  1906. $having_clause .= ltrim($this->_filterHaving, ' AND ');
  1907. if (isset($this->_having))
  1908. $having_clause .= $this->_having.' ';
  1909. }
  1910. $this->_listsql = '
  1911. SELECT SQL_CALC_FOUND_ROWS
  1912. '.($this->_tmpTableFilter ? ' * FROM (SELECT ' : '');
  1913. if ($this->explicitSelect)
  1914. {
  1915. foreach ($this->fields_list as $key => $array_value)
  1916. {
  1917. // Add it only if it is not already in $this->_select
  1918. if (isset($this->_select) && preg_match('/[\s]`?'.preg_quote($key, '/').'`?\s*,/', $this->_select))
  1919. continue;
  1920. if (isset($array_value['filter_key']))
  1921. $this->_listsql .= str_replace('!', '.', $array_value['filter_key']).' as '.$key.',';
  1922. elseif ($key == 'id_'.$this->table)
  1923. $this->_listsql .= 'a.`'.bqSQL($key).'`,';
  1924. elseif ($key != 'image' && !preg_match('/'.preg_quote($key, '/').'/i', $this->_select))
  1925. $this->_listsql .= '`'.bqSQL($key).'`,';
  1926. }
  1927. $this->_listsql = rtrim($this->_listsql, ',');
  1928. }
  1929. else
  1930. $this->_listsql .= ($this->lang ? 'b.*,' : '').' a.*';
  1931. $this->_listsql .= '
  1932. '.(isset($this->_select) ? ', '.$this->_select : '').$select_shop.'
  1933. FROM `'._DB_PREFIX_.$sql_table.'` a
  1934. '.$lang_join.'
  1935. '.(isset($this->_join) ? $this->_join.' ' : '').'
  1936. '.$join_shop.'
  1937. WHERE 1 '.(isset($this->_where) ? $this->_where.' ' : '').($this->deleted ? 'AND a.`deleted` = 0 ' : '').
  1938. (isset($this->_filter) ? $this->_filter : '').$where_shop.'
  1939. '.(isset($this->_group) ? $this->_group.' ' : '').'
  1940. '.$having_clause.'
  1941. ORDER BY '.(($order_by == $this->identifier) ? 'a.' : '').pSQL($order_by).' '.pSQL($order_way).
  1942. ($this->_tmpTableFilter ? ') tmpTable WHERE 1'.$this->_tmpTableFilter : '').
  1943. (($use_limit === true) ? ' LIMIT '.(int)$start.','.(int)$limit : '');
  1944. if (!($this->_list = Db::getInstance()->executeS($this->_listsql)))
  1945. $this->_list_error = Db::getInstance()->getMsgError();
  1946. else
  1947. $this->_listTotal = Db::getInstance()->getValue('SELECT FOUND_ROWS() AS `'._DB_PREFIX_.$this->table.'`');
  1948. }
  1949. public function getModulesList($filter_modules_list)
  1950. {
  1951. if (!is_array($filter_modules_list) && !is_null($filter_modules_list))
  1952. $filter_modules_list = array($filter_modules_list);
  1953. if (!count($filter_modules_list))
  1954. return false; //if there is no modules to display just return false;
  1955. $all_modules = Module::getModulesOnDisk(true);
  1956. $this->modules_list = array();
  1957. foreach($all_modules as $module)
  1958. {
  1959. $perm = true;
  1960. if ($module->id)
  1961. $perm &= Module::getPermissionStatic($module->id, 'configure');
  1962. else
  1963. {
  1964. $id_admin_module = Tab::getIdFromClassName('AdminModules');
  1965. $access = Profile::getProfileAccess($this->context->employee->id_profile, $id_admin_module);
  1966. if (!$access['edit'])
  1967. $perm &= false;
  1968. }
  1969. if (in_array($module->name, $filter_modules_list) && $perm)
  1970. {
  1971. $this->fillModuleData($module, 'select');
  1972. $this->modules_list[] = $module;
  1973. }
  1974. }
  1975. if (count($this->modules_list))
  1976. return true;
  1977. return false; //no module found on disk just return false;
  1978. }
  1979. public function getLanguages()
  1980. {
  1981. $cookie = $this->context->cookie;
  1982. $this->allow_employee_form_lang = Configuration::get('PS_BO_ALLOW_EMPLOYEE_FORM_LANG') ? Configuration::get('PS_BO_ALLOW_EMPLOYEE_FORM_LANG') : 0;
  1983. if ($this->allow_employee_form_lang && !$cookie->employee_form_lang)
  1984. $cookie->employee_form_lang = (int)Configuration::get('PS_LANG_DEFAULT');
  1985. $use_lang_from_cookie = false;
  1986. $this->_languages = Language::getLanguages(false);
  1987. if ($this->allow_employee_form_lang)
  1988. foreach ($this->_languages as $lang)
  1989. if ($cookie->employee_form_lang == $lang['id_lang'])
  1990. $use_lang_from_cookie = true;
  1991. if (!$use_lang_from_cookie)
  1992. $this->default_form_language = (int)Configuration::get('PS_LANG_DEFAULT');
  1993. else
  1994. $this->default_form_language = (int)$cookie->employee_form_lang;
  1995. foreach ($this->_languages as $k => $language)
  1996. $this->_languages[$k]['is_default'] = (int)($language['id_lang'] == $this->default_form_language);
  1997. return $this->_languages;
  1998. }
  1999. /**
  2000. * Return the list of fields value
  2001. *
  2002. * @param object $obj Object
  2003. * @return array
  2004. */
  2005. public function getFieldsValue($obj)
  2006. {
  2007. foreach ($this->fields_form as $fieldset)
  2008. if (isset($fieldset['form']['input']))
  2009. foreach ($fieldset['form']['input'] as $input)
  2010. if (!isset($this->fields_value[$input['name']]))
  2011. if (isset($input['type']) && $input['type'] == 'shop')
  2012. {
  2013. if ($obj->id)
  2014. {
  2015. $result = Shop::getShopById((int)$obj->id, $this->identifier, $this->table);
  2016. foreach ($result as $row)
  2017. $this->fields_value['shop'][$row['id_'.$input['type']]][] = $row['id_shop'];
  2018. }
  2019. }
  2020. elseif (isset($input['lang']) && $input['lang'])
  2021. foreach ($this->_languages as $language)
  2022. {
  2023. $fieldValue = $this->getFieldValue($obj, $input['name'], $language['id_lang']);
  2024. if (empty($fieldValue))
  2025. {
  2026. if (isset($input['default_value']) && is_array($input['default_value']) && isset($input['default_value'][$language['id_lang']]))
  2027. $fieldValue = $input['default_value'][$language['id_lang']];
  2028. elseif (isset($input['default_value']))
  2029. $fieldValue = $input['default_value'];
  2030. }
  2031. $this->fields_value[$input['name']][$language['id_lang']] = $fieldValue;
  2032. }
  2033. else
  2034. {
  2035. $fieldValue = $this->getFieldValue($obj, $input['name']);
  2036. if ($fieldValue === false && isset($input['default_value']))
  2037. $fieldValue = $input['default_value'];
  2038. $this->fields_value[$input['name']] = $fieldValue;
  2039. }
  2040. return $this->fields_value;
  2041. }
  2042. /**
  2043. * Return field value if possible (both classical and multilingual fields)
  2044. *
  2045. * Case 1 : Return value if present in $_POST / $_GET
  2046. * Case 2 : Return object value
  2047. *
  2048. * @param object $obj Object
  2049. * @param string $key Field name
  2050. * @param integer $id_lang Language id (optional)
  2051. * @return string
  2052. */
  2053. public function getFieldValue($obj, $key, $id_lang = null)
  2054. {
  2055. if ($id_lang)
  2056. $default_value = ($obj->id && isset($obj->{$key}[$id_lang])) ? $obj->{$key}[$id_lang] : false;
  2057. else
  2058. $default_value = isset($obj->{$key}) ? $obj->{$key} : false;
  2059. return Tools::getValue($key.($id_lang ? '_'.$id_lang : ''), $default_value);
  2060. }
  2061. /**
  2062. * Manage page display (form, list...)
  2063. *
  2064. * @param string $className Allow to validate a different class than the current one
  2065. */
  2066. public function validateRules($class_name = false)
  2067. {
  2068. if (!$class_name)
  2069. $class_name = $this->className;
  2070. /* Class specific validation rules */
  2071. if (!empty($class_name))
  2072. $rules = call_user_func(array($class_name, 'getValidationRules'), $class_name);
  2073. if (isset($rules) && count($rules) && (count($rules['requiredLang']) || count($rules['sizeLang']) || count($rules['validateLang'])))
  2074. {
  2075. /* Language() instance determined by default language */
  2076. $default_language = new Language((int)Configuration::get('PS_LANG_DEFAULT'));
  2077. /* All availables languages */
  2078. $languages = Language::getLanguages(false);
  2079. }
  2080. /* Checking for required fields */
  2081. if (isset($rules['required']) && is_array($rules['required']))
  2082. foreach ($rules['required'] as $field)
  2083. if (($value = Tools::getValue($field)) == false && (string)$value != '0')
  2084. if (!Tools::getValue($this->identifier) || ($field != 'passwd' && $field != 'no-picture'))
  2085. $this->errors[] = sprintf(
  2086. Tools::displayError('The %s field is required.'),
  2087. call_user_func(array($class_name, 'displayFieldName'), $field, $class_name)
  2088. );
  2089. /* Checking for multilingual required fields */
  2090. if (isset($rules['requiredLang']) && is_array($rules['requiredLang']))
  2091. foreach ($rules['requiredLang'] as $field_lang)
  2092. if (($empty = Tools::getValue($field_lang.'_'.$default_language->id)) === false || $empty !== '0' && empty($empty))
  2093. $this->errors[] = sprintf(
  2094. Tools::displayError('The field %1$s is required at least in %2$s.'),
  2095. call_user_func(array($class_name, 'displayFieldName'), $field_lang, $class_name),
  2096. $default_language->name
  2097. );
  2098. /* Checking for maximum fields sizes */
  2099. if (isset($rules['size']) && is_array($rules['size']))
  2100. foreach ($rules['size'] as $field => $max_length)
  2101. if (Tools::getValue($field) !== false && Tools::strlen(Tools::getValue($field)) > $max_length)
  2102. $this->errors[] = sprintf(
  2103. Tools::displayError('The %1$s field is too long (%2$d chars max).'),
  2104. call_user_func(array($class_name, 'displayFieldName'), $field, $class_name),
  2105. $max_length
  2106. );
  2107. /* Checking for maximum multilingual fields size */
  2108. if (isset($rules['sizeLang']) && is_array($rules['sizeLang']))
  2109. foreach ($rules['sizeLang'] as $field_lang => $max_length)
  2110. foreach ($languages as $language)
  2111. {
  2112. $field_lang_value = Tools::getValue($field_lang.'_'.$language['id_lang']);
  2113. if ($field_lang_value !== false && Tools::strlen($field_lang_value) > $max_length)
  2114. $this->errors[] = sprintf(
  2115. Tools::displayError('The field %1$s (%2$s) is too long (%3$d chars max, html chars including).'),
  2116. call_user_func(array($class_name, 'displayFieldName'), $field_lang, $class_name),
  2117. $language['name'],
  2118. $max_length
  2119. );
  2120. }
  2121. /* Overload this method for custom checking */
  2122. $this->_childValidation();
  2123. /* Checking for fields validity */
  2124. if (isset($rules['validate']) && is_array($rules['validate']))
  2125. foreach ($rules['validate'] as $field => $function)
  2126. if (($value = Tools::getValue($field)) !== false && ($field != 'passwd'))
  2127. if (!Validate::$function($value) && !empty($value))
  2128. $this->errors[] = sprintf(
  2129. Tools::displayError('The %s field is invalid.'),
  2130. call_user_func(array($class_name, 'displayFieldName'), $field, $class_name)
  2131. );
  2132. /* Checking for passwd_old validity */
  2133. if (($value = Tools::getValue('passwd')) != false)
  2134. {
  2135. if ($class_name == 'Employee' && !Validate::isPasswdAdmin($value))
  2136. $this->errors[] = sprintf(
  2137. Tools::displayError('The %s field is invalid.'),
  2138. call_user_func(array($class_name, 'displayFieldName'), 'passwd', $class_name)
  2139. );
  2140. elseif ($class_name == 'Customer' && !Validate::isPasswd($value))
  2141. $this->errors[] = sprintf(
  2142. Tools::displayError('The %s field is invalid.'),
  2143. call_user_func(array($class_name, 'displayFieldName'), 'passwd', $class_name)
  2144. );
  2145. }
  2146. /* Checking for multilingual fields validity */
  2147. if (isset($rules['validateLang']) && is_array($rules['validateLang']))
  2148. foreach ($rules['validateLang'] as $field_lang => $function)
  2149. foreach ($languages as $language)
  2150. if (($value = Tools::getValue($field_lang.'_'.$language['id_lang'])) !== false && !empty($value))
  2151. if (!Validate::$function($value))
  2152. $this->errors[] = sprintf(
  2153. Tools::displayError('The %1$s field (%2$s) is invalid.'),
  2154. call_user_func(array($class_name, 'displayFieldName'), $field_lang, $class_name),
  2155. $language['name']
  2156. );
  2157. }
  2158. /**
  2159. * Overload this method for custom checking
  2160. */
  2161. protected function _childValidation()
  2162. {
  2163. }
  2164. /**
  2165. * Display object details
  2166. */
  2167. public function viewDetails()
  2168. {
  2169. }
  2170. /**
  2171. * Called before deletion
  2172. *
  2173. * @param object $object Object
  2174. * @return boolean
  2175. */
  2176. protected function beforeDelete($object)
  2177. {
  2178. return false;
  2179. }
  2180. /**
  2181. * Called before deletion
  2182. *
  2183. * @param object $object Object
  2184. * @return boolean
  2185. */
  2186. protected function afterDelete($object, $oldId)
  2187. {
  2188. return true;
  2189. }
  2190. protected function afterAdd($object)
  2191. {
  2192. return true;
  2193. }
  2194. protected function afterUpdate($object)
  2195. {
  2196. return true;
  2197. }
  2198. /**
  2199. * Check rights to view the current tab
  2200. *
  2201. * @return boolean
  2202. */
  2203. protected function afterImageUpload()
  2204. {
  2205. return true;
  2206. }
  2207. /**
  2208. * Copy datas from $_POST to object
  2209. *
  2210. * @param object &$object Object
  2211. * @param string $table Object table
  2212. */
  2213. protected function copyFromPost(&$object, $table)
  2214. {
  2215. /* Classical fields */
  2216. foreach ($_POST as $key => $value)
  2217. if (key_exists($key, $object) && $key != 'id_'.$table)
  2218. {
  2219. /* Do not take care of password field if empty */
  2220. if ($key == 'passwd' && Tools::getValue('id_'.$table) && empty($value))
  2221. continue;
  2222. /* Automatically encrypt password in MD5 */
  2223. if ($key == 'passwd' && !empty($value))
  2224. $value = Tools::encrypt($value);
  2225. $object->{$key} = $value;
  2226. }
  2227. /* Multilingual fields */
  2228. $rules = call_user_func(array(get_class($object), 'getValidationRules'), get_class($object));
  2229. if (count($rules['validateLang']))
  2230. {
  2231. $languages = Language::getLanguages(false);
  2232. foreach ($languages as $language)
  2233. foreach (array_keys($rules['validateLang']) as $field)
  2234. if (isset($_POST[$field.'_'.(int)$language['id_lang']]))
  2235. $object->{$field}[(int)$language['id_lang']] = $_POST[$field.'_'.(int)$language['id_lang']];
  2236. }
  2237. }
  2238. /**
  2239. * Returns an array with selected shops and type (group or boutique shop)
  2240. *
  2241. * @param string $table
  2242. * @return array
  2243. */
  2244. protected function getSelectedAssoShop($table)
  2245. {
  2246. if (!Shop::isFeatureActive() || !Shop::isTableAssociated($table))
  2247. return array();
  2248. $shops = Shop::getShops(true, null, true);
  2249. if (count($shops) == 1 && isset($shops[0]))
  2250. return array($shops[0], 'shop');
  2251. $assos = array();
  2252. if (Tools::isSubmit('checkBoxShopAsso_'.$table))
  2253. foreach (Tools::getValue('checkBoxShopAsso_'.$table) as $id_shop => $value)
  2254. $assos[] = (int)$id_shop;
  2255. else if (Shop::getTotalShops(false) == 1)// if we do not have the checkBox multishop, we can have an admin with only one shop and being in multishop
  2256. $assos[] = (int)Shop::getContextShopID();
  2257. return $assos;
  2258. }
  2259. /**
  2260. * Update the associations of shops
  2261. *
  2262. * @param int $id_object
  2263. */
  2264. protected function updateAssoShop($id_object)
  2265. {
  2266. if (!Shop::isFeatureActive())
  2267. return;
  2268. if (!Shop::isTableAssociated($this->table))
  2269. return;
  2270. $assos_data = $this->getSelectedAssoShop($this->table, $id_object);
  2271. // Get list of shop id we want to exclude from asso deletion
  2272. $exclude_ids = $assos_data;
  2273. foreach (Db::getInstance()->executeS('SELECT id_shop FROM '._DB_PREFIX_.'shop') as $row)
  2274. if (!$this->context->employee->hasAuthOnShop($row['id_shop']))
  2275. $exclude_ids[] = $row['id_shop'];
  2276. Db::getInstance()->delete($this->table.'_shop', '`'.$this->identifier.'` = '.(int)$id_object.($exclude_ids ? ' AND id_shop NOT IN ('.implode(', ', $exclude_ids).')' : ''));
  2277. $insert = array();
  2278. foreach ($assos_data as $id_shop)
  2279. $insert[] = array(
  2280. $this->identifier => $id_object,
  2281. 'id_shop' => (int)$id_shop,
  2282. );
  2283. return Db::getInstance()->insert($this->table.'_shop', $insert, false, true, Db::INSERT_IGNORE);
  2284. }
  2285. protected function validateField($value, $field)
  2286. {
  2287. if (isset($field['validation']))
  2288. {
  2289. $valid_method_exists = method_exists('Validate', $field['validation']);
  2290. if ((!isset($field['empty']) || !$field['empty'] || (isset($field['empty']) && $field['empty'] && $value)) && $valid_method_exists)
  2291. {
  2292. if (!Validate::$field['validation']($value))
  2293. {
  2294. $this->errors[] = Tools::displayError($field['title'].' : Incorrect value');
  2295. return false;
  2296. }
  2297. }
  2298. }
  2299. return true;
  2300. }
  2301. /**
  2302. * Can be overriden
  2303. */
  2304. public function beforeUpdateOptions()
  2305. {
  2306. }
  2307. /**
  2308. * Overload this method for custom checking
  2309. *
  2310. * @param integer $id Object id used for deleting images
  2311. * @return boolean
  2312. */
  2313. protected function postImage($id)
  2314. {
  2315. if (isset($this->fieldImageSettings['name']) && isset($this->fieldImageSettings['dir']))
  2316. return $this->uploadImage($id, $this->fieldImageSettings['name'], $this->fieldImageSettings['dir'].'/');
  2317. elseif (!empty($this->fieldImageSettings))
  2318. foreach ($this->fieldImageSettings as $image)
  2319. if (isset($image['name']) && isset($image['dir']))
  2320. $this->uploadImage($id, $image['name'], $image['dir'].'/');
  2321. return !count($this->errors) ? true : false;
  2322. }
  2323. protected function uploadImage($id, $name, $dir, $ext = false, $width = null, $height = null)
  2324. {
  2325. if (isset($_FILES[$name]['tmp_name']) && !empty($_FILES[$name]['tmp_name']))
  2326. {
  2327. // Delete old image
  2328. if (Validate::isLoadedObject($object = $this->loadObject()))
  2329. $object->deleteImage();
  2330. else
  2331. return false;
  2332. // Check image validity
  2333. $max_size = isset($this->max_image_size) ? $this->max_image_size : 0;
  2334. if ($error = ImageManager::validateUpload($_FILES[$name], Tools::getMaxUploadSize($max_size)))
  2335. $this->errors[] = $error;
  2336. $tmp_name = tempnam(_PS_TMP_IMG_DIR_, 'PS');
  2337. if (!$tmp_name)
  2338. return false;
  2339. if (!move_uploaded_file($_FILES[$name]['tmp_name'], $tmp_name))
  2340. return false;
  2341. // Evaluate the memory required to resize the image: if it's too much, you can't resize it.
  2342. if (!ImageManager::checkImageMemoryLimit($tmp_name))
  2343. $this->errors[] = Tools::displayError('Due to memory limit restrictions, this image cannot be loaded. Please increase your memory_limit value via your server\'s configuration settings. ');
  2344. // Copy new image
  2345. if (empty($this->errors) && !ImageManager::resize($tmp_name, _PS_IMG_DIR_.$dir.$id.'.'.$this->imageType, (int)$width, (int)$height, ($ext ? $ext : $this->imageType)))
  2346. $this->errors[] = Tools::displayError('An error occurred while uploading the image.');
  2347. if (count($this->errors))
  2348. return false;
  2349. if ($this->afterImageUpload())
  2350. {
  2351. unlink($tmp_name);
  2352. return true;
  2353. }
  2354. return false;
  2355. }
  2356. return true;
  2357. }
  2358. /**
  2359. * Delete multiple items
  2360. *
  2361. * @return boolean true if succcess
  2362. */
  2363. protected function processBulkDelete()
  2364. {
  2365. if (is_array($this->boxes) && !empty($this->boxes))
  2366. {
  2367. $object = new $this->className();
  2368. if (isset($object->noZeroObject))
  2369. {
  2370. $objects_count = count(call_user_func(array($this->className, $object->noZeroObject)));
  2371. // Check if all object will be deleted
  2372. if ($objects_count <= 1 || count($this->boxes) == $objects_count)
  2373. $this->errors[] = Tools::displayError('You need at least one object.').
  2374. ' <b>'.$this->table.'</b><br />'.
  2375. Tools::displayError('You cannot delete all of the items.');
  2376. }
  2377. else
  2378. {
  2379. $result = true;
  2380. if ($this->deleted)
  2381. {
  2382. foreach ($this->boxes as $id)
  2383. {
  2384. $to_delete = new $this->className($id);
  2385. $to_delete->deleted = 1;
  2386. $result = $result && $to_delete->update();
  2387. }
  2388. }
  2389. else
  2390. $result = $object->deleteSelection(Tools::getValue($this->table.'Box'));
  2391. if ($result)
  2392. $this->redirect_after = self::$currentIndex.'&conf=2&token='.$this->token;
  2393. $this->errors[] = Tools::displayError('An error occurred while deleting this selection.');
  2394. }
  2395. }
  2396. else
  2397. $this->errors[] = Tools::displayError('You must select at least one element to delete.');
  2398. if (isset($result))
  2399. return $result;
  2400. else
  2401. return false;
  2402. }
  2403. /**
  2404. * Enable multiple items
  2405. *
  2406. * @return boolean true if succcess
  2407. */
  2408. protected function processBulkEnableSelection()
  2409. {
  2410. return $this->processBulkStatusSelection(1);
  2411. }
  2412. /**
  2413. * Disable multiple items
  2414. *
  2415. * @return boolean true if succcess
  2416. */
  2417. protected function processBulkDisableSelection()
  2418. {
  2419. return $this->processBulkStatusSelection(0);
  2420. }
  2421. /**
  2422. * Toggle status of multiple items
  2423. *
  2424. * @return boolean true if succcess
  2425. */
  2426. protected function processBulkStatusSelection($status)
  2427. {
  2428. $result = true;
  2429. if (is_array($this->boxes) && !empty($this->boxes))
  2430. {
  2431. foreach ($this->boxes as $id)
  2432. {
  2433. $object = new $this->className((int)$id);
  2434. $object->active = (int)$status;
  2435. $result &= $object->update();
  2436. }
  2437. }
  2438. return $result;
  2439. }
  2440. protected function processBulkAffectZone()
  2441. {
  2442. $result = false;
  2443. if (is_array($this->boxes) && !empty($this->boxes))
  2444. {
  2445. $object = new $this->className();
  2446. $result = $object->affectZoneToSelection(Tools::getValue($this->table.'Box'), Tools::getValue('zone_to_affect'));
  2447. if ($result)
  2448. $this->redirect_after = self::$currentIndex.'&conf=28&token='.$this->token;
  2449. $this->errors[] = Tools::displayError('An error occurred while affecting a zone to the selection.');
  2450. }
  2451. else
  2452. $this->errors[] = Tools::displayError('You must select at least one element to affect a new zone.');
  2453. return $result;
  2454. }
  2455. /**
  2456. * Called before Add
  2457. *
  2458. * @param object $object Object
  2459. * @return boolean
  2460. */
  2461. protected function beforeAdd($object)
  2462. {
  2463. return true;
  2464. }
  2465. /**
  2466. * prepare the view to display the required fields form
  2467. */
  2468. public function displayRequiredFields()
  2469. {
  2470. if (!$this->tabAccess['add'] || !$this->tabAccess['delete'] === '1' || !$this->required_database)
  2471. return;
  2472. $helper = new Helper();
  2473. $helper->currentIndex = self::$currentIndex;
  2474. $helper->token = $this->token;
  2475. return $helper->renderRequiredFields($this->className, $this->identifier, $this->required_fields);
  2476. }
  2477. /**
  2478. * Create a template from the override file, else from the base file.
  2479. *
  2480. * @param string $tpl_name filename
  2481. * @return Template
  2482. */
  2483. public function createTemplate($tpl_name)
  2484. {
  2485. // Use override tpl if it exists
  2486. // If view access is denied, we want to use the default template that will be used to display an error
  2487. if ($this->viewAccess() && $this->override_folder)
  2488. {
  2489. if (file_exists($this->context->smarty->getTemplateDir(1).DIRECTORY_SEPARATOR.$this->override_folder.$tpl_name))
  2490. return $this->context->smarty->createTemplate($this->override_folder.$tpl_name, $this->context->smarty);
  2491. else if (file_exists($this->context->smarty->getTemplateDir(0).'controllers'.DIRECTORY_SEPARATOR.$this->override_folder.$tpl_name))
  2492. return $this->context->smarty->createTemplate('controllers'.DIRECTORY_SEPARATOR.$this->override_folder.$tpl_name, $this->context->smarty);
  2493. }
  2494. return $this->context->smarty->createTemplate($this->context->smarty->getTemplateDir(0).$tpl_name, $this->context->smarty);
  2495. }
  2496. /**
  2497. * Shortcut to set up a json success payload
  2498. *
  2499. * @param $message success message
  2500. */
  2501. public function jsonConfirmation($message)
  2502. {
  2503. $this->json = true;
  2504. $this->confirmations[] = $message;
  2505. if ($this->status === '')
  2506. $this->status = 'ok';
  2507. }
  2508. /**
  2509. * Shortcut to set up a json error payload
  2510. *
  2511. * @param $message error message
  2512. */
  2513. public function jsonError($message)
  2514. {
  2515. $this->json = true;
  2516. $this->errors[] = $message;
  2517. if ($this->status === '')
  2518. $this->status = 'error';
  2519. }
  2520. public function isFresh($file, $timeout = 604800000)
  2521. {
  2522. if (file_exists(_PS_ROOT_DIR_.$file))
  2523. {
  2524. if (filesize(_PS_ROOT_DIR_.$file) < 1)
  2525. return false;
  2526. return ((time() - filemtime(_PS_ROOT_DIR_.$file)) < $timeout);
  2527. }
  2528. else
  2529. return false;
  2530. }
  2531. public function refresh($file_to_refresh, $external_file)
  2532. {
  2533. $content = Tools::file_get_contents($external_file);
  2534. if ($content)
  2535. return (bool)file_put_contents(_PS_ROOT_DIR_.$file_to_refresh, $content);
  2536. return false;
  2537. }
  2538. public function fillModuleData(&$module, $output_type = 'link', $back = null)
  2539. {
  2540. $obj = null;
  2541. if ($module->onclick_option)
  2542. $obj = new $module->name();
  2543. // Fill module data
  2544. $module->logo = '../../img/questionmark.png';
  2545. if (file_exists('../modules/'.$module->name.'/logo.gif'))
  2546. $module->logo = 'logo.gif';
  2547. if (file_exists('../modules/'.$module->name.'/logo.png'))
  2548. $module->logo = 'logo.png';
  2549. $module->optionsHtml = $this->displayModuleOptions($module, $output_type);
  2550. $link_admin_modules = $this->context->link->getAdminLink('AdminModules', true);
  2551. $module->options['install_url'] = $link_admin_modules.'&install='.urlencode($module->name).'&tab_module='.$module->tab.'&module_name='.$module->name.'&anchor=anchor'.ucfirst($module->name);
  2552. $module->options['update_url'] = $link_admin_modules.'&update='.urlencode($module->name).'&tab_module='.$module->tab.'&module_name='.$module->name.'&anchor=anchor'.ucfirst($module->name);
  2553. $module->options['uninstall_url'] = $link_admin_modules.'&uninstall='.urlencode($module->name).'&tab_module='.$module->tab.'&module_name='.$module->name.'&anchor=anchor'.ucfirst($module->name);
  2554. $module->options['uninstall_onclick'] = ((!$module->onclick_option) ?
  2555. ((empty($module->confirmUninstall)) ? '' : 'return confirm(\''.addslashes($module->confirmUninstall).'\');') :
  2556. $obj->onclickOption('uninstall', $module->options['uninstall_url']));
  2557. if ((Tools::getValue('module_name') == $module->name || in_array($module->name, explode('|', Tools::getValue('modules_list')))) && (int)Tools::getValue('conf') > 0)
  2558. $module->message = $this->_conf[(int)Tools::getValue('conf')];
  2559. if ((Tools::getValue('module_name') == $module->name || in_array($module->name, explode('|', Tools::getValue('modules_list')))) && (int)Tools::getValue('conf') > 0)
  2560. unset($obj);
  2561. }
  2562. /**
  2563. * Display modules list
  2564. *
  2565. * @param $module
  2566. * @param $output_type (link or select)
  2567. * @param $back
  2568. *
  2569. * @return string
  2570. */
  2571. protected $translationsTab = array();
  2572. public function displayModuleOptions($module, $output_type = 'link', $back = null)
  2573. {
  2574. if (!isset($this->translationsTab['Disable this module']))
  2575. {
  2576. $this->translationsTab['Disable this module'] = $this->l('Disable this module');
  2577. $this->translationsTab['Enable this module for all shops'] = $this->l('Enable this module for all shops');
  2578. $this->translationsTab['Disable'] = $this->l('Disable');
  2579. $this->translationsTab['Enable'] = $this->l('Enable');
  2580. $this->translationsTab['Reset'] = $this->l('Reset');
  2581. $this->translationsTab['Configure'] = $this->l('Configure');
  2582. $this->translationsTab['Delete'] = $this->l('Delete');
  2583. $this->translationsTab['Install'] = $this->l('Install');
  2584. $this->translationsTab['Uninstall'] = $this->l('Uninstall');
  2585. $this->translationsTab['This action will permanently remove the module from the server. Are you sure you want to do this?'] = $this->l('This action will permanently remove the module from the server. Are you sure you want to do this?');
  2586. }
  2587. $link_admin_modules = $this->context->link->getAdminLink('AdminModules', true);
  2588. $modules_options = array(
  2589. 'configure-module' => array(
  2590. 'href' => $link_admin_modules.'&configure='.urlencode($module->name).'&tab_module='.$module->tab.'&module_name='.urlencode($module->name),
  2591. 'onclick' => $module->onclick_option && isset($module->onclick_option_content['configure']) ? $module->onclick_option_content['configure'] : '',
  2592. 'title' => '',
  2593. 'text' => $this->translationsTab['Configure'],
  2594. 'cond' => $module->id && isset($module->is_configurable) && $module->is_configurable,
  2595. ),
  2596. 'desactive-module' => array(
  2597. 'href' => $link_admin_modules.'&module_name='.urlencode($module->name).'&'.($module->active ? 'enable=0' : 'enable=1').'&tab_module='.$module->tab,
  2598. 'onclick' => $module->active && $module->onclick_option && isset($module->onclick_option_content['desactive']) ? $module->onclick_option_content['desactive'] : '' ,
  2599. 'title' => Shop::isFeatureActive() ? htmlspecialchars($module->active ? $this->translationsTab['Disable this module'] : $this->translationsTab['Enable this module for all shops']) : '',
  2600. 'text' => $module->active ? $this->translationsTab['Disable'] : $this->translationsTab['Enable'],
  2601. 'cond' => $module->id,
  2602. ),
  2603. 'reset-module' => array(
  2604. 'href' => $link_admin_modules.'&module_name='.urlencode($module->name).'&reset&tab_module='.$module->tab,
  2605. 'onclick' => $module->onclick_option && isset($module->onclick_option_content['reset']) ? $module->onclick_option_content['reset'] : '',
  2606. 'title' => '',
  2607. 'text' => $this->translationsTab['Reset'],
  2608. 'cond' => $module->id && $module->active,
  2609. ),
  2610. 'delete-module' => array(
  2611. 'href' => $link_admin_modules.'&delete='.urlencode($module->name).'&tab_module='.$module->tab.'&module_name='.urlencode($module->name),
  2612. 'onclick' => $module->onclick_option && isset($module->onclick_option_content['delete']) ? $module->onclick_option_content['delete'] : 'return confirm(\''.$this->translationsTab['This action will permanently remove the module from the server. Are you sure you want to do this?'].'\');',
  2613. 'title' => '',
  2614. 'text' => $this->translationsTab['Delete'],
  2615. 'cond' => true,
  2616. ),
  2617. );
  2618. $return = '';
  2619. foreach ($modules_options as $option_name => $option)
  2620. {
  2621. if ($option['cond'])
  2622. {
  2623. if ($output_type == 'link')
  2624. $return .= '<span class="'.$option_name.'">
  2625. <a class="action_module" href="'.$option['href'].(!is_null($back) ? '&back='.urlencode($back) : '').'" onclick="'.$option['onclick'].'" title="'.$option['title'].'">'.$option['text'].'</a>
  2626. </span>';
  2627. elseif ($output_type == 'select')
  2628. $return .= '<option id="'.$option_name.'" data-href="'.$option['href'].(!is_null($back) ? '&back='.urlencode($back) : '').'" data-onclick="'.$option['onclick'].'">'.$option['text'].'</option>';
  2629. }
  2630. }
  2631. if ($output_type == 'select')
  2632. {
  2633. if (!$module->id)
  2634. $return = '<option data-onclick="" data-href="'.$link_admin_modules.'&install='.urlencode($module->name).'&tab_module='.$module->tab.'&module_name='.$module->name.'&anchor=anchor'.ucfirst($module->name).(!is_null($back) ? '&back='.urlencode($back) : '').'" >'.$this->translationsTab['Install'].'</option>'.$return;
  2635. else
  2636. $return .= '<option data-onclick="" data-href="'.$link_admin_modules.'&uninstall='.urlencode($module->name).'&tab_module='.$module->tab.'&module_name='.$module->name.'&anchor=anchor'.ucfirst($module->name).(!is_null($back) ? '&back='.urlencode($back) : '').'" >'.$this->translationsTab['Uninstall'].'</option>';
  2637. $return = '<select id="select_'.$module->name.'">'.$return.'</select>';
  2638. }
  2639. return $return;
  2640. }
  2641. }