PageRenderTime 78ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/classes/controller/AdminController.php

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