PageRenderTime 42ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 1ms

/classes/controller/AdminController.php

https://gitlab.com/mtellezgalindo/PrestaShop
PHP | 3971 lines | 2969 code | 494 blank | 508 comment | 783 complexity | 3b99df94b6eb36c743783b5b3652aa12 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-3.0
  1. <?php
  2. /*
  3. * 2007-2014 PrestaShop
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@prestashop.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
  18. * versions in the future. If you wish to customize PrestaShop for your
  19. * needs please refer to http://www.prestashop.com for more information.
  20. *
  21. * @author PrestaShop SA <contact@prestashop.com>
  22. * @copyright 2007-2014 PrestaShop SA
  23. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  24. * International Registered Trademark & Property of PrestaShop SA
  25. */
  26. class AdminControllerCore extends Controller
  27. {
  28. public $path;
  29. public static $currentIndex;
  30. public $content;
  31. public $warnings = array();
  32. public $informations = array();
  33. public $confirmations = array();
  34. public $shopShareDatas = false;
  35. public $_languages = array();
  36. public $default_form_language;
  37. public $allow_employee_form_lang;
  38. public $layout = 'layout.tpl';
  39. public $bootstrap = false;
  40. protected $meta_title;
  41. public $template = 'content.tpl';
  42. /** @var string Associated table name */
  43. public $table = 'configuration';
  44. public $list_id;
  45. /** @var string Object identifier inside the associated table */
  46. protected $identifier = false;
  47. protected $identifier_name = 'name';
  48. /** @var string Tab name */
  49. public $className;
  50. /** @var array tabAccess */
  51. public $tabAccess;
  52. /** @var integer Tab id */
  53. public $id = -1;
  54. public $required_database = false;
  55. /** @var string Security token */
  56. public $token;
  57. /** @var string shop | group_shop */
  58. public $shopLinkType;
  59. /** @var string Default ORDER BY clause when $_orderBy is not defined */
  60. protected $_defaultOrderBy = false;
  61. protected $_defaultOrderWay = 'ASC';
  62. public $tpl_form_vars = array();
  63. public $tpl_list_vars = array();
  64. public $tpl_delete_link_vars = array();
  65. public $tpl_option_vars = array();
  66. public $tpl_view_vars = array();
  67. public $tpl_required_fields_vars = array();
  68. public $base_tpl_view = null;
  69. public $base_tpl_form = null;
  70. /** @var bool if you want more fieldsets in the form */
  71. public $multiple_fieldsets = false;
  72. public $fields_value = false;
  73. /** @var array Errors displayed after post processing */
  74. public $errors = array();
  75. /** @var define if the header of the list contains filter and sorting links or not */
  76. protected $list_simple_header;
  77. /** @var array list to be generated */
  78. protected $fields_list;
  79. /** @var array modules list filters */
  80. protected $filter_modules_list = null;
  81. /** @var array modules list filters */
  82. protected $modules_list = array();
  83. /** @var array edit form to be generated */
  84. protected $fields_form;
  85. /** @var override of $fields_form */
  86. protected $fields_form_override;
  87. /** @var override form action */
  88. protected $submit_action;
  89. /** @var array list of option forms to be generated */
  90. protected $fields_options = array();
  91. protected $shopLink;
  92. /** @var string SQL query */
  93. protected $_listsql = '';
  94. /** @var array Cache for query results */
  95. protected $_list = array();
  96. /** @var define if the header of the list contains filter and sorting links or not */
  97. protected $toolbar_title;
  98. /** @var array list of toolbar buttons */
  99. protected $toolbar_btn = null;
  100. /** @var boolean scrolling toolbar */
  101. protected $toolbar_scroll = true;
  102. /** @var boolean set to false to hide toolbar and page title */
  103. protected $show_toolbar = true;
  104. /** @var boolean set to true to show toolbar and page title for options */
  105. protected $show_toolbar_options = false;
  106. /** @var integer Number of results in list */
  107. protected $_listTotal = 0;
  108. /** @var boolean Automatically join language table if true */
  109. public $lang = false;
  110. /** @var array WHERE clause determined by filter fields */
  111. protected $_filter;
  112. protected $_filterHaving;
  113. /** @var array Temporary SQL table WHERE clause determinated by filter fields */
  114. protected $_tmpTableFilter = '';
  115. /** @var array Number of results in list per page (used in select field) */
  116. protected $_pagination = array(20, 50, 100, 300, 1000);
  117. /** @var integer Default number of results in list per page */
  118. protected $_default_pagination = 50;
  119. /** @var string ORDER BY clause determined by field/arrows in list header */
  120. protected $_orderBy;
  121. /** @var string Order way (ASC, DESC) determined by arrows in list header */
  122. protected $_orderWay;
  123. /** @var array list of available actions for each list row - default actions are view, edit, delete, duplicate */
  124. protected $actions_available = array('view', 'edit', 'duplicate', 'delete');
  125. /** @var array list of required actions for each list row */
  126. protected $actions = array();
  127. /** @var array list of row ids associated with a given action for witch this action have to not be available */
  128. protected $list_skip_actions = array();
  129. /* @var boolean don't show header & footer */
  130. protected $lite_display = false;
  131. /** @var bool boolean List content lines are clickable if true */
  132. protected $list_no_link = false;
  133. protected $allow_export = false;
  134. /** @var array $cache_lang cache for traduction */
  135. public static $cache_lang = array();
  136. /** @var array required_fields to display in the Required Fields form */
  137. public $required_fields = array();
  138. /** @var Helper */
  139. protected $helper;
  140. /**
  141. * @var array actions to execute on multiple selections
  142. * Usage:
  143. * array(
  144. * 'actionName' => array(
  145. * 'text' => $this->l('Message displayed on the submit button (mandatory)'),
  146. * 'confirm' => $this->l('If set, this confirmation message will pop-up (optional)')),
  147. * 'anotherAction' => array(...)
  148. * );
  149. *
  150. * If your action is named 'actionName', you need to have a method named bulkactionName() that will be executed when the button is clicked.
  151. */
  152. protected $bulk_actions;
  153. /**
  154. * @var array ids of the rows selected
  155. */
  156. protected $boxes;
  157. /** @var string Do not automatically select * anymore but select only what is necessary */
  158. protected $explicitSelect = false;
  159. /** @var string Add fields into data query to display list */
  160. protected $_select;
  161. /** @var string Join tables into data query to display list */
  162. protected $_join;
  163. /** @var string Add conditions into data query to display list */
  164. protected $_where;
  165. /** @var string Group rows into data query to display list */
  166. protected $_group;
  167. /** @var string Having rows into data query to display list */
  168. protected $_having;
  169. protected $is_cms = false;
  170. /** @var string identifier to use for changing positions in lists (can be omitted if positions cannot be changed) */
  171. protected $position_identifier;
  172. protected $position_group_identifier;
  173. /** @var boolean Table records are not deleted but marked as deleted if set to true */
  174. protected $deleted = false;
  175. /**
  176. * @var bool is a list filter set
  177. */
  178. protected $filter;
  179. protected $noLink;
  180. protected $specificConfirmDelete = null;
  181. protected $colorOnBackground;
  182. /** @var bool If true, activates color on hover */
  183. protected $row_hover = true;
  184. /** @string Action to perform : 'edit', 'view', 'add', ... */
  185. protected $action;
  186. protected $display;
  187. protected $_includeContainer = true;
  188. protected $tab_modules_list = array('default_list' => array(), 'slider_list' => array());
  189. public $tpl_folder;
  190. protected $bo_theme;
  191. /** @var bool Redirect or not ater a creation */
  192. protected $_redirect = true;
  193. /** @var array Name and directory where class image are located */
  194. public $fieldImageSettings = array();
  195. /** @var string Image type */
  196. public $imageType = 'jpg';
  197. /** @var instanciation of the class associated with the AdminController */
  198. protected $object;
  199. /** @var int current object ID */
  200. protected $id_object;
  201. /**
  202. * @var current controller name without suffix
  203. */
  204. public $controller_name;
  205. public $multishop_context = -1;
  206. public $multishop_context_group = true;
  207. /**
  208. * Current breadcrumb position as an array of tab names
  209. */
  210. protected $breadcrumbs;
  211. /* Bootstrap variable */
  212. public $show_page_header_toolbar = false;
  213. public $page_header_toolbar_title;
  214. public $page_header_toolbar_btn = array();
  215. public $show_form_cancel_button;
  216. public $admin_webpath;
  217. protected $list_natives_modules = array();
  218. protected $list_partners_modules = array();
  219. public $modals = array();
  220. protected $logged_on_addons = false;
  221. public function __construct()
  222. {
  223. global $timer_start;
  224. $this->timer_start = $timer_start;
  225. // Has to be remove for the next Prestashop version
  226. global $token;
  227. $this->controller_type = 'admin';
  228. $this->controller_name = get_class($this);
  229. if (strpos($this->controller_name, 'Controller'))
  230. $this->controller_name = substr($this->controller_name, 0, -10);
  231. parent::__construct();
  232. if ($this->multishop_context == -1)
  233. $this->multishop_context = Shop::CONTEXT_ALL | Shop::CONTEXT_GROUP | Shop::CONTEXT_SHOP;
  234. $this->bo_theme = ((Validate::isLoadedObject($this->context->employee) && $this->context->employee->bo_theme) ? $this->context->employee->bo_theme : 'default');
  235. if (!file_exists(_PS_BO_ALL_THEMES_DIR_.$this->bo_theme.DIRECTORY_SEPARATOR.'template'))
  236. $this->bo_theme = 'default';
  237. $this->bo_css = ((Validate::isLoadedObject($this->context->employee) && $this->context->employee->bo_css) ? $this->context->employee->bo_css : 'admin-theme.css');
  238. if (!file_exists(_PS_BO_ALL_THEMES_DIR_.$this->bo_theme.DIRECTORY_SEPARATOR.'css'.DIRECTORY_SEPARATOR.$this->bo_css))
  239. $this->bo_css = 'admin-theme.css';
  240. $this->context->smarty->setTemplateDir(array(
  241. _PS_BO_ALL_THEMES_DIR_.$this->bo_theme.DIRECTORY_SEPARATOR.'template',
  242. _PS_OVERRIDE_DIR_.'controllers'.DIRECTORY_SEPARATOR.'admin'.DIRECTORY_SEPARATOR.'templates'
  243. ));
  244. $this->id = Tab::getIdFromClassName($this->controller_name);
  245. $this->token = Tools::getAdminToken($this->controller_name.(int)$this->id.(int)$this->context->employee->id);
  246. $token = $this->token;
  247. $this->_conf = array(
  248. 1 => $this->l('Successful deletion'),
  249. 2 => $this->l('The selection has been successfully deleted.'),
  250. 3 => $this->l('Successful creation'),
  251. 4 => $this->l('Successful update'),
  252. 5 => $this->l('The status has been successfully updated.'),
  253. 6 => $this->l('The settings have been successfully updated.'),
  254. 7 => $this->l('The image was successfully deleted.'),
  255. 8 => $this->l('The module was successfully downloaded.'),
  256. 9 => $this->l('The thumbnails were successfully regenerated.'),
  257. 10 => $this->l('The message was successfully sent to the customer.'),
  258. 11 => $this->l('Comment successfully added'),
  259. 12 => $this->l('Module(s) installed successfully.'),
  260. 13 => $this->l('Module(s) uninstalled successfully.'),
  261. 14 => $this->l('The translation was successfully copied.'),
  262. 15 => $this->l('The translations have been successfully added.'),
  263. 16 => $this->l('The module transplanted successfully to the hook.'),
  264. 17 => $this->l('The module was successfully removed from the hook.'),
  265. 18 => $this->l('Successful upload'),
  266. 19 => $this->l('Duplication was completed successfully.'),
  267. 20 => $this->l('The translation was added successfully, but the language has not been created.'),
  268. 21 => $this->l('Module reset successfully.'),
  269. 22 => $this->l('Module deleted successfully.'),
  270. 23 => $this->l('Localization pack imported successfully.'),
  271. 24 => $this->l('Localization pack imported successfully.'),
  272. 25 => $this->l('The selected images have successfully been moved.'),
  273. 26 => $this->l('Your cover image selection has been saved.'),
  274. 27 => $this->l('The image\'s shop association has been modified.'),
  275. 28 => $this->l('A zone has been assigned to the selection successfully.'),
  276. 29 => $this->l('Successful upgrade'),
  277. 30 => $this->l('A partial refund was successfully created.'),
  278. 31 => $this->l('The discount was successfully generated.'),
  279. 32 => $this->l('Successfully signed in to PrestaShop Addons')
  280. );
  281. if (!$this->identifier) $this->identifier = 'id_'.$this->table;
  282. if (!$this->_defaultOrderBy) $this->_defaultOrderBy = $this->identifier;
  283. $this->tabAccess = Profile::getProfileAccess($this->context->employee->id_profile, $this->id);
  284. // Fix for homepage
  285. if ($this->controller_name == 'AdminDashboard')
  286. $_POST['token'] = $this->token;
  287. if (!Shop::isFeatureActive())
  288. $this->shopLinkType = '';
  289. //$this->base_template_folder = _PS_BO_ALL_THEMES_DIR_.$this->bo_theme.'/template';
  290. $this->override_folder = Tools::toUnderscoreCase(substr($this->controller_name, 5)).'/';
  291. // Get the name of the folder containing the custom tpl files
  292. $this->tpl_folder = Tools::toUnderscoreCase(substr($this->controller_name, 5)).'/';
  293. $this->initShopContext();
  294. $this->context->currency = new Currency(Configuration::get('PS_CURRENCY_DEFAULT'));
  295. $this->admin_webpath = str_ireplace(_PS_CORE_DIR_, '', _PS_ADMIN_DIR_);
  296. $this->admin_webpath = preg_replace('/^'.preg_quote(DIRECTORY_SEPARATOR, '/').'/', '', $this->admin_webpath);
  297. // Check if logged on Addons
  298. $this->logged_on_addons = false;
  299. if (isset($this->context->cookie->username_addons) && isset($this->context->cookie->password_addons) && !empty($this->context->cookie->username_addons) && !empty($this->context->cookie->password_addons))
  300. $this->logged_on_addons = true;
  301. // Set context mode
  302. if (defined('_PS_HOST_MODE_') && _PS_HOST_MODE_)
  303. {
  304. if (isset($this->context->cookie->is_contributor) && (int)$this->context->cookie->is_contributor === 1)
  305. $this->context->mode = Context::MODE_HOST_CONTRIB;
  306. else
  307. $this->context->mode = Context::MODE_HOST;
  308. }
  309. elseif (isset($this->context->cookie->is_contributor) && (int)$this->context->cookie->is_contributor === 1)
  310. $this->context->mode = Context::MODE_CONTRIB;
  311. else
  312. $this->context->mode = Context::MODE_STD;
  313. $this->context->smarty->assign(array(
  314. 'context_mode' => $this->context->mode,
  315. 'logged_on_addons' => $this->logged_on_addons,
  316. ));
  317. }
  318. /**
  319. * Set breadcrumbs array for the controller page
  320. */
  321. public function initBreadcrumbs($tab_id = null, $tabs = null)
  322. {
  323. if (is_array($tabs) || count($tabs))
  324. $tabs = array();
  325. if (is_null($tab_id))
  326. $tab_id = $this->id;
  327. $tabs = Tab::recursiveTab($tab_id, $tabs);
  328. $dummy = array('name' => '', 'href' => '', 'icon' => '');
  329. $breadcrumbs2 = array(
  330. 'container' => $dummy,
  331. 'tab' => $dummy,
  332. 'action' => $dummy
  333. );
  334. if (isset($tabs[0]))
  335. {
  336. $breadcrumbs2['tab']['name'] = $tabs[0]['name'];
  337. $breadcrumbs2['tab']['href'] = __PS_BASE_URI__.basename(_PS_ADMIN_DIR_ ).'/'.$this->context->link->getAdminLink($tabs[0]['class_name']);
  338. if (!isset($tabs[1]))
  339. $breadcrumbs2['tab']['icon'] = 'icon-'.$tabs[0]['class_name'];
  340. }
  341. if (isset($tabs[1]))
  342. {
  343. $breadcrumbs2['container']['name'] = $tabs[1]['name'];
  344. $breadcrumbs2['container']['href'] = __PS_BASE_URI__.basename(_PS_ADMIN_DIR_ ).'/'.$this->context->link->getAdminLink($tabs[1]['class_name']);
  345. $breadcrumbs2['container']['icon'] = 'icon-'.$tabs[1]['class_name'];
  346. }
  347. /* content, edit, list, add, details, options, view */
  348. switch ($this->display)
  349. {
  350. case 'add':
  351. $breadcrumbs2['action']['name'] = $this->l('Add', null, null, false);
  352. $breadcrumbs2['action']['icon'] = 'icon-plus';
  353. break;
  354. case 'edit':
  355. $breadcrumbs2['action']['name'] = $this->l('Edit', null, null, false);
  356. $breadcrumbs2['action']['icon'] = 'icon-pencil';
  357. break;
  358. case '':
  359. case 'list':
  360. $breadcrumbs2['action']['name'] = $this->l('List', null, null, false);
  361. $breadcrumbs2['action']['icon'] = 'icon-th-list';
  362. break;
  363. case 'details':
  364. case 'view':
  365. $breadcrumbs2['action']['name'] = $this->l('View details', null, null, false);
  366. $breadcrumbs2['action']['icon'] = 'icon-zoom-in';
  367. break;
  368. case 'options':
  369. $breadcrumbs2['action']['name'] = $this->l('Options', null, null, false);
  370. $breadcrumbs2['action']['icon'] = 'icon-cogs';
  371. break;
  372. case 'generator':
  373. $breadcrumbs2['action']['name'] = $this->l('Generator', null, null, false);
  374. $breadcrumbs2['action']['icon'] = 'icon-flask';
  375. break;
  376. }
  377. $this->context->smarty->assign(array(
  378. 'breadcrumbs2' => $breadcrumbs2,
  379. 'quick_access_current_link_name' => $breadcrumbs2['tab']['name'].(isset($breadcrumbs2['action']) ? ' - '.$breadcrumbs2['action']['name'] : ''),
  380. 'quick_access_current_link_icon' => $breadcrumbs2['container']['icon']
  381. ));
  382. /* BEGIN - Backward compatibility < 1.6.0.3 */
  383. $this->breadcrumbs[] = $tabs[0]['name'];
  384. $navigationPipe = (Configuration::get('PS_NAVIGATION_PIPE') ? Configuration::get('PS_NAVIGATION_PIPE') : '>');
  385. $this->context->smarty->assign('navigationPipe', $navigationPipe);
  386. /* END - Backward compatibility < 1.6.0.3 */
  387. }
  388. /**
  389. * set default toolbar_title to admin breadcrumb
  390. *
  391. * @return void
  392. */
  393. public function initToolbarTitle()
  394. {
  395. $this->toolbar_title = is_array($this->breadcrumbs) ? array_unique($this->breadcrumbs) : array($this->breadcrumbs);
  396. switch ($this->display)
  397. {
  398. case 'edit':
  399. $this->toolbar_title[] = $this->l('Edit', null, null, false);
  400. break;
  401. case 'add':
  402. $this->toolbar_title[] = $this->l('Add new', null, null, false);
  403. break;
  404. case 'view':
  405. $this->toolbar_title[] = $this->l('View', null, null, false);
  406. break;
  407. }
  408. if ($filter = $this->addFiltersToBreadcrumbs())
  409. $this->toolbar_title[] = $filter;
  410. }
  411. public function addFiltersToBreadcrumbs()
  412. {
  413. if ($this->filter && is_array($this->fields_list))
  414. {
  415. $filters = array();
  416. foreach ($this->fields_list as $field => $t)
  417. {
  418. if (isset($t['filter_key']))
  419. $field = $t['filter_key'];
  420. if ($val = Tools::getValue($this->table.'Filter_'.$field))
  421. {
  422. if (!is_array($val))
  423. {
  424. $filter_value = '';
  425. if (isset($t['type']) && $t['type'] == 'bool')
  426. $filter_value = ((bool)$val) ? $this->l('yes') : $this->l('no');
  427. elseif (is_string($val))
  428. $filter_value = htmlspecialchars($val, ENT_QUOTES, 'UTF-8');
  429. if (!empty($filter_value))
  430. $filters[] = sprintf($this->l('%s: %s'), $t['title'], $filter_value);
  431. }
  432. else
  433. {
  434. $filter_value = '';
  435. foreach ($val as $v)
  436. if (is_string($v) && !empty($v))
  437. $filter_value .= ' - '.htmlspecialchars($v, ENT_QUOTES, 'UTF-8');
  438. $filter_value = ltrim($filter_value, ' -');
  439. if (!empty($filter_value))
  440. $filters[] = sprintf($this->l('%s: %s'), $t['title'], $filter_value);
  441. }
  442. }
  443. }
  444. if (count($filters))
  445. return sprintf($this->l('filter by %s'), implode(', ', $filters));
  446. }
  447. }
  448. /**
  449. * Check rights to view the current tab
  450. *
  451. * @param bool $disable
  452. * @return boolean
  453. */
  454. public function viewAccess($disable = false)
  455. {
  456. if ($disable)
  457. return true;
  458. if ($this->tabAccess['view'] === '1')
  459. return true;
  460. return false;
  461. }
  462. /**
  463. * Check for security token
  464. */
  465. public function checkToken()
  466. {
  467. $token = Tools::getValue('token');
  468. if (!empty($token) && $token === $this->token)
  469. return true;
  470. if (count($_POST) || !isset($_GET['controller']) || !Validate::isControllerName($_GET['controller']) || $token)
  471. return false;
  472. foreach ($_GET as $key => $value)
  473. if (is_array($value) || !in_array($key, array('controller', 'controllerUri')))
  474. return false;
  475. $cookie = Context::getContext()->cookie;
  476. $whitelist = array('date_add', 'id_lang', 'id_employee', 'email', 'profile', 'passwd', 'remote_addr', 'shopContext', 'collapse_menu', 'checksum');
  477. foreach ($cookie->getAll() as $key => $value)
  478. if (!in_array($key, $whitelist))
  479. unset($cookie->$key);
  480. $cookie->write();
  481. return true;
  482. }
  483. /**
  484. * Set the filters used for the list display
  485. */
  486. public function processFilter()
  487. {
  488. if (!isset($this->list_id))
  489. $this->list_id = $this->table;
  490. $prefix = str_replace(array('admin', 'controller'), '', Tools::strtolower(get_class($this)));
  491. if (isset($this->list_id))
  492. {
  493. foreach ($_POST as $key => $value)
  494. {
  495. if ($value === '')
  496. unset($this->context->cookie->{$prefix.$key});
  497. elseif (stripos($key, $this->list_id.'Filter_') === 0)
  498. $this->context->cookie->{$prefix.$key} = !is_array($value) ? $value : serialize($value);
  499. elseif (stripos($key, 'submitFilter') === 0)
  500. $this->context->cookie->$key = !is_array($value) ? $value : serialize($value);
  501. }
  502. foreach ($_GET as $key => $value)
  503. {
  504. if (stripos($key, $this->list_id.'Filter_') === 0)
  505. $this->context->cookie->{$prefix.$key} = !is_array($value) ? $value : serialize($value);
  506. elseif (stripos($key, 'submitFilter') === 0)
  507. $this->context->cookie->$key = !is_array($value) ? $value : serialize($value);
  508. if (stripos($key, $this->list_id.'Orderby') === 0 && Validate::isOrderBy($value))
  509. {
  510. if ($value === '' || $value == $this->_defaultOrderBy)
  511. unset($this->context->cookie->{$prefix.$key});
  512. else
  513. $this->context->cookie->{$prefix.$key} = $value;
  514. }
  515. elseif (stripos($key, $this->list_id.'Orderway') === 0 && Validate::isOrderWay($value))
  516. {
  517. if ($value === '' || $value == $this->_defaultOrderWay)
  518. unset($this->context->cookie->{$prefix.$key});
  519. else
  520. $this->context->cookie->{$prefix.$key} = $value;
  521. }
  522. }
  523. }
  524. $filters = $this->context->cookie->getFamily($prefix.$this->list_id.'Filter_');
  525. foreach ($filters as $key => $value)
  526. {
  527. /* Extracting filters from $_POST on key filter_ */
  528. if ($value != null && !strncmp($key, $prefix.$this->list_id.'Filter_', 7 + Tools::strlen($prefix.$this->list_id)))
  529. {
  530. $key = Tools::substr($key, 7 + Tools::strlen($prefix.$this->list_id));
  531. /* Table alias could be specified using a ! eg. alias!field */
  532. $tmp_tab = explode('!', $key);
  533. $filter = count($tmp_tab) > 1 ? $tmp_tab[1] : $tmp_tab[0];
  534. if ($field = $this->filterToField($key, $filter))
  535. {
  536. $type = (array_key_exists('filter_type', $field) ? $field['filter_type'] : (array_key_exists('type', $field) ? $field['type'] : false));
  537. if (($type == 'date' || $type == 'datetime') && is_string($value))
  538. $value = Tools::unSerialize($value);
  539. $key = isset($tmp_tab[1]) ? $tmp_tab[0].'.`'.$tmp_tab[1].'`' : '`'.$tmp_tab[0].'`';
  540. // Assignement by reference
  541. if (array_key_exists('tmpTableFilter', $field))
  542. $sql_filter = & $this->_tmpTableFilter;
  543. elseif (array_key_exists('havingFilter', $field))
  544. $sql_filter = & $this->_filterHaving;
  545. else
  546. $sql_filter = & $this->_filter;
  547. /* Only for date filtering (from, to) */
  548. if (is_array($value))
  549. {
  550. if (isset($value[0]) && !empty($value[0]))
  551. {
  552. if (!Validate::isDate($value[0]))
  553. $this->errors[] = Tools::displayError('The \'From\' date format is invalid (YYYY-MM-DD)');
  554. else
  555. $sql_filter .= ' AND '.pSQL($key).' >= \''.pSQL(Tools::dateFrom($value[0])).'\'';
  556. }
  557. if (isset($value[1]) && !empty($value[1]))
  558. {
  559. if (!Validate::isDate($value[1]))
  560. $this->errors[] = Tools::displayError('The \'To\' date format is invalid (YYYY-MM-DD)');
  561. else
  562. $sql_filter .= ' AND '.pSQL($key).' <= \''.pSQL(Tools::dateTo($value[1])).'\'';
  563. }
  564. }
  565. else
  566. {
  567. $sql_filter .= ' AND ';
  568. $check_key = ($key == $this->identifier || $key == '`'.$this->identifier.'`');
  569. if ($type == 'int' || $type == 'bool')
  570. $sql_filter .= (($check_key || $key == '`active`') ? 'a.' : '').pSQL($key).' = '.(int)$value.' ';
  571. elseif ($type == 'decimal')
  572. $sql_filter .= ($check_key ? 'a.' : '').pSQL($key).' = '.(float)$value.' ';
  573. elseif ($type == 'select')
  574. $sql_filter .= ($check_key ? 'a.' : '').pSQL($key).' = \''.pSQL($value).'\' ';
  575. else
  576. {
  577. if ($type == 'price')
  578. $value = (float)str_replace(',', '.', $value);
  579. $sql_filter .= ($check_key ? 'a.' : '').pSQL($key).' LIKE \'%'.pSQL($value).'%\' ';
  580. }
  581. }
  582. }
  583. }
  584. }
  585. }
  586. /**
  587. * @todo uses redirectAdmin only if !$this->ajax
  588. */
  589. public function postProcess()
  590. {
  591. try {
  592. if ($this->ajax)
  593. {
  594. // from ajax-tab.php
  595. $action = Tools::getValue('action');
  596. // no need to use displayConf() here
  597. if (!empty($action) && method_exists($this, 'ajaxProcess'.Tools::toCamelCase($action)))
  598. {
  599. Hook::exec('actionAdmin'.ucfirst($this->action).'Before', array('controller' => $this));
  600. Hook::exec('action'.get_class($this).ucfirst($this->action).'Before', array('controller' => $this));
  601. $return = $this->{'ajaxProcess'.Tools::toCamelCase($action)}();
  602. Hook::exec('actionAdmin'.ucfirst($this->action).'After', array('controller' => $this, 'return' => $return));
  603. Hook::exec('action'.get_class($this).ucfirst($this->action).'After', array('controller' => $this, 'return' => $return));
  604. return $return;
  605. }
  606. elseif (!empty($action) && $this->controller_name == 'AdminModules' && Tools::getIsset('configure'))
  607. {
  608. $module_obj = Module::getInstanceByName(Tools::getValue('configure'));
  609. if (Validate::isLoadedObject($module_obj) && method_exists($module_obj, 'ajaxProcess'.$action))
  610. return $module_obj->{'ajaxProcess'.$action}();
  611. }
  612. elseif (method_exists($this, 'ajaxProcess'))
  613. return $this->ajaxProcess();
  614. }
  615. else
  616. {
  617. // Process list filtering
  618. if ($this->filter && $this->action != 'reset_filters')
  619. $this->processFilter();
  620. // If the method named after the action exists, call "before" hooks, then call action method, then call "after" hooks
  621. if (!empty($this->action) && method_exists($this, 'process'.ucfirst(Tools::toCamelCase($this->action))))
  622. {
  623. // Hook before action
  624. Hook::exec('actionAdmin'.ucfirst($this->action).'Before', array('controller' => $this));
  625. Hook::exec('action'.get_class($this).ucfirst($this->action).'Before', array('controller' => $this));
  626. // Call process
  627. $return = $this->{'process'.Tools::toCamelCase($this->action)}();
  628. // Hook After Action
  629. Hook::exec('actionAdmin'.ucfirst($this->action).'After', array('controller' => $this, 'return' => $return));
  630. Hook::exec('action'.get_class($this).ucfirst($this->action).'After', array('controller' => $this, 'return' => $return));
  631. return $return;
  632. }
  633. }
  634. } catch (PrestaShopException $e) {
  635. $this->errors[] = $e->getMessage();
  636. };
  637. return false;
  638. }
  639. /**
  640. * Object Delete images
  641. */
  642. public function processDeleteImage()
  643. {
  644. if (Validate::isLoadedObject($object = $this->loadObject()))
  645. {
  646. if (($object->deleteImage()))
  647. {
  648. $redirect = self::$currentIndex.'&add'.$this->table.'&'.$this->identifier.'='.Tools::getValue($this->identifier).'&conf=7&token='.$this->token;
  649. if (!$this->ajax)
  650. $this->redirect_after = $redirect;
  651. else
  652. $this->content = 'ok';
  653. }
  654. }
  655. $this->errors[] = Tools::displayError('An error occurred while attempting to delete the image. (cannot load object).');
  656. return $object;
  657. }
  658. public function processExport($text_delimiter = '"')
  659. {
  660. // clean buffer
  661. if (ob_get_level() && ob_get_length() > 0)
  662. ob_clean();
  663. $this->getList($this->context->language->id, null, null, 0, false);
  664. if (!count($this->_list))
  665. return;
  666. header('Content-type: text/csv');
  667. header('Content-Type: application/force-download; charset=UTF-8');
  668. header('Cache-Control: no-store, no-cache');
  669. header('Content-disposition: attachment; filename="'.$this->table.'_'.date('Y-m-d_His').'.csv"');
  670. $headers = array();
  671. foreach ($this->fields_list as $key => $datas)
  672. {
  673. if ($datas['title'] == 'PDF')
  674. unset($this->fields_list[$key]);
  675. else
  676. $headers[] = Tools::htmlentitiesDecodeUTF8($datas['title']);
  677. }
  678. $content = array();
  679. foreach ($this->_list as $i => $row)
  680. {
  681. $content[$i] = array();
  682. $path_to_image = false;
  683. foreach ($this->fields_list as $key => $params)
  684. {
  685. $field_value = isset($row[$key]) ? Tools::htmlentitiesDecodeUTF8(
  686. Tools::nl2br($row[$key])) : '';
  687. if ($key == 'image')
  688. {
  689. if ($params['image'] != 'p' || Configuration::get('PS_LEGACY_IMAGES'))
  690. $path_to_image = Tools::getShopDomain(true)._PS_IMG_.$params['image'].'/'.$row['id_'.$this->table].(isset($row['id_image']) ? '-'.(int)$row['id_image'] : '').'.'.$this->imageType;
  691. else
  692. $path_to_image = Tools::getShopDomain(true)._PS_IMG_.$params['image'].'/'.Image::getImgFolderStatic($row['id_image']).(int)$row['id_image'].'.'.$this->imageType;
  693. if ($path_to_image)
  694. $field_value = $path_to_image;
  695. }
  696. if (isset($params['callback']))
  697. {
  698. $callback_obj = (isset($params['callback_object'])) ? $params['callback_object'] : $this->context->controller;
  699. if (!preg_match('/<([a-z]+)([^<]+)*(?:>(.*)<\/\1>|\s+\/>)/ism', call_user_func_array(array($callback_obj, $params['callback']), array($field_value, $row))))
  700. $field_value = call_user_func_array(array($callback_obj, $params['callback']), array($field_value, $row));
  701. }
  702. $content[$i][] = $field_value;
  703. }
  704. }
  705. $this->context->smarty->assign(array(
  706. 'export_precontent' => "\xEF\xBB\xBF",
  707. 'export_headers' => $headers,
  708. 'export_content' => $content,
  709. 'text_delimiter' => $text_delimiter
  710. )
  711. );
  712. $this->layout = 'layout-export.tpl';
  713. }
  714. /**
  715. * Object Delete
  716. */
  717. public function processDelete()
  718. {
  719. if (Validate::isLoadedObject($object = $this->loadObject()))
  720. {
  721. $res = true;
  722. // check if request at least one object with noZeroObject
  723. if (isset($object->noZeroObject) && count(call_user_func(array($this->className, $object->noZeroObject))) <= 1)
  724. {
  725. $this->errors[] = Tools::displayError('You need at least one object.').
  726. ' <b>'.$this->table.'</b><br />'.
  727. Tools::displayError('You cannot delete all of the items.');
  728. }
  729. 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
  730. $this->errors[] = Tools::displayError('You cannot delete this item.');
  731. else
  732. {
  733. if ($this->deleted)
  734. {
  735. if (!empty($this->fieldImageSettings))
  736. $res = $object->deleteImage();
  737. if (!$res)
  738. $this->errors[] = Tools::displayError('Unable to delete associated images.');
  739. $object->deleted = 1;
  740. if ($res = $object->update())
  741. $this->redirect_after = self::$currentIndex.'&conf=1&token='.$this->token;
  742. }
  743. elseif ($res = $object->delete())
  744. $this->redirect_after = self::$currentIndex.'&conf=1&token='.$this->token;
  745. $this->errors[] = Tools::displayError('An error occurred during deletion.');
  746. if ($res)
  747. PrestaShopLogger::addLog(sprintf($this->l('%s deletion', 'AdminTab', false, false), $this->className), 1, null, $this->className, (int)$this->object->id, true, (int)$this->context->employee->id);
  748. }
  749. }
  750. else
  751. {
  752. $this->errors[] = Tools::displayError('An error occurred while deleting the object.').
  753. ' <b>'.$this->table.'</b> '.
  754. Tools::displayError('(cannot load object)');
  755. }
  756. return $object;
  757. }
  758. /**
  759. * Call the right method for creating or updating object
  760. *
  761. * @return mixed
  762. */
  763. public function processSave()
  764. {
  765. if ($this->id_object)
  766. {
  767. $this->object = $this->loadObject();
  768. return $this->processUpdate();
  769. }
  770. else
  771. return $this->processAdd();
  772. }
  773. /**
  774. * Object creation
  775. */
  776. public function processAdd()
  777. {
  778. if (!isset($this->className) || empty($this->className))
  779. return false;
  780. $this->validateRules();
  781. if (count($this->errors) <= 0)
  782. {
  783. $this->object = new $this->className();
  784. $this->copyFromPost($this->object, $this->table);
  785. $this->beforeAdd($this->object);
  786. if (method_exists($this->object, 'add') && !$this->object->add())
  787. {
  788. $this->errors[] = Tools::displayError('An error occurred while creating an object.').
  789. ' <b>'.$this->table.' ('.Db::getInstance()->getMsgError().')</b>';
  790. }
  791. /* voluntary do affectation here */
  792. elseif (($_POST[$this->identifier] = $this->object->id) && $this->postImage($this->object->id) && !count($this->errors) && $this->_redirect)
  793. {
  794. PrestaShopLogger::addLog(sprintf($this->l('%s addition', 'AdminTab', false, false), $this->className), 1, null, $this->className, (int)$this->object->id, true, (int)$this->context->employee->id);
  795. $parent_id = (int)Tools::getValue('id_parent', 1);
  796. $this->afterAdd($this->object);
  797. $this->updateAssoShop($this->object->id);
  798. // Save and stay on same form
  799. if (empty($this->redirect_after) && $this->redirect_after !== false && Tools::isSubmit('submitAdd'.$this->table.'AndStay'))
  800. $this->redirect_after = self::$currentIndex.'&'.$this->identifier.'='.$this->object->id.'&conf=3&update'.$this->table.'&token='.$this->token;
  801. // Save and back to parent
  802. if (empty($this->redirect_after) && $this->redirect_after !== false && Tools::isSubmit('submitAdd'.$this->table.'AndBackToParent'))
  803. $this->redirect_after = self::$currentIndex.'&'.$this->identifier.'='.$parent_id.'&conf=3&token='.$this->token;
  804. // Default behavior (save and back)
  805. if (empty($this->redirect_after) && $this->redirect_after !== false)
  806. $this->redirect_after = self::$currentIndex.($parent_id ? '&'.$this->identifier.'='.$this->object->id : '').'&conf=3&token='.$this->token;
  807. }
  808. }
  809. $this->errors = array_unique($this->errors);
  810. if (!empty($this->errors))
  811. {
  812. // if we have errors, we stay on the form instead of going back to the list
  813. $this->display = 'edit';
  814. return false;
  815. }
  816. return $this->object;
  817. }
  818. /**
  819. * Object update
  820. */
  821. public function processUpdate()
  822. {
  823. /* Checking fields validity */
  824. $this->validateRules();
  825. if (empty($this->errors))
  826. {
  827. $id = (int)Tools::getValue($this->identifier);
  828. /* Object update */
  829. if (isset($id) && !empty($id))
  830. {
  831. $object = new $this->className($id);
  832. if (Validate::isLoadedObject($object))
  833. {
  834. /* Specific to objects which must not be deleted */
  835. if ($this->deleted && $this->beforeDelete($object))
  836. {
  837. // Create new one with old objet values
  838. $object_new = $object->duplicateObject();
  839. if (Validate::isLoadedObject($object_new))
  840. {
  841. // Update old object to deleted
  842. $object->deleted = 1;
  843. $object->update();
  844. // Update new object with post values
  845. $this->copyFromPost($object_new, $this->table);
  846. $result = $object_new->update();
  847. if (Validate::isLoadedObject($object_new))
  848. $this->afterDelete($object_new, $object->id);
  849. }
  850. }
  851. else
  852. {
  853. $this->copyFromPost($object, $this->table);
  854. $result = $object->update();
  855. $this->afterUpdate($object);
  856. }
  857. if ($object->id)
  858. $this->updateAssoShop($object->id);
  859. if (!$result)
  860. {
  861. $this->errors[] = Tools::displayError('An error occurred while updating an object.').
  862. ' <b>'.$this->table.'</b> ('.Db::getInstance()->getMsgError().')';
  863. }
  864. elseif ($this->postImage($object->id) && !count($this->errors) && $this->_redirect)
  865. {
  866. $parent_id = (int)Tools::getValue('id_parent', 1);
  867. // Specific back redirect
  868. if ($back = Tools::getValue('back'))
  869. $this->redirect_after = urldecode($back).'&conf=4';
  870. // Specific scene feature
  871. // @todo change stay_here submit name (not clear for redirect to scene ... )
  872. if (Tools::getValue('stay_here') == 'on' || Tools::getValue('stay_here') == 'true' || Tools::getValue('stay_here') == '1')
  873. $this->redirect_after = self::$currentIndex.'&'.$this->identifier.'='.$object->id.'&conf=4&updatescene&token='.$this->token;
  874. // Save and stay on same form
  875. // @todo on the to following if, we may prefer to avoid override redirect_after previous value
  876. if (Tools::isSubmit('submitAdd'.$this->table.'AndStay'))
  877. $this->redirect_after = self::$currentIndex.'&'.$this->identifier.'='.$object->id.'&conf=4&update'.$this->table.'&token='.$this->token;
  878. // Save and back to parent
  879. if (Tools::isSubmit('submitAdd'.$this->table.'AndBackToParent'))
  880. $this->redirect_after = self::$currentIndex.'&'.$this->identifier.'='.$parent_id.'&conf=4&token='.$this->token;
  881. // Default behavior (save and back)
  882. if (empty($this->redirect_after) && $this->redirect_after !== false)
  883. $this->redirect_after = self::$currentIndex.($parent_id ? '&'.$this->identifier.'='.$object->id : '').'&conf=4&token='.$this->token;
  884. }
  885. PrestaShopLogger::addLog(sprintf($this->l('%s modification', 'AdminTab', false, false), $this->className), 1, null, $this->className, (int)$object->id, true, (int)$this->context->employee->id);
  886. }
  887. else
  888. $this->errors[] = Tools::displayError('An error occurred while updating an object.').
  889. ' <b>'.$this->table.'</b> '.Tools::displayError('(cannot load object)');
  890. }
  891. }
  892. $this->errors = array_unique($this->errors);
  893. if (!empty($this->errors))
  894. {
  895. // if we have errors, we stay on the form instead of going back to the list
  896. $this->display = 'edit';
  897. return false;
  898. }
  899. if (isset($object))
  900. return $object;
  901. return;
  902. }
  903. /**
  904. * Change object required fields
  905. */
  906. public function processUpdateFields()
  907. {
  908. if (!is_array($fields = Tools::getValue('fieldsBox')))
  909. $fields = array();
  910. $object = new $this->className();
  911. if (!$object->addFieldsRequiredDatabase($fields))
  912. $this->errors[] = Tools::displayError('An error occurred when attempting to update the required fields.');
  913. else
  914. $this->redirect_after = self::$currentIndex.'&conf=4&token='.$this->token;
  915. return $object;
  916. }
  917. /**
  918. * Change object status (active, inactive)
  919. */
  920. public function processStatus()
  921. {
  922. if (Validate::isLoadedObject($object = $this->loadObject()))
  923. {
  924. if ($object->toggleStatus())
  925. {
  926. $matches = array();
  927. if (preg_match('/[\?|&]controller=([^&]*)/', (string)$_SERVER['HTTP_REFERER'], $matches) !== false
  928. && strtolower($matches[1]) != strtolower(preg_replace('/controller/i', '', get_class($this))))
  929. $this->redirect_after = preg_replace('/[\?|&]conf=([^&]*)/i', '', (string)$_SERVER['HTTP_REFERER']);
  930. else
  931. $this->redirect_after = self::$currentIndex.'&token='.$this->token;
  932. $id_category = (($id_category = (int)Tools::getValue('id_category')) && Tools::getValue('id_product')) ? '&id_category='.$id_category : '';
  933. $this->redirect_after .= '&conf=5'.$id_category;
  934. }
  935. else
  936. $this->errors[] = Tools::displayError('An error occurred while updating the status.');
  937. }
  938. else
  939. $this->errors[] = Tools::displayError('An error occurred while updating the status for an object.').
  940. ' <b>'.$this->table.'</b> '.
  941. Tools::displayError('(cannot load object)');
  942. return $object;
  943. }
  944. /**
  945. * Change object position
  946. */
  947. public function processPosition()
  948. {
  949. if (!Validate::isLoadedObject($object = $this->loadObject()))
  950. {
  951. $this->errors[] = Tools::displayError('An error occurred while updating the status for an object.').
  952. ' <b>'.$this->table.'</b> '.Tools::displayError('(cannot load object)');
  953. }
  954. elseif (!$object->updatePosition((int)Tools::getValue('way'), (int)Tools::getValue('position')))
  955. $this->errors[] = Tools::displayError('Failed to update the position.');
  956. else
  957. {
  958. $id_identifier_str = ($id_identifier = (int)Tools::getValue($this->identifier)) ? '&'.$this->identifier.'='.$id_identifier : '';
  959. $redirect = self::$currentIndex.'&'.$this->table.'Orderby=position&'.$this->table.'Orderway=asc&conf=5'.$id_identifier_str.'&token='.$this->token;
  960. $this->redirect_after = $redirect;
  961. }
  962. return $object;
  963. }
  964. /**
  965. * Cancel all filters for this tab
  966. */
  967. public function processResetFilters($list_id = null)
  968. {
  969. if ($list_id === null)
  970. $list_id = isset($this->list_id) ? $this->list_id : $this->table;
  971. $prefix = str_replace(array('admin', 'controller'), '', Tools::strtolower(get_class($this)));
  972. $filters = $this->context->cookie->getFamily($prefix.$list_id.'Filter_');
  973. foreach ($filters as $cookie_key => $filter)
  974. if (strncmp($cookie_key, $prefix.$list_id.'Filter_', 7 + Tools::strlen($prefix.$list_id)) == 0)
  975. {
  976. $key = substr($cookie_key, 7 + Tools::strlen($prefix.$list_id));
  977. if (is_array($this->fields_list) && array_key_exists($key, $this->fields_list))
  978. $this->context->cookie->$cookie_key = null;
  979. unset($this->context->cookie->$cookie_key);
  980. }
  981. if (isset($this->context->cookie->{'submitFilter'.$list_id}))
  982. unset($this->context->cookie->{'submitFilter'.$list_id});
  983. if (isset($this->context->cookie->{$prefix.$list_id.'Orderby'}))
  984. unset($this->context->cookie->{$prefix.$list_id.'Orderby'});
  985. if (isset($this->context->cookie->{$prefix.$list_id.'Orderway'}))
  986. unset($this->context->cookie->{$prefix.$list_id.'Orderway'});
  987. $_POST = array();
  988. $this->_filter = false;
  989. unset($this->_filterHaving);
  990. unset($this->_having);
  991. }
  992. /**
  993. * Update options and preferences
  994. */
  995. protected function processUpdateOptions()
  996. {
  997. $this->beforeUpdateOptions();
  998. $languages = Language::getLanguages(false);
  999. $hide_multishop_checkbox = (Shop::getTotalShops(false, null) < 2) ? true : false;
  1000. foreach ($this->fields_options as $category_data)
  1001. {
  1002. if (!isset($category_data['fields']))
  1003. continue;
  1004. $fields = $category_data['fields'];
  1005. foreach ($fields as $field => $values)
  1006. {
  1007. if (isset($values['type']) && $values['type'] == 'selectLang')
  1008. {
  1009. foreach ($languages as $lang)
  1010. if (Tools::getValue($field.'_'.strtoupper($lang['iso_code'])))
  1011. $fields[$field.'_'.strtoupper($lang['iso_code'])] = array(
  1012. 'type' => 'select',
  1013. 'cast' => 'strval',
  1014. 'identifier' => 'mode',
  1015. 'list' => $values['list']
  1016. );
  1017. }
  1018. }
  1019. // Validate fields
  1020. foreach ($fields as $field => $values)
  1021. {
  1022. // We don't validate fields with no visibility
  1023. if (!$hide_multishop_checkbox && Shop::isFeatureActive() && isset($values['visibility']) && $values['visibility'] > Shop::getContext())
  1024. continue;
  1025. // Check if field is required
  1026. if ((!Shop::isFeatureActive() && isset($values['required']) && $values['required'])
  1027. || (Shop::isFeatureActive() && isset($_POST['multishopOverrideOption'][$field]) && isset($values['required']) && $values['required']))
  1028. if (isset($values['type']) && $values['type'] == 'textLang')
  1029. {
  1030. foreach ($languages as $language)
  1031. if (($value = Tools::getValue($field.'_'.$language['id_lang'])) == false && (string)$value != '0')
  1032. $this->errors[] = sprintf(Tools::displayError('field %s is required.'), $values['title']);
  1033. }
  1034. elseif (($value = Tools::getValue($field)) == false && (string)$value != '0')
  1035. $this->errors[] = sprintf(Tools::displayError('field %s is required.'), $values['title']);
  1036. // Check field validator
  1037. if (isset($values['type']) && $values['type'] == 'textLang')
  1038. {
  1039. foreach ($languages as $language)
  1040. if (Tools::getValue($field.'_'.$language['id_lang']) && isset($values['validation']))
  1041. if (!Validate::$values['validation'](Tools::getValue($field.'_'.$language['id_lang'])))
  1042. $this->errors[] = sprintf(Tools::displayError('field %s is invalid.'), $values['title']);
  1043. }
  1044. elseif (Tools::getValue($field) && isset($values['validation']))
  1045. if (!Validate::$values['validation'](Tools::getValue($field)))
  1046. $this->errors[] = sprintf(Tools::displayError('field %s is invalid.'), $values['title']);
  1047. // Set default value
  1048. if (Tools::getValue($field) === false && isset($values['default']))
  1049. $_POST[$field] = $values['default'];
  1050. }
  1051. if (!count($this->errors))
  1052. {
  1053. foreach ($fields as $key => $options)
  1054. {
  1055. if (Shop::isFeatureActive() && isset($options['visibility']) && $options['visibility'] > Shop::getContext())
  1056. continue;
  1057. if (!$hide_multishop_checkbox && Shop::isFeatureActive() && Shop::getContext() != Shop::CONTEXT_ALL && empty($options['no_multishop_checkbox']) && empty($_POST['multishopOverrideOption'][$key]))
  1058. {
  1059. Configuration::deleteFromContext($key);
  1060. continue;
  1061. }
  1062. // check if a method updateOptionFieldName is available
  1063. $method_name = 'updateOption'.Tools::toCamelCase($key, true);
  1064. if (method_exists($this, $method_name))
  1065. $this->$method_name(Tools::getValue($key));
  1066. elseif (isset($options['type']) && in_array($options['type'], array('textLang', 'textareaLang')))
  1067. {
  1068. $list = array();
  1069. foreach ($languages as $language)
  1070. {
  1071. $key_lang = Tools::getValue($key.'_'.$language['id_lang']);
  1072. $val = (isset($options['cast']) ? $options['cast']($key_lang) : $key_lang);
  1073. if ($this->validateField($val, $options))
  1074. {
  1075. if (Validate::isCleanHtml($val))
  1076. $list[$language['id_lang']] = $val;
  1077. else
  1078. $this->errors[] = Tools::displayError('Can not add configuration '.$key.' for lang '.Language::getIsoById((int)$language['id_lang']));
  1079. }
  1080. }
  1081. Configuration::updateValue($key, $list, isset($values['validation']) && $options['validation'] == 'isCleanHtml' ? true : false);
  1082. }
  1083. else
  1084. {
  1085. $val = (isset($options['cast']) ? $options['cast'](Tools::getValue($key)) : Tools::getValue($key));
  1086. if ($this->validateField($val, $options))
  1087. {
  1088. if (Validate::isCleanHtml($val))
  1089. Configuration::updateValue($key, $val);
  1090. else
  1091. $this->errors[] = Tools::displayError('Can not add configuration '.$key);
  1092. }
  1093. }
  1094. }
  1095. }
  1096. }
  1097. $this->display = 'list';
  1098. if (empty($this->errors))
  1099. $this->confirmations[] = $this->_conf[6];
  1100. }
  1101. public function initPageHeaderToolbar()
  1102. {
  1103. if (empty($this->toolbar_title))
  1104. $this->initToolbarTitle();
  1105. if (!is_array($this->toolbar_title))
  1106. $this->toolbar_title = array($this->toolbar_title);
  1107. switch ($this->display)
  1108. {
  1109. case 'view':
  1110. // Default cancel button - like old back link
  1111. $back = Tools::safeOutput(Tools::getValue('back', ''));
  1112. if (empty($back))
  1113. $back = self::$currentIndex.'&token='.$this->token;
  1114. if (!Validate::isCleanHtml($back))
  1115. die(Tools::displayError());
  1116. if (!$this->lite_display)
  1117. $this->page_header_toolbar_btn['back'] = array(
  1118. 'href' => $back,
  1119. 'desc' => $this->l('Back to list')
  1120. );
  1121. $obj = $this->loadObject(true);
  1122. if (Validate::isLoadedObject($obj) && isset($obj->{$this->identifier_name}) && !empty($obj->{$this->identifier_name}))
  1123. {
  1124. array_pop($this->toolbar_title);
  1125. $this->toolbar_title[] = is_array($obj->{$this->identifier_name}) ? $obj->{$this->identifier_name}[$this->context->employee->id_lang] : $obj->{$this->identifier_name};
  1126. }
  1127. break;
  1128. case 'edit':
  1129. $obj = $this->loadObject(true);
  1130. if (Validate::isLoadedObject($obj) && isset($obj->{$this->identifier_name}) && !empty($obj->{$this->identifier_name}))
  1131. {
  1132. array_pop($this->toolbar_title);
  1133. $this->toolbar_title[] = sprintf($this->l('Edit: %s'),
  1134. is_array($obj->{$this->identifier_name}) ? $obj->{$this->identifier_name}[$this->context->employee->id_lang] : $obj->{$this->identifier_name});
  1135. }
  1136. break;
  1137. }
  1138. if (is_array($this->page_header_toolbar_btn)
  1139. && $this->page_header_toolbar_btn instanceof Traversable
  1140. || count($this->toolbar_title))
  1141. $this->show_page_header_toolbar = true;
  1142. if (empty($this->page_header_toolbar_title))
  1143. $this->page_header_toolbar_title = array_pop($this->toolbar_title);
  1144. $this->addPageHeaderToolBarModulesListButton();
  1145. $this->context->smarty->assign('help_link', 'http://help.prestashop.com/'.$this->context->language->iso_code.'/doc/'.Tools::getValue('controller').'?version='._PS_VERSION_.'&country='.$this->context->country->iso_code);
  1146. }
  1147. /**
  1148. * assign default action in toolbar_btn smarty var, if they are not set.
  1149. * uses override to specifically add, modify or remove items
  1150. *
  1151. */
  1152. public function initToolbar()
  1153. {
  1154. switch ($this->display)
  1155. {
  1156. case 'add':
  1157. case 'edit':
  1158. // Default save button - action dynamically handled in javascript
  1159. $this->toolbar_btn['save'] = array(
  1160. 'href' => '#',
  1161. 'desc' => $this->l('Save')
  1162. );
  1163. $back = Tools::safeOutput(Tools::getValue('back', ''));
  1164. if (empty($back))
  1165. $back = self::$currentIndex.'&token='.$this->token;
  1166. if (!Validate::isCleanHtml($back))
  1167. die(Tools::displayError());
  1168. if (!$this->lite_display)
  1169. $this->toolbar_btn['cancel'] = array(
  1170. 'href' => $back,
  1171. 'desc' => $this->l('Cancel')
  1172. );
  1173. break;
  1174. case 'view':
  1175. // Default cancel button - like old back link
  1176. $back = Tools::safeOutput(Tools::getValue('back', ''));
  1177. if (empty($back))
  1178. $back = self::$currentIndex.'&token='.$this->token;
  1179. if (!Validate::isCleanHtml($back))
  1180. die(Tools::displayError());
  1181. if (!$this->lite_display)
  1182. $this->toolbar_btn['back'] = array(
  1183. 'href' => $back,
  1184. 'desc' => $this->l('Back to list')
  1185. );
  1186. break;
  1187. case 'options':
  1188. $this->toolbar_btn['save'] = array(
  1189. 'href' => '#',
  1190. 'desc' => $this->l('Save')
  1191. );
  1192. break;
  1193. default: // list
  1194. $this->toolbar_btn['new'] = array(
  1195. 'href' => self::$currentIndex.'&add'.$this->table.'&token='.$this->token,
  1196. 'desc' => $this->l('Add new')
  1197. );
  1198. if ($this->allow_export)
  1199. $this->toolbar_btn['export'] = array(
  1200. 'href' => self::$currentIndex.'&export'.$this->table.'&token='.$this->token,
  1201. 'desc' => $this->l('Export')
  1202. );
  1203. }
  1204. $this->addToolBarModulesListButton();
  1205. }
  1206. /**
  1207. * Load class object using identifier in $_GET (if possible)
  1208. * otherwise return an empty object, or die
  1209. *
  1210. * @param boolean $opt Return an empty object if load fail
  1211. * @return object|boolean
  1212. */
  1213. protected function loadObject($opt = false)
  1214. {
  1215. if (!isset($this->className) || empty($this->className))
  1216. return true;
  1217. $id = (int)Tools::getValue($this->identifier);
  1218. if ($id && Validate::isUnsignedId($id))
  1219. {
  1220. if (!$this->object)
  1221. $this->object = new $this->className($id);
  1222. if (Validate::isLoadedObject($this->object))
  1223. return $this->object;
  1224. // throw exception
  1225. $this->errors[] = Tools::displayError('The object cannot be loaded (or found)');
  1226. return false;
  1227. }
  1228. elseif ($opt)
  1229. {
  1230. if (!$this->object)
  1231. $this->object = new $this->className();
  1232. return $this->object;
  1233. }
  1234. else
  1235. {
  1236. $this->errors[] = Tools::displayError('The object cannot be loaded (the identifier is missing or invalid)');
  1237. return false;
  1238. }
  1239. }
  1240. /**
  1241. * Check if the token is valid, else display a warning page
  1242. */
  1243. public function checkAccess()
  1244. {
  1245. if (!$this->checkToken())
  1246. {
  1247. // If this is an XSS attempt, then we should only display a simple, secure page
  1248. // ${1} in the replacement string of the regexp is required,
  1249. // because the token may begin with a number and mix up with it (e.g. $17)
  1250. $url = preg_replace('/([&?]token=)[^&]*(&.*)?$/', '${1}'.$this->token.'$2', $_SERVER['REQUEST_URI']);
  1251. if (false === strpos($url, '?token=') && false === strpos($url, '&token='))
  1252. $url .= '&token='.$this->token;
  1253. if (strpos($url, '?') === false)
  1254. $url = str_replace('&token', '?controller=AdminDashboard&token', $url);
  1255. $this->context->smarty->assign('url', htmlentities($url));
  1256. return false;
  1257. }
  1258. return true;
  1259. }
  1260. protected function filterToField($key, $filter)
  1261. {
  1262. if (!isset($this->fields_list))
  1263. return false;
  1264. foreach ($this->fields_list as $field)
  1265. if (array_key_exists('filter_key', $field) && $field['filter_key'] == $key)
  1266. return $field;
  1267. if (array_key_exists($filter, $this->fields_list))
  1268. return $this->fields_list[$filter];
  1269. return false;
  1270. }
  1271. public function displayNoSmarty()
  1272. {
  1273. }
  1274. public function displayAjax()
  1275. {
  1276. if ($this->json)
  1277. {
  1278. $this->context->smarty->assign(array(
  1279. 'json' => true,
  1280. 'status' => $this->status,
  1281. ));
  1282. }
  1283. $this->layout = 'layout-ajax.tpl';
  1284. $this->display_header = false;
  1285. $this->display_footer = false;
  1286. return $this->display();
  1287. }
  1288. protected function redirect()
  1289. {
  1290. header('Location: '.$this->redirect_after);
  1291. exit;
  1292. }
  1293. public function display()
  1294. {
  1295. $this->context->smarty->assign(array(
  1296. 'display_header' => $this->display_header,
  1297. 'display_footer' => $this->display_footer,
  1298. 'js_def' => Media::getJsDef(),
  1299. ));
  1300. // Use page title from meta_title if it has been set else from the breadcrumbs array
  1301. if (!$this->meta_title)
  1302. $this->meta_title = strip_tags(is_array($this->toolbar_title) ? implode(' '.Configuration::get('PS_NAVIGATION_PIPE').' ', $this->toolbar_title) : $this->toolbar_title);
  1303. $this->context->smarty->assign('meta_title', $this->meta_title);
  1304. $template_dirs = $this->context->smarty->getTemplateDir();
  1305. // Check if header/footer have been overriden
  1306. $dir = $this->context->smarty->getTemplateDir(0).'controllers'.DIRECTORY_SEPARATOR.trim($this->override_folder, '\\/').DIRECTORY_SEPARATOR;
  1307. $module_list_dir = $this->context->smarty->getTemplateDir(0).'helpers'.DIRECTORY_SEPARATOR.'modules_list'.DIRECTORY_SEPARATOR;
  1308. $header_tpl = file_exists($dir.'header.tpl') ? $dir.'header.tpl' : 'header.tpl';
  1309. $page_header_toolbar = file_exists($dir.'page_header_toolbar.tpl') ? $dir.'page_header_toolbar.tpl' : 'page_header_toolbar.tpl';
  1310. $footer_tpl = file_exists($dir.'footer.tpl') ? $dir.'footer.tpl' : 'footer.tpl';
  1311. $modal_module_list = file_exists($module_list_dir.'modal.tpl') ? $module_list_dir.'modal.tpl' : 'modal.tpl';
  1312. $tpl_action = $this->tpl_folder.$this->display.'.tpl';
  1313. // Check if action template has been overriden
  1314. foreach ($template_dirs as $template_dir)
  1315. if (file_exists($template_dir.DIRECTORY_SEPARATOR.$tpl_action) && $this->display != 'view' && $this->display != 'options')
  1316. {
  1317. if (method_exists($this, $this->display.Tools::toCamelCase($this->className)))
  1318. $this->{$this->display.Tools::toCamelCase($this->className)}();
  1319. $this->context->smarty->assign('content', $this->context->smarty->fetch($tpl_action));
  1320. break;
  1321. }
  1322. if (!$this->ajax)
  1323. {
  1324. $template = $this->createTemplate($this->template);
  1325. $page = $template->fetch();
  1326. }
  1327. else
  1328. $page = $this->content;
  1329. if ($conf = Tools::getValue('conf'))
  1330. $this->context->smarty->assign('conf', $this->json ? Tools::jsonEncode($this->_conf[(int)$conf]) : $this->_conf[(int)$conf]);
  1331. foreach (array('errors', 'warnings', 'informations', 'confirmations') as $type)
  1332. {
  1333. if (!is_array($this->$type))
  1334. $this->$type = (array)$this->$type;
  1335. $this->context->smarty->assign($type, $this->json ? Tools::jsonEncode(array_unique($this->$type)) : array_unique($this->$type));
  1336. }
  1337. if ($this->show_page_header_toolbar && !$this->lite_display)
  1338. $this->context->smarty->assign(array(
  1339. 'page_header_toolbar' => $this->context->smarty->fetch($page_header_toolbar),
  1340. 'modal_module_list' => $this->context->smarty->fetch($modal_module_list),
  1341. )
  1342. );
  1343. $this->context->smarty->assign(array(
  1344. 'page' => $this->json ? Tools::jsonEncode($page) : $page,
  1345. 'header' => $this->context->smarty->fetch($header_tpl),
  1346. 'footer' => $this->context->smarty->fetch($footer_tpl),
  1347. )
  1348. );
  1349. $this->smartyOutputContent($this->layout);
  1350. }
  1351. /**
  1352. * add a warning message to display at the top of the page
  1353. *
  1354. * @param string $msg
  1355. */
  1356. protected function displayWarning($msg)
  1357. {
  1358. $this->warnings[] = $msg;
  1359. }
  1360. /**
  1361. * add a info message to display at the top of the page
  1362. *
  1363. * @param string $msg
  1364. */
  1365. protected function displayInformation($msg)
  1366. {
  1367. $this->informations[] = $msg;
  1368. }
  1369. /**
  1370. * Assign smarty variables for the header
  1371. */
  1372. public function initHeader()
  1373. {
  1374. header('Cache-Control: no-store, no-cache');
  1375. // Multishop
  1376. $is_multishop = Shop::isFeatureActive();
  1377. // Quick access
  1378. if ((int)$this->context->employee->id)
  1379. {
  1380. $quick_access = QuickAccess::getQuickAccesses($this->context->language->id);
  1381. foreach ($quick_access as $index => $quick)
  1382. {
  1383. if ($quick['link'] == '../' && Shop::getContext() == Shop::CONTEXT_SHOP)
  1384. {
  1385. $url = $this->context->shop->getBaseURL();
  1386. if (!$url)
  1387. {
  1388. unset($quick_access[$index]);
  1389. continue;
  1390. }
  1391. $quick_access[$index]['link'] = $url;
  1392. }
  1393. else
  1394. {
  1395. preg_match('/controller=(.+)(&.+)?$/', $quick['link'], $admin_tab);
  1396. if (isset($admin_tab[1]))
  1397. {
  1398. if (strpos($admin_tab[1], '&'))
  1399. $admin_tab[1] = substr($admin_tab[1], 0, strpos($admin_tab[1], '&'));
  1400. $token = Tools::getAdminToken($admin_tab[1].(int)Tab::getIdFromClassName($admin_tab[1]).(int)$this->context->employee->id);
  1401. $quick_access[$index]['target'] = $admin_tab[1];
  1402. $quick_access[$index]['link'] .= '&token='.$token;
  1403. }
  1404. }
  1405. }
  1406. }
  1407. // Tab list
  1408. $tabs = Tab::getTabs($this->context->language->id, 0);
  1409. $current_id = Tab::getCurrentParentId();
  1410. foreach ($tabs as $index => $tab)
  1411. {
  1412. if (!Tab::checkTabRights($tab['id_tab'])
  1413. || ($tab['class_name'] == 'AdminStock' && Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT') == 0)
  1414. || $tab['class_name'] == 'AdminCarrierWizard')
  1415. {
  1416. unset($tabs[$index]);
  1417. continue;
  1418. }
  1419. $img_cache_url = 'themes/'.$this->context->employee->bo_theme.'/img/t/'.$tab['class_name'].'.png';
  1420. $img_exists_cache = Tools::file_exists_cache(_PS_ADMIN_DIR_.$img_cache_url);
  1421. // retrocompatibility : change png to gif if icon not exists
  1422. if (!$img_exists_cache)
  1423. $img_exists_cache = Tools::file_exists_cache(_PS_ADMIN_DIR_.str_replace('.png', '.gif', $img_cache_url));
  1424. if ($img_exists_cache)
  1425. $path_img = $img = $img_exists_cache;
  1426. else
  1427. {
  1428. $path_img = _PS_IMG_DIR_.'t/'.$tab['class_name'].'.png';
  1429. // Relative link will always work, whatever the base uri set in the admin
  1430. $img = '../img/t/'.$tab['class_name'].'.png';
  1431. }
  1432. if (trim($tab['module']) != '')
  1433. {
  1434. $path_img = _PS_MODULE_DIR_.$tab['module'].'/'.$tab['class_name'].'.png';
  1435. // Relative link will always work, whatever the base uri set in the admin
  1436. $img = '../modules/'.$tab['module'].'/'.$tab['class_name'].'.png';
  1437. }
  1438. // retrocompatibility
  1439. if (!file_exists($path_img))
  1440. $img = str_replace('png', 'gif', $img);
  1441. // tab[class_name] does not contains the "Controller" suffix
  1442. $tabs[$index]['current'] = ($tab['class_name'].'Controller' == get_class($this)) || ($current_id == $tab['id_tab']);
  1443. $tabs[$index]['img'] = $img;
  1444. $tabs[$index]['href'] = $this->context->link->getAdminLink($tab['class_name']);
  1445. $sub_tabs = Tab::getTabs($this->context->language->id, $tab['id_tab']);
  1446. foreach ($sub_tabs as $index2 => $sub_tab)
  1447. {
  1448. //check if module is enable and
  1449. if (isset($sub_tab['module']) && !empty($sub_tab['module']))
  1450. {
  1451. $module = Module::getInstanceByName($sub_tab['module']);
  1452. if (is_object($module) && !$module->isEnabledForShopContext())
  1453. {
  1454. unset($sub_tabs[$index2]);
  1455. continue;
  1456. }
  1457. }
  1458. // class_name is the name of the class controller
  1459. if (Tab::checkTabRights($sub_tab['id_tab']) === true && (bool)$sub_tab['active'] && $sub_tab['class_name'] != 'AdminCarrierWizard')
  1460. {
  1461. $sub_tabs[$index2]['href'] = $this->context->link->getAdminLink($sub_tab['class_name']);
  1462. $sub_tabs[$index2]['current'] = ($sub_tab['class_name'].'Controller' == get_class($this) || $sub_tab['class_name'] == Tools::getValue('controller'));
  1463. }
  1464. elseif ($sub_tab['class_name'] == 'AdminCarrierWizard' && $sub_tab['class_name'].'Controller' == get_class($this))
  1465. {
  1466. foreach ($sub_tabs as $i => $tab)
  1467. if ($tab['class_name'] == 'AdminCarriers')
  1468. break;
  1469. $sub_tabs[$i]['current'] = true;
  1470. unset($sub_tabs[$index2]);
  1471. }
  1472. else
  1473. unset($sub_tabs[$index2]);
  1474. }
  1475. $tabs[$index]['sub_tabs'] = array_values($sub_tabs);
  1476. }
  1477. if (Validate::isLoadedObject($this->context->employee))
  1478. {
  1479. $accesses = Profile::getProfileAccesses($this->context->employee->id_profile, 'class_name');
  1480. /* Hooks are volontary out the initialize array (need those variables already assigned) */
  1481. $bo_color = empty($this->context->employee->bo_color) ? '#FFFFFF' : $this->context->employee->bo_color;
  1482. $this->context->smarty->assign(array(
  1483. 'autorefresh_notifications' => Configuration::get('PS_ADMINREFRESH_NOTIFICATION'),
  1484. 'help_box' => Configuration::get('PS_HELPBOX'),
  1485. 'round_mode' => Configuration::get('PS_PRICE_ROUND_MODE'),
  1486. 'brightness' => Tools::getBrightness($bo_color) < 128 ? 'white' : '#383838',
  1487. 'bo_width' => (int)$this->context->employee->bo_width,
  1488. 'bo_color' => isset($this->context->employee->bo_color) ? Tools::htmlentitiesUTF8($this->context->employee->bo_color) : null,
  1489. 'show_new_orders' => Configuration::get('PS_SHOW_NEW_ORDERS') && isset($accesses['AdminOrders']) && $accesses['AdminOrders']['view'],
  1490. 'show_new_customers' => Configuration::get('PS_SHOW_NEW_CUSTOMERS') && isset($accesses['AdminCustomers']) && $accesses['AdminCustomers']['view'],
  1491. 'show_new_messages' => Configuration::get('PS_SHOW_NEW_MESSAGES') && isset($accesses['AdminCustomerThreads']) && $accesses['AdminCustomerThreads']['view'],
  1492. 'employee' => $this->context->employee,
  1493. 'search_type' => Tools::getValue('bo_search_type'),
  1494. 'bo_query' => Tools::safeOutput(Tools::stripslashes(Tools::getValue('bo_query'))),
  1495. 'quick_access' => $quick_access,
  1496. 'multi_shop' => Shop::isFeatureActive(),
  1497. 'shop_list' => Helper::renderShopList(),
  1498. 'shop' => $this->context->shop,
  1499. 'shop_group' => new ShopGroup((int)Shop::getContextShopGroupID()),
  1500. 'current_parent_id' => (int)Tab::getCurrentParentId(),
  1501. 'tabs' => $tabs,
  1502. 'is_multishop' => $is_multishop,
  1503. 'multishop_context' => $this->multishop_context,
  1504. 'default_tab_link' => $this->context->link->getAdminLink(Tab::getClassNameById((int)Context::getContext()->employee->default_tab)),
  1505. 'collapse_menu' => isset($this->context->cookie->collapse_menu) ? (int)$this->context->cookie->collapse_menu : 0,
  1506. ));
  1507. }
  1508. else
  1509. $this->context->smarty->assign('default_tab_link', $this->context->link->getAdminLink('AdminDashboard'));
  1510. // Shop::initialize() in config.php may empty $this->context->shop->virtual_uri so using a new shop instance for getBaseUrl()
  1511. $this->context->shop = new Shop((int)$this->context->shop->id);
  1512. $this->context->smarty->assign(array(
  1513. 'img_dir' => _PS_IMG_,
  1514. 'iso' => $this->context->language->iso_code,
  1515. 'class_name' => $this->className,
  1516. 'iso_user' => $this->context->language->iso_code,
  1517. 'country_iso_code' => $this->context->country->iso_code,
  1518. 'version' => _PS_VERSION_,
  1519. 'lang_iso' => $this->context->language->iso_code,
  1520. 'full_language_code' => $this->context->language->language_code,
  1521. 'link' => $this->context->link,
  1522. 'shop_name' => Configuration::get('PS_SHOP_NAME'),
  1523. 'base_url' => $this->context->shop->getBaseURL(),
  1524. 'tab' => isset($tab) ? $tab : null, // Deprecated, this tab is declared in the foreach, so it's the last tab in the foreach
  1525. 'current_parent_id' => (int)Tab::getCurrentParentId(),
  1526. 'tabs' => $tabs,
  1527. 'install_dir_exists' => file_exists(_PS_ADMIN_DIR_.'/../install'),
  1528. 'pic_dir' => _THEME_PROD_PIC_DIR_,
  1529. 'controller_name' => htmlentities(Tools::getValue('controller')),
  1530. 'currentIndex' => self::$currentIndex,
  1531. 'bootstrap' => $this->bootstrap,
  1532. 'default_language' => (int)Configuration::get('PS_LANG_DEFAULT'),
  1533. 'display_addons_connection' => Tab::checkTabRights(Tab::getIdFromClassName('AdminModulesController'))
  1534. ));
  1535. $module = Module::getInstanceByName('themeconfigurator');
  1536. $lang = '';
  1537. if (Configuration::get('PS_REWRITING_SETTINGS') && count(Language::getLanguages(true)) > 1)
  1538. $lang = Language::getIsoById($this->context->employee->id_lang).'/';
  1539. if (is_object($module) && $module->active && (int)Configuration::get('PS_TC_ACTIVE') == 1 && $this->context->shop->getBaseURL())
  1540. {
  1541. $request =
  1542. 'live_configurator_token='.$module->getLiveConfiguratorToken()
  1543. .'&id_employee='.(int)$this->context->employee->id
  1544. .'&id_shop='.(int)$this->context->shop->id
  1545. .(Configuration::get('PS_TC_THEME') != '' ? '&theme='.Configuration::get('PS_TC_THEME') : '')
  1546. .(Configuration::get('PS_TC_FONT') != '' ? '&theme_font='.Configuration::get('PS_TC_FONT') : '');
  1547. $this->context->smarty->assign('base_url_tc', $this->context->link->getPageLink('index', null, $id_lang = null, $request));
  1548. }
  1549. }
  1550. /**
  1551. * Declare an action to use for each row in the list
  1552. */
  1553. public function addRowAction($action)
  1554. {
  1555. $action = strtolower($action);
  1556. $this->actions[] = $action;
  1557. }
  1558. /**
  1559. * Add an action to use for each row in the list
  1560. */
  1561. public function addRowActionSkipList($action, $list)
  1562. {
  1563. $action = strtolower($action);
  1564. $list = (array)$list;
  1565. if (array_key_exists($action, $this->list_skip_actions))
  1566. $this->list_skip_actions[$action] = array_merge($this->list_skip_actions[$action], $list);
  1567. else
  1568. $this->list_skip_actions[$action] = $list;
  1569. }
  1570. /**
  1571. * Assign smarty variables for all default views, list and form, then call other init functions
  1572. */
  1573. public function initContent()
  1574. {
  1575. if (!$this->viewAccess())
  1576. {
  1577. $this->errors[] = Tools::displayError('You do not have permission to view this.');
  1578. return;
  1579. }
  1580. $this->getLanguages();
  1581. $this->initToolbar();
  1582. $this->initTabModuleList();
  1583. $this->initPageHeaderToolbar();
  1584. if ($this->display == 'edit' || $this->display == 'add')
  1585. {
  1586. if (!$this->loadObject(true))
  1587. return;
  1588. $this->content .= $this->renderForm();
  1589. }
  1590. elseif ($this->display == 'view')
  1591. {
  1592. // Some controllers use the view action without an object
  1593. if ($this->className)
  1594. $this->loadObject(true);
  1595. $this->content .= $this->renderView();
  1596. }
  1597. elseif ($this->display == 'details')
  1598. $this->content .= $this->renderDetails();
  1599. elseif (!$this->ajax)
  1600. {
  1601. $this->content .= $this->renderModulesList();
  1602. $this->content .= $this->renderKpis();
  1603. $this->content .= $this->renderList();
  1604. $this->content .= $this->renderOptions();
  1605. // if we have to display the required fields form
  1606. if ($this->required_database)
  1607. $this->content .= $this->displayRequiredFields();
  1608. }
  1609. $this->context->smarty->assign(array(
  1610. 'content' => $this->content,
  1611. 'lite_display' => $this->lite_display,
  1612. 'url_post' => self::$currentIndex.'&token='.$this->token,
  1613. 'show_page_header_toolbar' => $this->show_page_header_toolbar,
  1614. 'page_header_toolbar_title' => $this->page_header_toolbar_title,
  1615. 'title' => $this->page_header_toolbar_title,
  1616. 'toolbar_btn' => $this->page_header_toolbar_btn,
  1617. 'page_header_toolbar_btn' => $this->page_header_toolbar_btn
  1618. ));
  1619. }
  1620. /**
  1621. * init tab modules list and add button in toolbar
  1622. */
  1623. protected function initTabModuleList()
  1624. {
  1625. if (!$this->isFresh(Module::CACHE_FILE_MUST_HAVE_MODULES_LIST, 86400))
  1626. @file_put_contents(_PS_ROOT_DIR_.Module::CACHE_FILE_MUST_HAVE_MODULES_LIST, Tools::addonsRequest('must-have'));
  1627. if (!$this->isFresh(Module::CACHE_FILE_TAB_MODULES_LIST, 604800))
  1628. $this->refresh(Module::CACHE_FILE_TAB_MODULES_LIST, 'http://'.Tab::TAB_MODULE_LIST_URL);
  1629. $this->tab_modules_list = Tab::getTabModulesList($this->id);
  1630. if (is_array($this->tab_modules_list['default_list']) && count($this->tab_modules_list['default_list']))
  1631. $this->filter_modules_list = $this->tab_modules_list['default_list'];
  1632. elseif (is_array($this->tab_modules_list['slider_list']) && count($this->tab_modules_list['slider_list']))
  1633. {
  1634. $this->addToolBarModulesListButton();
  1635. $this->addPageHeaderToolBarModulesListButton();
  1636. $this->context->smarty->assign(array(
  1637. 'tab_modules_list' => implode(',', $this->tab_modules_list['slider_list']),
  1638. 'admin_module_ajax_url' => $this->context->link->getAdminLink('AdminModules'),
  1639. 'back_tab_modules_list' => $this->context->link->getAdminLink(Tools::getValue('controller')),
  1640. 'tab_modules_open' => (int)Tools::getValue('tab_modules_open')
  1641. ));
  1642. }
  1643. }
  1644. protected function addPageHeaderToolBarModulesListButton()
  1645. {
  1646. $this->filterTabModuleList();
  1647. if (is_array($this->tab_modules_list['slider_list']) && count($this->tab_modules_list['slider_list']))
  1648. $this->page_header_toolbar_btn['modules-list'] = array(
  1649. 'href' => '#',
  1650. 'desc' => $this->l('Recommended Modules')
  1651. );
  1652. }
  1653. protected function addToolBarModulesListButton()
  1654. {
  1655. $this->filterTabModuleList();
  1656. if (is_array($this->tab_modules_list['slider_list']) && count($this->tab_modules_list['slider_list']))
  1657. $this->toolbar_btn['modules-list'] = array(
  1658. 'href' => '#',
  1659. 'desc' => $this->l('Recommended Modules')
  1660. );
  1661. }
  1662. protected function filterTabModuleList()
  1663. {
  1664. static $list_is_filtered = null;
  1665. if ($list_is_filtered !== null)
  1666. return;
  1667. if (!$this->isFresh(Module::CACHE_FILE_DEFAULT_COUNTRY_MODULES_LIST, 86400))
  1668. file_put_contents(_PS_ROOT_DIR_.Module::CACHE_FILE_DEFAULT_COUNTRY_MODULES_LIST, Tools::addonsRequest('native'));
  1669. if (!$this->isFresh(Module::CACHE_FILE_ALL_COUNTRY_MODULES_LIST, 86400))
  1670. file_put_contents(_PS_ROOT_DIR_.Module::CACHE_FILE_ALL_COUNTRY_MODULES_LIST, Tools::addonsRequest('native_all'));
  1671. if (!$this->isFresh(Module::CACHE_FILE_MUST_HAVE_MODULES_LIST, 86400))
  1672. @file_put_contents(_PS_ROOT_DIR_.Module::CACHE_FILE_MUST_HAVE_MODULES_LIST, Tools::addonsRequest('must-have'));
  1673. libxml_use_internal_errors(true);
  1674. $country_module_list = file_get_contents(_PS_ROOT_DIR_.Module::CACHE_FILE_DEFAULT_COUNTRY_MODULES_LIST);
  1675. $must_have_module_list = file_get_contents(_PS_ROOT_DIR_.Module::CACHE_FILE_MUST_HAVE_MODULES_LIST);
  1676. $all_module_list = array();
  1677. if (!empty($country_module_list) && $country_module_list_xml = simplexml_load_string($country_module_list))
  1678. {
  1679. $country_module_list_array = array();
  1680. if (is_object($country_module_list_xml->module))
  1681. foreach ($country_module_list_xml->module as $k => $m)
  1682. $all_module_list[] = (string)$m->name;
  1683. }
  1684. if (!empty($must_have_module_list) && $must_have_module_list_xml = simplexml_load_string($must_have_module_list))
  1685. {
  1686. $must_have_module_list_array = array();
  1687. if (is_object($country_module_list_xml->module))
  1688. foreach ($must_have_module_list_xml->module as $l => $mo)
  1689. $all_module_list[] = (string)$mo->name;
  1690. }
  1691. $this->tab_modules_list['slider_list'] = array_intersect($this->tab_modules_list['slider_list'], $all_module_list);
  1692. $list_is_filtered = true;
  1693. }
  1694. /**
  1695. * initialize the invalid doom page of death
  1696. *
  1697. * @return void
  1698. */
  1699. public function initCursedPage()
  1700. {
  1701. $this->layout = 'invalid_token.tpl';
  1702. }
  1703. /**
  1704. * Assign smarty variables for the footer
  1705. */
  1706. public function initFooter()
  1707. {
  1708. //RTL Support
  1709. //rtl.js overrides inline styles
  1710. //iso_code.css overrides default fonts for every language (optional)
  1711. if ($this->context->language->is_rtl)
  1712. {
  1713. $this->addJS(_PS_JS_DIR_.'rtl.js');
  1714. $this->addCSS(__PS_BASE_URI__.$this->admin_webpath.'/themes/'.$this->bo_theme.'/css/'.$this->context->language->iso_code.'.css', 'all', false);
  1715. }
  1716. // We assign js and css files on the last step before display template, because controller can add many js and css files
  1717. $this->context->smarty->assign('css_files', $this->css_files);
  1718. $this->context->smarty->assign('js_files', array_unique($this->js_files));
  1719. $this->context->smarty->assign(array(
  1720. 'ps_version' => _PS_VERSION_,
  1721. 'timer_start' => $this->timer_start,
  1722. 'iso_is_fr' => strtoupper($this->context->language->iso_code) == 'FR',
  1723. 'modals' => $this->renderModal(),
  1724. ));
  1725. }
  1726. public function initModal()
  1727. {
  1728. if ($this->logged_on_addons)
  1729. {
  1730. $this->context->smarty->assign(array(
  1731. 'logged_on_addons' => 1,
  1732. 'username_addons' => $this->context->cookie->username_addons
  1733. ));
  1734. }
  1735. // Iso needed to generate Addons login
  1736. $iso_code_caps = strtoupper($this->context->language->iso_code);
  1737. $this->context->smarty->assign(array(
  1738. 'check_url_fopen' => (ini_get('allow_url_fopen') ? 'ok' : 'ko'),
  1739. 'check_openssl' => (extension_loaded('openssl') ? 'ok' : 'ko'),
  1740. 'add_permission' => 1,
  1741. 'addons_register_link' => '//addons.prestashop.com/'.$this->context->language->iso_code.'/login?'
  1742. .'email='.urlencode($this->context->employee->email)
  1743. .'&firstname='.urlencode($this->context->employee->firstname)
  1744. .'&lastname='.urlencode($this->context->employee->lastname)
  1745. .'&website='.urlencode($this->context->shop->getBaseURL())
  1746. .'&utm_source=back-office&utm_medium=connect-to-addons'
  1747. .'&utm_campaign=back-office-'.Tools::strtoupper($this->context->language->iso_code)
  1748. .'&utm_content='.(defined('_PS_HOST_MODE_') ? 'cloud' : 'download').'#createnow',
  1749. 'addons_forgot_password_link' => '//addons.prestashop.com/'.$this->context->language->iso_code.'/forgot-your-password'
  1750. ));
  1751. $this->modals[] = array(
  1752. 'modal_id' => 'modal_addons_connect',
  1753. 'modal_class' => 'modal-md',
  1754. 'modal_title' => '<i class="icon-puzzle-piece"></i> <a target="_blank" href="http://addons.prestashop.com/'
  1755. .'?utm_source=back-office&utm_medium=modules'
  1756. .'&utm_campaign=back-office-'.Tools::strtoupper($this->context->language->iso_code)
  1757. .'&utm_content='.(defined('_PS_HOST_MODE_') ? 'cloud' : 'download').'>PrestaShop Addons</a>',
  1758. 'modal_content' => $this->context->smarty->fetch('controllers/modules/login_addons.tpl'),
  1759. );
  1760. }
  1761. public function renderModal()
  1762. {
  1763. $modal_render = '';
  1764. if (is_array($this->modals) && count($this->modals))
  1765. {
  1766. foreach ($this->modals as $modal)
  1767. {
  1768. $this->context->smarty->assign($modal);
  1769. $modal_render .= $this->context->smarty->fetch('modal.tpl');
  1770. }
  1771. }
  1772. return $modal_render;
  1773. }
  1774. public function renderModulesList()
  1775. {
  1776. // Load cache file modules list (natives and partners modules)
  1777. $xmlModules = false;
  1778. if (file_exists(_PS_ROOT_DIR_.Module::CACHE_FILE_MODULES_LIST))
  1779. $xmlModules = @simplexml_load_file(_PS_ROOT_DIR_.Module::CACHE_FILE_MODULES_LIST);
  1780. if ($xmlModules)
  1781. foreach ($xmlModules->children() as $xmlModule)
  1782. foreach ($xmlModule->children() as $module)
  1783. foreach ($module->attributes() as $key => $value)
  1784. {
  1785. if ($xmlModule->attributes() == 'native' && $key == 'name')
  1786. $this->list_natives_modules[] = (string)$value;
  1787. if ($xmlModule->attributes() == 'partner' && $key == 'name')
  1788. $this->list_partners_modules[] = (string)$value;
  1789. }
  1790. if ($this->getModulesList($this->filter_modules_list))
  1791. {
  1792. foreach ($this->modules_list as $key => $module)
  1793. {
  1794. if (in_array($module->name, $this->list_partners_modules))
  1795. $this->modules_list[$key]->type = 'addonsPartner';
  1796. if (isset($module->description_full) && trim($module->description_full) != '')
  1797. $module->show_quick_view = true;
  1798. }
  1799. $helper = new Helper();
  1800. return $helper->renderModulesList($this->modules_list);
  1801. }
  1802. }
  1803. /**
  1804. * Function used to render the list to display for this controller
  1805. */
  1806. public function renderList()
  1807. {
  1808. if (!($this->fields_list && is_array($this->fields_list)))
  1809. return false;
  1810. $this->getList($this->context->language->id);
  1811. // If list has 'active' field, we automatically create bulk action
  1812. if (isset($this->fields_list) && is_array($this->fields_list) && array_key_exists('active', $this->fields_list)
  1813. && !empty($this->fields_list['active']))
  1814. {
  1815. if (!is_array($this->bulk_actions))
  1816. $this->bulk_actions = array();
  1817. $this->bulk_actions = array_merge(array(
  1818. 'enableSelection' => array(
  1819. 'text' => $this->l('Enable selection'),
  1820. 'icon' => 'icon-power-off text-success'
  1821. ),
  1822. 'disableSelection' => array(
  1823. 'text' => $this->l('Disable selection'),
  1824. 'icon' => 'icon-power-off text-danger'
  1825. ),
  1826. 'divider' => array(
  1827. 'text' => 'divider'
  1828. )
  1829. ), $this->bulk_actions);
  1830. }
  1831. $helper = new HelperList();
  1832. // Empty list is ok
  1833. if (!is_array($this->_list))
  1834. {
  1835. $this->displayWarning($this->l('Bad SQL query', 'Helper').'<br />'.htmlspecialchars($this->_list_error));
  1836. return false;
  1837. }
  1838. $this->setHelperDisplay($helper);
  1839. $helper->_default_pagination = $this->_default_pagination;
  1840. $helper->_pagination = $this->_pagination;
  1841. $helper->tpl_vars = $this->getTemplateListVars();
  1842. $helper->tpl_delete_link_vars = $this->tpl_delete_link_vars;
  1843. // For compatibility reasons, we have to check standard actions in class attributes
  1844. foreach ($this->actions_available as $action)
  1845. if (!in_array($action, $this->actions) && isset($this->$action) && $this->$action)
  1846. $this->actions[] = $action;
  1847. $helper->is_cms = $this->is_cms;
  1848. $helper->sql = $this->_listsql;
  1849. $list = $helper->generateList($this->_list, $this->fields_list);
  1850. return $list;
  1851. }
  1852. public function getTemplateListVars()
  1853. {
  1854. return $this->tpl_list_vars;
  1855. }
  1856. /**
  1857. * Override to render the view page
  1858. */
  1859. public function renderView()
  1860. {
  1861. $helper = new HelperView($this);
  1862. $this->setHelperDisplay($helper);
  1863. $helper->tpl_vars = $this->getTemplateViewVars();
  1864. if (!is_null($this->base_tpl_view))
  1865. $helper->base_tpl = $this->base_tpl_view;
  1866. $view = $helper->generateView();
  1867. return $view;
  1868. }
  1869. public function getTemplateViewVars()
  1870. {
  1871. return $this->tpl_view_vars;
  1872. }
  1873. /**
  1874. * Override to render the view page
  1875. */
  1876. public function renderDetails()
  1877. {
  1878. return $this->renderList();
  1879. }
  1880. /**
  1881. * Function used to render the form for this controller
  1882. */
  1883. public function renderForm()
  1884. {
  1885. if (!$this->default_form_language)
  1886. $this->getLanguages();
  1887. if (Tools::getValue('submitFormAjax'))
  1888. $this->content .= $this->context->smarty->fetch('form_submit_ajax.tpl');
  1889. if ($this->fields_form && is_array($this->fields_form))
  1890. {
  1891. if (!$this->multiple_fieldsets)
  1892. $this->fields_form = array(array('form' => $this->fields_form));
  1893. // For add a fields via an override of $fields_form, use $fields_form_override
  1894. if (is_array($this->fields_form_override) && !empty($this->fields_form_override))
  1895. $this->fields_form[0]['form']['input'] = array_merge($this->fields_form[0]['form']['input'], $this->fields_form_override);
  1896. $fields_value = $this->getFieldsValue($this->object);
  1897. Hook::exec('action'.$this->controller_name.'FormModifier', array(
  1898. 'fields' => &$this->fields_form,
  1899. 'fields_value' => &$fields_value,
  1900. 'form_vars' => &$this->tpl_form_vars,
  1901. ));
  1902. $helper = new HelperForm($this);
  1903. $this->setHelperDisplay($helper);
  1904. $helper->fields_value = $fields_value;
  1905. $helper->submit_action = $this->submit_action;
  1906. $helper->tpl_vars = $this->getTemplateFormVars();
  1907. $helper->show_cancel_button = (isset($this->show_form_cancel_button)) ? $this->show_form_cancel_button : ($this->display == 'add' || $this->display == 'edit');
  1908. $back = Tools::safeOutput(Tools::getValue('back', ''));
  1909. if (empty($back))
  1910. $back = self::$currentIndex.'&token='.$this->token;
  1911. if (!Validate::isCleanHtml($back))
  1912. die(Tools::displayError());
  1913. $helper->back_url = $back;
  1914. !is_null($this->base_tpl_form) ? $helper->base_tpl = $this->base_tpl_form : '';
  1915. if ($this->tabAccess['view'])
  1916. {
  1917. if (Tools::getValue('back'))
  1918. $helper->tpl_vars['back'] = Tools::safeOutput(Tools::getValue('back'));
  1919. else
  1920. $helper->tpl_vars['back'] = Tools::safeOutput(Tools::getValue(self::$currentIndex.'&token='.$this->token));
  1921. }
  1922. $form = $helper->generateForm($this->fields_form);
  1923. return $form;
  1924. }
  1925. }
  1926. public function getTemplateFormVars()
  1927. {
  1928. return $this->tpl_form_vars;
  1929. }
  1930. public function renderKpis()
  1931. {
  1932. }
  1933. /**
  1934. * Function used to render the options for this controller
  1935. */
  1936. public function renderOptions()
  1937. {
  1938. Hook::exec('action'.$this->controller_name.'OptionsModifier', array(
  1939. 'options' => &$this->fields_options,
  1940. 'option_vars' => &$this->tpl_option_vars,
  1941. ));
  1942. if ($this->fields_options && is_array($this->fields_options))
  1943. {
  1944. if (isset($this->display) && $this->display != 'options' && $this->display != 'list')
  1945. $this->show_toolbar = false;
  1946. else
  1947. $this->display = 'options';
  1948. unset($this->toolbar_btn);
  1949. $this->initToolbar();
  1950. $helper = new HelperOptions($this);
  1951. $this->setHelperDisplay($helper);
  1952. $helper->id = $this->id;
  1953. $helper->tpl_vars = $this->tpl_option_vars;
  1954. $options = $helper->generateOptions($this->fields_options);
  1955. return $options;
  1956. }
  1957. }
  1958. /**
  1959. * this function set various display option for helper list
  1960. *
  1961. * @param Helper $helper
  1962. * @return void
  1963. */
  1964. public function setHelperDisplay(Helper $helper)
  1965. {
  1966. if (empty($this->toolbar_title))
  1967. $this->initToolbarTitle();
  1968. // tocheck
  1969. if ($this->object && $this->object->id)
  1970. $helper->id = $this->object->id;
  1971. // @todo : move that in Helper
  1972. $helper->title = is_array($this->toolbar_title) ? implode(' '.Configuration::get('PS_NAVIGATION_PIPE').' ', $this->toolbar_title) : $this->toolbar_title;
  1973. $helper->toolbar_btn = $this->toolbar_btn;
  1974. $helper->show_toolbar = $this->show_toolbar;
  1975. $helper->toolbar_scroll = $this->toolbar_scroll;
  1976. $helper->override_folder = $this->tpl_folder;
  1977. $helper->actions = $this->actions;
  1978. $helper->simple_header = $this->list_simple_header;
  1979. $helper->bulk_actions = $this->bulk_actions;
  1980. $helper->currentIndex = self::$currentIndex;
  1981. $helper->className = $this->className;
  1982. $helper->table = $this->table;
  1983. $helper->name_controller = Tools::getValue('controller');
  1984. $helper->orderBy = $this->_orderBy;
  1985. $helper->orderWay = $this->_orderWay;
  1986. $helper->listTotal = $this->_listTotal;
  1987. $helper->shopLink = $this->shopLink;
  1988. $helper->shopLinkType = $this->shopLinkType;
  1989. $helper->identifier = $this->identifier;
  1990. $helper->token = $this->token;
  1991. $helper->languages = $this->_languages;
  1992. $helper->specificConfirmDelete = $this->specificConfirmDelete;
  1993. $helper->imageType = $this->imageType;
  1994. $helper->no_link = $this->list_no_link;
  1995. $helper->colorOnBackground = $this->colorOnBackground;
  1996. $helper->ajax_params = (isset($this->ajax_params) ? $this->ajax_params : null);
  1997. $helper->default_form_language = $this->default_form_language;
  1998. $helper->allow_employee_form_lang = $this->allow_employee_form_lang;
  1999. $helper->multiple_fieldsets = $this->multiple_fieldsets;
  2000. $helper->row_hover = $this->row_hover;
  2001. $helper->position_identifier = $this->position_identifier;
  2002. $helper->position_group_identifier = $this->position_group_identifier;
  2003. $helper->controller_name = $this->controller_name;
  2004. $helper->list_id = isset($this->list_id) ? $this->list_id : $this->table;
  2005. $helper->bootstrap = $this->bootstrap;
  2006. // For each action, try to add the corresponding skip elements list
  2007. $helper->list_skip_actions = $this->list_skip_actions;
  2008. $this->helper = $helper;
  2009. }
  2010. /* @deprecated 1.6.0 */
  2011. public function setDeprecatedMedia()
  2012. {
  2013. }
  2014. public function setMedia()
  2015. {
  2016. //Bootstrap + Specific Admin Theme
  2017. $this->addCSS(__PS_BASE_URI__.$this->admin_webpath.'/themes/'.$this->bo_theme.'/css/'.$this->bo_css, 'all', 0);
  2018. $this->addCSS(__PS_BASE_URI__.$this->admin_webpath.'/themes/'.$this->bo_theme.'/css/overrides.css', 'all', 99);
  2019. $this->addJquery();
  2020. $this->addjQueryPlugin(array('scrollTo', 'alerts', 'chosen', 'autosize', 'fancybox' ));
  2021. $this->addjQueryPlugin('growl', null, false);
  2022. $this->addJqueryUI(array('ui.slider', 'ui.datepicker'));
  2023. $this->addJS(array(
  2024. _PS_JS_DIR_.'admin.js',
  2025. _PS_JS_DIR_.'tools.js',
  2026. _PS_JS_DIR_.'jquery/plugins/timepicker/jquery-ui-timepicker-addon.js'
  2027. ));
  2028. //loads specific javascripts for the admin theme
  2029. $this->addJS(__PS_BASE_URI__.$this->admin_webpath.'/themes/'.$this->bo_theme.'/js/vendor/bootstrap.min.js');
  2030. $this->addJS(__PS_BASE_URI__.$this->admin_webpath.'/themes/'.$this->bo_theme.'/js/vendor/modernizr.min.js');
  2031. $this->addJS(__PS_BASE_URI__.$this->admin_webpath.'/themes/'.$this->bo_theme.'/js/modernizr-loads.js');
  2032. $this->addJS(__PS_BASE_URI__.$this->admin_webpath.'/themes/'.$this->bo_theme.'/js/vendor/moment-with-langs.min.js');
  2033. if (!$this->lite_display)
  2034. $this->addJS(__PS_BASE_URI__.$this->admin_webpath.'/themes/'.$this->bo_theme.'/js/help.js');
  2035. if (!Tools::getValue('submitFormAjax'))
  2036. $this->addJs(_PS_JS_DIR_.'admin/notifications.js');
  2037. // Execute Hook AdminController SetMedia
  2038. Hook::exec('actionAdminControllerSetMedia');
  2039. }
  2040. /**
  2041. * non-static method which uses AdminController::translate()
  2042. *
  2043. * @param mixed $string term or expression in english
  2044. * @param string $class name of the class
  2045. * @param boolan $addslashes if set to true, the return value will pass through addslashes(). Otherwise, stripslashes().
  2046. * @param boolean $htmlentities if set to true(default), the return value will pass through htmlentities($string, ENT_QUOTES, 'utf-8')
  2047. * @return string the translation if available, or the english default text.
  2048. */
  2049. protected function l($string, $class = null, $addslashes = false, $htmlentities = true)
  2050. {
  2051. if ($class === null || $class == 'AdminTab')
  2052. $class = substr(get_class($this), 0, -10);
  2053. // classname has changed, from AdminXXX to AdminXXXController, so we remove 10 characters and we keep same keys
  2054. elseif (strtolower(substr($class, -10)) == 'controller')
  2055. $class = substr($class, 0, -10);
  2056. return Translate::getAdminTranslation($string, $class, $addslashes, $htmlentities);
  2057. }
  2058. /**
  2059. * Init context and dependencies, handles POST and GET
  2060. */
  2061. public function init()
  2062. {
  2063. // Has to be removed for the next Prestashop version
  2064. global $currentIndex;
  2065. parent::init();
  2066. if (Tools::getValue('ajax'))
  2067. $this->ajax = '1';
  2068. /* Server Params */
  2069. $protocol_link = (Tools::usingSecureMode() && Configuration::get('PS_SSL_ENABLED')) ? 'https://' : 'http://';
  2070. $protocol_content = (Tools::usingSecureMode() && Configuration::get('PS_SSL_ENABLED')) ? 'https://' : 'http://';
  2071. $this->context->link = new Link($protocol_link, $protocol_content);
  2072. if (isset($_GET['logout']))
  2073. $this->context->employee->logout();
  2074. if (isset(Context::getContext()->cookie->last_activity))
  2075. {
  2076. if ($this->context->cookie->last_activity + 900 < time())
  2077. $this->context->employee->logout();
  2078. else
  2079. $this->context->cookie->last_activity = time();
  2080. }
  2081. if ($this->controller_name != 'AdminLogin' && (!isset($this->context->employee) || !$this->context->employee->isLoggedBack()))
  2082. {
  2083. if (isset($this->context->employee))
  2084. $this->context->employee->logout();
  2085. $email = false;
  2086. if (Tools::getValue('email') && Validate::isEmail(Tools::getValue('email')))
  2087. $email = Tools::getValue('email');
  2088. Tools::redirectAdmin($this->context->link->getAdminLink('AdminLogin').((!isset($_GET['logout']) && $this->controller_name != 'AdminNotFound' && Tools::getValue('controller')) ? '&redirect='.$this->controller_name : '').($email ? '&email='.$email : ''));
  2089. }
  2090. // Set current index
  2091. $current_index = 'index.php'.(($controller = Tools::getValue('controller')) ? '?controller='.$controller : '');
  2092. if ($back = Tools::getValue('back'))
  2093. $current_index .= '&back='.urlencode($back);
  2094. self::$currentIndex = $current_index;
  2095. $currentIndex = $current_index;
  2096. if ((int)Tools::getValue('liteDisplaying'))
  2097. {
  2098. $this->display_header = false;
  2099. $this->display_footer = false;
  2100. $this->content_only = false;
  2101. $this->lite_display = true;
  2102. }
  2103. if ($this->ajax && method_exists($this, 'ajaxPreprocess'))
  2104. $this->ajaxPreProcess();
  2105. $this->context->smarty->assign(array(
  2106. 'table' => $this->table,
  2107. 'current' => self::$currentIndex,
  2108. 'token' => $this->token,
  2109. 'host_mode' => defined('_PS_HOST_MODE_') ? 1 : 0,
  2110. 'stock_management' => (int)Configuration::get('PS_STOCK_MANAGEMENT')
  2111. ));
  2112. if ($this->display_header)
  2113. $this->context->smarty->assign('displayBackOfficeHeader', Hook::exec('displayBackOfficeHeader', array()));
  2114. $this->context->smarty->assign(array(
  2115. 'displayBackOfficeTop' => Hook::exec('displayBackOfficeTop', array()),
  2116. 'submit_form_ajax' => (int)Tools::getValue('submitFormAjax')
  2117. ));
  2118. Employee::setLastConnectionDate($this->context->employee->id);
  2119. $this->initProcess();
  2120. $this->initBreadcrumbs();
  2121. $this->initModal();
  2122. }
  2123. public function initShopContext()
  2124. {
  2125. if (!$this->context->employee->isLoggedBack())
  2126. return;
  2127. // Change shop context ?
  2128. if (Shop::isFeatureActive() && Tools::getValue('setShopContext') !== false)
  2129. {
  2130. $this->context->cookie->shopContext = Tools::getValue('setShopContext');
  2131. $url = parse_url($_SERVER['REQUEST_URI']);
  2132. $query = (isset($url['query'])) ? $url['query'] : '';
  2133. parse_str($query, $parse_query);
  2134. unset($parse_query['setShopContext'], $parse_query['conf']);
  2135. $this->redirect_after = $url['path'].'?'.http_build_query($parse_query, '', '&');
  2136. }
  2137. elseif (!Shop::isFeatureActive())
  2138. $this->context->cookie->shopContext = 's-'.(int)Configuration::get('PS_SHOP_DEFAULT');
  2139. elseif (Shop::getTotalShops(false, null) < 2)
  2140. $this->context->cookie->shopContext = 's-'.(int)$this->context->employee->getDefaultShopID();
  2141. $shop_id = '';
  2142. Shop::setContext(Shop::CONTEXT_ALL);
  2143. if ($this->context->cookie->shopContext)
  2144. {
  2145. $split = explode('-', $this->context->cookie->shopContext);
  2146. if (count($split) == 2)
  2147. {
  2148. if ($split[0] == 'g')
  2149. {
  2150. if ($this->context->employee->hasAuthOnShopGroup($split[1]))
  2151. Shop::setContext(Shop::CONTEXT_GROUP, $split[1]);
  2152. else
  2153. {
  2154. $shop_id = $this->context->employee->getDefaultShopID();
  2155. Shop::setContext(Shop::CONTEXT_SHOP, $shop_id);
  2156. }
  2157. }
  2158. elseif (Shop::getShop($split[1]) && $this->context->employee->hasAuthOnShop($split[1]))
  2159. {
  2160. $shop_id = $split[1];
  2161. Shop::setContext(Shop::CONTEXT_SHOP, $shop_id);
  2162. }
  2163. else
  2164. {
  2165. $shop_id = $this->context->employee->getDefaultShopID();
  2166. Shop::setContext(Shop::CONTEXT_SHOP, $shop_id);
  2167. }
  2168. }
  2169. }
  2170. // Check multishop context and set right context if need
  2171. if (!($this->multishop_context & Shop::getContext()))
  2172. {
  2173. if (Shop::getContext() == Shop::CONTEXT_SHOP && !($this->multishop_context & Shop::CONTEXT_SHOP))
  2174. Shop::setContext(Shop::CONTEXT_GROUP, Shop::getContextShopGroupID());
  2175. if (Shop::getContext() == Shop::CONTEXT_GROUP && !($this->multishop_context & Shop::CONTEXT_GROUP))
  2176. Shop::setContext(Shop::CONTEXT_ALL);
  2177. }
  2178. // Replace existing shop if necessary
  2179. if (!$shop_id)
  2180. $this->context->shop = new Shop((int)Configuration::get('PS_SHOP_DEFAULT'));
  2181. elseif ($this->context->shop->id != $shop_id)
  2182. $this->context->shop = new Shop((int)$shop_id);
  2183. if ($this->context->shop->id_theme != $this->context->theme->id)
  2184. $this->context->theme = new Theme((int)$this->context->shop->id_theme);
  2185. // Replace current default country
  2186. $this->context->country = new Country((int)Configuration::get('PS_COUNTRY_DEFAULT'));
  2187. }
  2188. /**
  2189. * Retrieve GET and POST value and translate them to actions
  2190. */
  2191. public function initProcess()
  2192. {
  2193. if (!isset($this->list_id))
  2194. $this->list_id = $this->table;
  2195. // Manage list filtering
  2196. if (Tools::isSubmit('submitFilter'.$this->list_id)
  2197. || $this->context->cookie->{'submitFilter'.$this->list_id} !== false
  2198. || Tools::getValue($this->list_id.'Orderby')
  2199. || Tools::getValue($this->list_id.'Orderway'))
  2200. $this->filter = true;
  2201. $this->id_object = (int)Tools::getValue($this->identifier);
  2202. /* Delete object image */
  2203. if (isset($_GET['deleteImage']))
  2204. {
  2205. if ($this->tabAccess['delete'] === '1')
  2206. $this->action = 'delete_image';
  2207. else
  2208. $this->errors[] = Tools::displayError('You do not have permission to delete this.');
  2209. }
  2210. /* Delete object */
  2211. elseif (isset($_GET['delete'.$this->table]))
  2212. {
  2213. if ($this->tabAccess['delete'] === '1')
  2214. $this->action = 'delete';
  2215. else
  2216. $this->errors[] = Tools::displayError('You do not have permission to delete this.');
  2217. }
  2218. /* Change object statuts (active, inactive) */
  2219. elseif ((isset($_GET['status'.$this->table]) || isset($_GET['status'])) && Tools::getValue($this->identifier))
  2220. {
  2221. if ($this->tabAccess['edit'] === '1')
  2222. $this->action = 'status';
  2223. else
  2224. $this->errors[] = Tools::displayError('You do not have permission to edit this.');
  2225. }
  2226. /* Move an object */
  2227. elseif (isset($_GET['position']))
  2228. {
  2229. if ($this->tabAccess['edit'] == '1')
  2230. $this->action = 'position';
  2231. else
  2232. $this->errors[] = Tools::displayError('You do not have permission to edit this.');
  2233. }
  2234. elseif (Tools::isSubmit('submitAdd'.$this->table)
  2235. || Tools::isSubmit('submitAdd'.$this->table.'AndStay')
  2236. || Tools::isSubmit('submitAdd'.$this->table.'AndPreview')
  2237. || Tools::isSubmit('submitAdd'.$this->table.'AndBackToParent'))
  2238. {
  2239. // case 1: updating existing entry
  2240. if ($this->id_object)
  2241. {
  2242. if ($this->tabAccess['edit'] === '1')
  2243. {
  2244. $this->action = 'save';
  2245. if (Tools::isSubmit('submitAdd'.$this->table.'AndStay'))
  2246. $this->display = 'edit';
  2247. else
  2248. $this->display = 'list';
  2249. }
  2250. else
  2251. $this->errors[] = Tools::displayError('You do not have permission to edit this.');
  2252. }
  2253. // case 2: creating new entry
  2254. else
  2255. {
  2256. if ($this->tabAccess['add'] === '1')
  2257. {
  2258. $this->action = 'save';
  2259. if (Tools::isSubmit('submitAdd'.$this->table.'AndStay'))
  2260. $this->display = 'edit';
  2261. else
  2262. $this->display = 'list';
  2263. }
  2264. else
  2265. $this->errors[] = Tools::displayError('You do not have permission to add this.');
  2266. }
  2267. }
  2268. elseif (isset($_GET['add'.$this->table]))
  2269. {
  2270. if ($this->tabAccess['add'] === '1')
  2271. {
  2272. $this->action = 'new';
  2273. $this->display = 'add';
  2274. }
  2275. else
  2276. $this->errors[] = Tools::displayError('You do not have permission to add this.');
  2277. }
  2278. elseif (isset($_GET['update'.$this->table]) && isset($_GET[$this->identifier]))
  2279. {
  2280. $this->display = 'edit';
  2281. if ($this->tabAccess['edit'] !== '1')
  2282. $this->errors[] = Tools::displayError('You do not have permission to edit this.');
  2283. }
  2284. elseif (isset($_GET['view'.$this->table]))
  2285. {
  2286. if ($this->tabAccess['view'] === '1')
  2287. {
  2288. $this->display = 'view';
  2289. $this->action = 'view';
  2290. }
  2291. else
  2292. $this->errors[] = Tools::displayError('You do not have permission to view this.');
  2293. }
  2294. elseif (isset($_GET['details'.$this->table]))
  2295. {
  2296. if ($this->tabAccess['view'] === '1')
  2297. {
  2298. $this->display = 'details';
  2299. $this->action = 'details';
  2300. }
  2301. else
  2302. $this->errors[] = Tools::displayError('You do not have permission to view this.');
  2303. }
  2304. elseif (isset($_GET['export'.$this->table]))
  2305. {
  2306. if ($this->tabAccess['view'] === '1')
  2307. $this->action = 'export';
  2308. }
  2309. /* Cancel all filters for this tab */
  2310. elseif (isset($_POST['submitReset'.$this->list_id]))
  2311. $this->action = 'reset_filters';
  2312. /* Submit options list */
  2313. elseif (Tools::isSubmit('submitOptions'.$this->table) || Tools::isSubmit('submitOptions'))
  2314. {
  2315. $this->display = 'options';
  2316. if ($this->tabAccess['edit'] === '1')
  2317. $this->action = 'update_options';
  2318. else
  2319. $this->errors[] = Tools::displayError('You do not have permission to edit this.');
  2320. }
  2321. elseif (Tools::getValue('action') && method_exists($this, 'process'.ucfirst(Tools::toCamelCase(Tools::getValue('action')))))
  2322. $this->action = Tools::getValue('action');
  2323. elseif (Tools::isSubmit('submitFields') && $this->required_database && $this->tabAccess['add'] === '1' && $this->tabAccess['delete'] === '1')
  2324. $this->action = 'update_fields';
  2325. elseif (is_array($this->bulk_actions))
  2326. {
  2327. $submit_bulk_actions = array_merge(array(
  2328. 'enableSelection' => array(
  2329. 'text' => $this->l('Enable selection'),
  2330. 'icon' => 'icon-power-off text-success'
  2331. ),
  2332. 'disableSelection' => array(
  2333. 'text' => $this->l('Disable selection'),
  2334. 'icon' => 'icon-power-off text-danger'
  2335. )
  2336. ), $this->bulk_actions);
  2337. foreach ($submit_bulk_actions as $bulk_action => $params)
  2338. {
  2339. if (Tools::isSubmit('submitBulk'.$bulk_action.$this->table) || Tools::isSubmit('submitBulk'.$bulk_action))
  2340. {
  2341. if ($bulk_action === 'delete')
  2342. {
  2343. if ($this->tabAccess['delete'] === '1')
  2344. {
  2345. $this->action = 'bulk'.$bulk_action;
  2346. $this->boxes = Tools::getValue($this->table.'Box');
  2347. if (empty($this->boxes) && $this->table == 'attribute')
  2348. $this->boxes = Tools::getValue($this->table.'_valuesBox');
  2349. }
  2350. else
  2351. $this->errors[] = Tools::displayError('You do not have permission to delete this.');
  2352. break;
  2353. }
  2354. elseif ($this->tabAccess['edit'] === '1')
  2355. {
  2356. $this->action = 'bulk'.$bulk_action;
  2357. $this->boxes = Tools::getValue($this->table.'Box');
  2358. }
  2359. else
  2360. $this->errors[] = Tools::displayError('You do not have permission to edit this.');
  2361. break;
  2362. }
  2363. elseif (Tools::isSubmit('submitBulk'))
  2364. {
  2365. if ($bulk_action === 'delete')
  2366. {
  2367. if ($this->tabAccess['delete'] === '1')
  2368. {
  2369. $this->action = 'bulk'.$bulk_action;
  2370. $this->boxes = Tools::getValue($this->table.'Box');
  2371. }
  2372. else
  2373. $this->errors[] = Tools::displayError('You do not have permission to delete this.');
  2374. break;
  2375. }
  2376. elseif ($this->tabAccess['edit'] === '1')
  2377. {
  2378. $this->action = 'bulk'.Tools::getValue('select_submitBulk');
  2379. $this->boxes = Tools::getValue($this->table.'Box');
  2380. }
  2381. else
  2382. $this->errors[] = Tools::displayError('You do not have permission to edit this.');
  2383. break;
  2384. }
  2385. }
  2386. }
  2387. elseif (!empty($this->fields_options) && empty($this->fields_list))
  2388. $this->display = 'options';
  2389. }
  2390. /**
  2391. * Get the current objects' list form the database
  2392. *
  2393. * @param integer $id_lang Language used for display
  2394. * @param string $order_by ORDER BY clause
  2395. * @param string $_orderWay Order way (ASC, DESC)
  2396. * @param integer $start Offset in LIMIT clause
  2397. * @param integer $limit Row count in LIMIT clause
  2398. */
  2399. public function getList($id_lang, $order_by = null, $order_way = null, $start = 0, $limit = null, $id_lang_shop = false)
  2400. {
  2401. Hook::exec('action'.$this->controller_name.'ListingFieldsModifier', array(
  2402. 'select' => &$this->_select,
  2403. 'join' => &$this->_join,
  2404. 'where' => &$this->_where,
  2405. 'group_by' => &$this->_group,
  2406. 'order_by' => &$this->_orderBy,
  2407. 'order_way' => &$this->_orderWay,
  2408. 'fields' => &$this->fields_list,
  2409. ));
  2410. if (!isset($this->list_id))
  2411. $this->list_id = $this->table;
  2412. /* Manage default params values */
  2413. $use_limit = true;
  2414. if ($limit === false)
  2415. $use_limit = false;
  2416. elseif (empty($limit))
  2417. {
  2418. if (isset($this->context->cookie->{$this->list_id.'_pagination'}) && $this->context->cookie->{$this->list_id.'_pagination'})
  2419. $limit = $this->context->cookie->{$this->list_id.'_pagination'};
  2420. else
  2421. $limit = $this->_default_pagination;
  2422. }
  2423. if (!Validate::isTableOrIdentifier($this->table))
  2424. throw new PrestaShopException(sprintf('Table name %s is invalid:', $this->table));
  2425. $prefix = str_replace(array('admin', 'controller'), '', Tools::strtolower(get_class($this)));
  2426. if (empty($order_by))
  2427. {
  2428. if ($this->context->cookie->{$prefix.$this->list_id.'Orderby'})
  2429. $order_by = $this->context->cookie->{$prefix.$this->list_id.'Orderby'};
  2430. elseif ($this->_orderBy)
  2431. $order_by = $this->_orderBy;
  2432. else
  2433. $order_by = $this->_defaultOrderBy;
  2434. }
  2435. if (empty($order_way))
  2436. {
  2437. if ($this->context->cookie->{$prefix.$this->list_id.'Orderway'})
  2438. $order_way = $this->context->cookie->{$prefix.$this->list_id.'Orderway'};
  2439. elseif ($this->_orderWay)
  2440. $order_way = $this->_orderWay;
  2441. else
  2442. $order_way = $this->_defaultOrderWay;
  2443. }
  2444. $limit = (int)Tools::getValue($this->list_id.'_pagination', $limit);
  2445. if (in_array($limit, $this->_pagination) && $limit != $this->_default_pagination)
  2446. $this->context->cookie->{$this->list_id.'_pagination'} = $limit;
  2447. else
  2448. unset($this->context->cookie->{$this->list_id.'_pagination'});
  2449. /* Check params validity */
  2450. if (!Validate::isOrderBy($order_by) || !Validate::isOrderWay($order_way)
  2451. || !is_numeric($start) || !is_numeric($limit)
  2452. || !Validate::isUnsignedId($id_lang))
  2453. throw new PrestaShopException('get list params is not valid');
  2454. if (!isset($this->fields_list[$order_by]['order_key']) && isset($this->fields_list[$order_by]['filter_key']))
  2455. $this->fields_list[$order_by]['order_key'] = $this->fields_list[$order_by]['filter_key'];
  2456. if (isset($this->fields_list[$order_by]) && isset($this->fields_list[$order_by]['order_key']))
  2457. $order_by = $this->fields_list[$order_by]['order_key'];
  2458. /* Determine offset from current page */
  2459. $start = 0;
  2460. if ((int)Tools::getValue('submitFilter'.$this->list_id))
  2461. $start = ((int)Tools::getValue('submitFilter'.$this->list_id) - 1) * $limit;
  2462. elseif (empty($start) && isset($this->context->cookie->{$this->list_id.'_start'}) && Tools::isSubmit('export'.$this->table))
  2463. $start = $this->context->cookie->{$this->list_id.'_start'};
  2464. // Either save or reset the offset in the cookie
  2465. if ($start)
  2466. $this->context->cookie->{$this->list_id.'_start'} = $start;
  2467. elseif (isset($this->context->cookie->{$this->list_id.'_start'}))
  2468. unset($this->context->cookie->{$this->list_id.'_start'});
  2469. /* Cache */
  2470. $this->_lang = (int)$id_lang;
  2471. $this->_orderBy = $order_by;
  2472. if (preg_match('/[.!]/', $order_by))
  2473. {
  2474. $order_by_split = preg_split('/[.!]/', $order_by);
  2475. $order_by = bqSQL($order_by_split[0]).'.`'.bqSQL($order_by_split[1]).'`';
  2476. }
  2477. elseif ($order_by)
  2478. $order_by = '`'.bqSQL($order_by).'`';
  2479. $this->_orderWay = Tools::strtoupper($order_way);
  2480. /* SQL table : orders, but class name is Order */
  2481. $sql_table = $this->table == 'order' ? 'orders' : $this->table;
  2482. // Add SQL shop restriction
  2483. $select_shop = $join_shop = $where_shop = '';
  2484. if ($this->shopLinkType)
  2485. {
  2486. $select_shop = ', shop.name as shop_name ';
  2487. $join_shop = ' LEFT JOIN '._DB_PREFIX_.$this->shopLinkType.' shop
  2488. ON a.id_'.$this->shopLinkType.' = shop.id_'.$this->shopLinkType;
  2489. $where_shop = Shop::addSqlRestriction($this->shopShareDatas, 'a', $this->shopLinkType);
  2490. }
  2491. if ($this->multishop_context && Shop::isTableAssociated($this->table) && !empty($this->className))
  2492. {
  2493. if (Shop::getContext() != Shop::CONTEXT_ALL || !$this->context->employee->isSuperAdmin())
  2494. {
  2495. $test_join = !preg_match('#`?'.preg_quote(_DB_PREFIX_.$this->table.'_shop').'`? *sa#', $this->_join);
  2496. if (Shop::isFeatureActive() && $test_join && Shop::isTableAssociated($this->table))
  2497. {
  2498. $this->_where .= ' AND a.'.$this->identifier.' IN (
  2499. SELECT sa.'.$this->identifier.'
  2500. FROM `'._DB_PREFIX_.$this->table.'_shop` sa
  2501. WHERE sa.id_shop IN ('.implode(', ', Shop::getContextListShopID()).')
  2502. )';
  2503. }
  2504. }
  2505. }
  2506. /* Query in order to get results with all fields */
  2507. $lang_join = '';
  2508. if ($this->lang)
  2509. {
  2510. $lang_join = 'LEFT JOIN `'._DB_PREFIX_.$this->table.'_lang` b ON (b.`'.$this->identifier.'` = a.`'.$this->identifier.'` AND b.`id_lang` = '.(int)$id_lang;
  2511. if ($id_lang_shop)
  2512. {
  2513. if (!Shop::isFeatureActive())
  2514. $lang_join .= ' AND b.`id_shop` = '.(int)Configuration::get('PS_SHOP_DEFAULT');
  2515. elseif (Shop::getContext() == Shop::CONTEXT_SHOP)
  2516. $lang_join .= ' AND b.`id_shop` = '.(int)$id_lang_shop;
  2517. else
  2518. $lang_join .= ' AND b.`id_shop` = a.id_shop_default';
  2519. }
  2520. $lang_join .= ')';
  2521. }
  2522. $having_clause = '';
  2523. if (isset($this->_filterHaving) || isset($this->_having))
  2524. {
  2525. $having_clause = ' HAVING ';
  2526. if (isset($this->_filterHaving))
  2527. $having_clause .= ltrim($this->_filterHaving, ' AND ');
  2528. if (isset($this->_having))
  2529. $having_clause .= $this->_having.' ';
  2530. }
  2531. do
  2532. {
  2533. $this->_listsql = '
  2534. SELECT SQL_CALC_FOUND_ROWS
  2535. '.($this->_tmpTableFilter ? ' * FROM (SELECT ' : '');
  2536. if ($this->explicitSelect)
  2537. {
  2538. foreach ($this->fields_list as $key => $array_value)
  2539. {
  2540. // Add it only if it is not already in $this->_select
  2541. if (isset($this->_select) && preg_match('/[\s]`?'.preg_quote($key, '/').'`?\s*,/', $this->_select))
  2542. continue;
  2543. if (isset($array_value['filter_key']))
  2544. $this->_listsql .= str_replace('!', '.', $array_value['filter_key']).' as '.$key.',';
  2545. elseif ($key == 'id_'.$this->table)
  2546. $this->_listsql .= 'a.`'.bqSQL($key).'`,';
  2547. elseif ($key != 'image' && !preg_match('/'.preg_quote($key, '/').'/i', $this->_select))
  2548. $this->_listsql .= '`'.bqSQL($key).'`,';
  2549. }
  2550. $this->_listsql = rtrim($this->_listsql, ',');
  2551. }
  2552. else
  2553. $this->_listsql .= ($this->lang ? 'b.*,' : '').' a.*';
  2554. $this->_listsql .= '
  2555. '.(isset($this->_select) ? ', '.rtrim($this->_select, ', ') : '').$select_shop.'
  2556. FROM `'._DB_PREFIX_.$sql_table.'` a
  2557. '.$lang_join.'
  2558. '.(isset($this->_join) ? $this->_join.' ' : '').'
  2559. '.$join_shop.'
  2560. WHERE 1 '.(isset($this->_where) ? $this->_where.' ' : '').($this->deleted ? 'AND a.`deleted` = 0 ' : '').
  2561. (isset($this->_filter) ? $this->_filter : '').$where_shop.'
  2562. '.(isset($this->_group) ? $this->_group.' ' : '').'
  2563. '.$having_clause.'
  2564. ORDER BY '.((str_replace('`', '', $order_by) == $this->identifier) ? 'a.' : '').$order_by.' '.pSQL($order_way).
  2565. ($this->_tmpTableFilter ? ') tmpTable WHERE 1'.$this->_tmpTableFilter : '').
  2566. (($use_limit === true) ? ' LIMIT '.(int)$start.','.(int)$limit : '');
  2567. $this->_list = Db::getInstance()->executeS($this->_listsql, true, false);
  2568. if ($this->_list === false)
  2569. {
  2570. $this->_list_error = Db::getInstance()->getMsgError();
  2571. break;
  2572. }
  2573. $this->_listTotal = Db::getInstance()->getValue('SELECT FOUND_ROWS() AS `'._DB_PREFIX_.$this->table.'`', false);
  2574. if ($use_limit === true)
  2575. {
  2576. $start = (int)$start - (int)$limit;
  2577. if ($start < 0)
  2578. break;
  2579. }
  2580. else
  2581. break;
  2582. } while (empty($this->_list));
  2583. Hook::exec('action'.$this->controller_name.'ListingResultsModifier', array(
  2584. 'list' => &$this->_list,
  2585. 'list_total' => &$this->_listTotal,
  2586. ));
  2587. }
  2588. public function getModulesList($filter_modules_list)
  2589. {
  2590. if (!is_array($filter_modules_list) && !is_null($filter_modules_list))
  2591. $filter_modules_list = array($filter_modules_list);
  2592. if (!count($filter_modules_list))
  2593. return false; //if there is no modules to display just return false;
  2594. $all_modules = Module::getModulesOnDisk(true);
  2595. $this->modules_list = array();
  2596. foreach ($all_modules as $module)
  2597. {
  2598. $perm = true;
  2599. if ($module->id)
  2600. $perm &= Module::getPermissionStatic($module->id, 'configure');
  2601. else
  2602. {
  2603. $id_admin_module = Tab::getIdFromClassName('AdminModules');
  2604. $access = Profile::getProfileAccess($this->context->employee->id_profile, $id_admin_module);
  2605. if (!$access['edit'])
  2606. $perm &= false;
  2607. }
  2608. if (in_array($module->name, $filter_modules_list) && $perm)
  2609. {
  2610. $this->fillModuleData($module, 'array');
  2611. $this->modules_list[array_search($module->name, $filter_modules_list)] = $module;
  2612. }
  2613. }
  2614. ksort($this->modules_list);
  2615. if (count($this->modules_list))
  2616. return true;
  2617. return false; //no module found on disk just return false;
  2618. }
  2619. public function getLanguages()
  2620. {
  2621. $cookie = $this->context->cookie;
  2622. $this->allow_employee_form_lang = (int)Configuration::get('PS_BO_ALLOW_EMPLOYEE_FORM_LANG');
  2623. if ($this->allow_employee_form_lang && !$cookie->employee_form_lang)
  2624. $cookie->employee_form_lang = (int)Configuration::get('PS_LANG_DEFAULT');
  2625. $lang_exists = false;
  2626. $this->_languages = Language::getLanguages(false);
  2627. foreach ($this->_languages as $lang)
  2628. if (isset($cookie->employee_form_lang) && $cookie->employee_form_lang == $lang['id_lang'])
  2629. $lang_exists = true;
  2630. $this->default_form_language = $lang_exists ? (int)$cookie->employee_form_lang : (int)Configuration::get('PS_LANG_DEFAULT');
  2631. foreach ($this->_languages as $k => $language)
  2632. $this->_languages[$k]['is_default'] = ((int)($language['id_lang'] == $this->default_form_language));
  2633. return $this->_languages;
  2634. }
  2635. /**
  2636. * Return the list of fields value
  2637. *
  2638. * @param object $obj Object
  2639. * @return array
  2640. */
  2641. public function getFieldsValue($obj)
  2642. {
  2643. foreach ($this->fields_form as $fieldset)
  2644. if (isset($fieldset['form']['input']))
  2645. foreach ($fieldset['form']['input'] as $input)
  2646. if (!isset($this->fields_value[$input['name']]))
  2647. if (isset($input['type']) && $input['type'] == 'shop')
  2648. {
  2649. if ($obj->id)
  2650. {
  2651. $result = Shop::getShopById((int)$obj->id, $this->identifier, $this->table);
  2652. foreach ($result as $row)
  2653. $this->fields_value['shop'][$row['id_'.$input['type']]][] = $row['id_shop'];
  2654. }
  2655. }
  2656. elseif (isset($input['lang']) && $input['lang'])
  2657. foreach ($this->_languages as $language)
  2658. {
  2659. $fieldValue = $this->getFieldValue($obj, $input['name'], $language['id_lang']);
  2660. if (empty($fieldValue))
  2661. {
  2662. if (isset($input['default_value']) && is_array($input['default_value']) && isset($input['default_value'][$language['id_lang']]))
  2663. $fieldValue = $input['default_value'][$language['id_lang']];
  2664. elseif (isset($input['default_value']))
  2665. $fieldValue = $input['default_value'];
  2666. }
  2667. $this->fields_value[$input['name']][$language['id_lang']] = $fieldValue;
  2668. }
  2669. else
  2670. {
  2671. $fieldValue = $this->getFieldValue($obj, $input['name']);
  2672. if ($fieldValue === false && isset($input['default_value']))
  2673. $fieldValue = $input['default_value'];
  2674. $this->fields_value[$input['name']] = $fieldValue;
  2675. }
  2676. return $this->fields_value;
  2677. }
  2678. /**
  2679. * Return field value if possible (both classical and multilingual fields)
  2680. *
  2681. * Case 1 : Return value if present in $_POST / $_GET
  2682. * Case 2 : Return object value
  2683. *
  2684. * @param object $obj Object
  2685. * @param string $key Field name
  2686. * @param integer $id_lang Language id (optional)
  2687. * @return string
  2688. */
  2689. public function getFieldValue($obj, $key, $id_lang = null)
  2690. {
  2691. if ($id_lang)
  2692. $default_value = (isset($obj->id) && $obj->id && isset($obj->{$key}[$id_lang])) ? $obj->{$key}[$id_lang] : false;
  2693. else
  2694. $default_value = isset($obj->{$key}) ? $obj->{$key} : false;
  2695. return Tools::getValue($key.($id_lang ? '_'.$id_lang : ''), $default_value);
  2696. }
  2697. /**
  2698. * Manage page display (form, list...)
  2699. *
  2700. * @param string $className Allow to validate a different class than the current one
  2701. */
  2702. public function validateRules($class_name = false)
  2703. {
  2704. if (!$class_name)
  2705. $class_name = $this->className;
  2706. $object = new $class_name();
  2707. if (method_exists($this, 'getValidationRules'))
  2708. $definition = $this->getValidationRules();
  2709. else
  2710. $definition = ObjectModel::getDefinition($class_name);
  2711. $default_language = new Language((int)Configuration::get('PS_LANG_DEFAULT'));
  2712. foreach ($definition['fields'] as $field => $def)
  2713. {
  2714. $skip = array();
  2715. if (in_array($field, array('passwd', 'no-picture')))
  2716. $skip = array('required');
  2717. if (isset($def['lang']) && $def['lang'])
  2718. {
  2719. if (isset($def['required']) && $def['required'])
  2720. {
  2721. $value = Tools::getValue($field.'_'.$default_language->id);
  2722. if (empty($value))
  2723. $this->errors[$field.'_'.$default_language->id] = sprintf(
  2724. Tools::displayError('The field %1$s is required at least in %2$s.'),
  2725. $object->displayFieldName($field, $class_name),
  2726. $default_language->name
  2727. );
  2728. }
  2729. foreach (Language::getLanguages(false) as $language)
  2730. {
  2731. $value = Tools::getValue($field.'_'.$language['id_lang']);
  2732. if (!empty($value))
  2733. if (($error = $object->validateField($field, $value, $language['id_lang'], $skip, true)) !== true)
  2734. $this->errors[$field.'_'.$language['id_lang']] = $error;
  2735. }
  2736. }
  2737. else
  2738. if (($error = $object->validateField($field, Tools::getValue($field), null, $skip, true)) !== true)
  2739. $this->errors[$field] = $error;
  2740. }
  2741. /* Overload this method for custom checking */
  2742. $this->_childValidation();
  2743. /* Checking for multilingual fields validity */
  2744. if (isset($rules['validateLang']) && is_array($rules['validateLang']))
  2745. foreach ($rules['validateLang'] as $field_lang => $function)
  2746. foreach ($languages as $language)
  2747. if (($value = Tools::getValue($field_lang.'_'.$language['id_lang'])) !== false && !empty($value))
  2748. {
  2749. if (Tools::strtolower($function) == 'iscleanhtml' && Configuration::get('PS_ALLOW_HTML_IFRAME'))
  2750. $res = Validate::$function($value, true);
  2751. else
  2752. $res = Validate::$function($value);
  2753. if (!$res)
  2754. $this->errors[$field_lang.'_'.$language['id_lang']] = sprintf(
  2755. Tools::displayError('The %1$s field (%2$s) is invalid.'),
  2756. call_user_func(array($class_name, 'displayFieldName'), $field_lang, $class_name),
  2757. $language['name']
  2758. );
  2759. }
  2760. }
  2761. /**
  2762. * Overload this method for custom checking
  2763. */
  2764. protected function _childValidation()
  2765. {
  2766. }
  2767. /**
  2768. * Display object details
  2769. */
  2770. public function viewDetails()
  2771. {
  2772. }
  2773. /**
  2774. * Called before deletion
  2775. *
  2776. * @param object $object Object
  2777. * @return boolean
  2778. */
  2779. protected function beforeDelete($object)
  2780. {
  2781. return false;
  2782. }
  2783. /**
  2784. * Called before deletion
  2785. *
  2786. * @param object $object Object
  2787. * @return boolean
  2788. */
  2789. protected function afterDelete($object, $oldId)
  2790. {
  2791. return true;
  2792. }
  2793. protected function afterAdd($object)
  2794. {
  2795. return true;
  2796. }
  2797. protected function afterUpdate($object)
  2798. {
  2799. return true;
  2800. }
  2801. /**
  2802. * Check rights to view the current tab
  2803. *
  2804. * @return boolean
  2805. */
  2806. protected function afterImageUpload()
  2807. {
  2808. return true;
  2809. }
  2810. /**
  2811. * Copy datas from $_POST to object
  2812. *
  2813. * @param object &$object Object
  2814. * @param string $table Object table
  2815. */
  2816. protected function copyFromPost(&$object, $table)
  2817. {
  2818. /* Classical fields */
  2819. foreach ($_POST as $key => $value)
  2820. if (array_key_exists($key, $object) && $key != 'id_'.$table)
  2821. {
  2822. /* Do not take care of password field if empty */
  2823. if ($key == 'passwd' && Tools::getValue('id_'.$table) && empty($value))
  2824. continue;
  2825. /* Automatically encrypt password in MD5 */
  2826. if ($key == 'passwd' && !empty($value))
  2827. $value = Tools::encrypt($value);
  2828. $object->{$key} = $value;
  2829. }
  2830. /* Multilingual fields */
  2831. $languages = Language::getLanguages(false);
  2832. $class_vars = get_class_vars(get_class($object));
  2833. $fields = array();
  2834. if (isset($class_vars['definition']['fields']))
  2835. $fields = $class_vars['definition']['fields'];
  2836. foreach ($fields as $field => $params)
  2837. if (array_key_exists('lang', $params) && $params['lang'])
  2838. foreach ($languages as $language)
  2839. if (Tools::isSubmit($field.'_'.(int)$language['id_lang']))
  2840. $object->{$field}[(int)$language['id_lang']] = Tools::getValue($field.'_'.(int)$language['id_lang']);
  2841. }
  2842. /**
  2843. * Returns an array with selected shops and type (group or boutique shop)
  2844. *
  2845. * @param string $table
  2846. * @return array
  2847. */
  2848. protected function getSelectedAssoShop($table)
  2849. {
  2850. if (!Shop::isFeatureActive() || !Shop::isTableAssociated($table))
  2851. return array();
  2852. $shops = Shop::getShops(true, null, true);
  2853. if (count($shops) == 1 && isset($shops[0]))
  2854. return array($shops[0], 'shop');
  2855. $assos = array();
  2856. if (Tools::isSubmit('checkBoxShopAsso_'.$table))
  2857. foreach (Tools::getValue('checkBoxShopAsso_'.$table) as $id_shop => $value)
  2858. $assos[] = (int)$id_shop;
  2859. elseif (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
  2860. $assos[] = (int)Shop::getContextShopID();
  2861. return $assos;
  2862. }
  2863. /**
  2864. * Update the associations of shops
  2865. *
  2866. * @param int $id_object
  2867. */
  2868. protected function updateAssoShop($id_object)
  2869. {
  2870. if (!Shop::isFeatureActive())
  2871. return;
  2872. if (!Shop::isTableAssociated($this->table))
  2873. return;
  2874. $assos_data = $this->getSelectedAssoShop($this->table);
  2875. // Get list of shop id we want to exclude from asso deletion
  2876. $exclude_ids = $assos_data;
  2877. foreach (Db::getInstance()->executeS('SELECT id_shop FROM '._DB_PREFIX_.'shop') as $row)
  2878. if (!$this->context->employee->hasAuthOnShop($row['id_shop']))
  2879. $exclude_ids[] = $row['id_shop'];
  2880. Db::getInstance()->delete($this->table.'_shop', '`'.bqSQL($this->identifier).'` = '.(int)$id_object.($exclude_ids ? ' AND id_shop NOT IN ('.implode(', ', array_map('intval', $exclude_ids)).')' : ''));
  2881. $insert = array();
  2882. foreach ($assos_data as $id_shop)
  2883. $insert[] = array(
  2884. $this->identifier => (int)$id_object,
  2885. 'id_shop' => (int)$id_shop,
  2886. );
  2887. return Db::getInstance()->insert($this->table.'_shop', $insert, false, true, Db::INSERT_IGNORE);
  2888. }
  2889. protected function validateField($value, $field)
  2890. {
  2891. if (isset($field['validation']))
  2892. {
  2893. $valid_method_exists = method_exists('Validate', $field['validation']);
  2894. if ((!isset($field['empty']) || !$field['empty'] || (isset($field['empty']) && $field['empty'] && $value)) && $valid_method_exists)
  2895. {
  2896. if (!Validate::$field['validation']($value))
  2897. {
  2898. $this->errors[] = Tools::displayError($field['title'].' : Incorrect value');
  2899. return false;
  2900. }
  2901. }
  2902. }
  2903. return true;
  2904. }
  2905. /**
  2906. * Can be overriden
  2907. */
  2908. public function beforeUpdateOptions()
  2909. {
  2910. }
  2911. /**
  2912. * Overload this method for custom checking
  2913. *
  2914. * @param integer $id Object id used for deleting images
  2915. * @return boolean
  2916. */
  2917. protected function postImage($id)
  2918. {
  2919. if (isset($this->fieldImageSettings['name']) && isset($this->fieldImageSettings['dir']))
  2920. return $this->uploadImage($id, $this->fieldImageSettings['name'], $this->fieldImageSettings['dir'].'/');
  2921. elseif (!empty($this->fieldImageSettings))
  2922. foreach ($this->fieldImageSettings as $image)
  2923. if (isset($image['name']) && isset($image['dir']))
  2924. $this->uploadImage($id, $image['name'], $image['dir'].'/');
  2925. return !count($this->errors) ? true : false;
  2926. }
  2927. protected function uploadImage($id, $name, $dir, $ext = false, $width = null, $height = null)
  2928. {
  2929. if (isset($_FILES[$name]['tmp_name']) && !empty($_FILES[$name]['tmp_name']))
  2930. {
  2931. // Delete old image
  2932. if (Validate::isLoadedObject($object = $this->loadObject()))
  2933. $object->deleteImage();
  2934. else
  2935. return false;
  2936. // Check image validity
  2937. $max_size = isset($this->max_image_size) ? $this->max_image_size : 0;
  2938. if ($error = ImageManager::validateUpload($_FILES[$name], Tools::getMaxUploadSize($max_size)))
  2939. $this->errors[] = $error;
  2940. $tmp_name = tempnam(_PS_TMP_IMG_DIR_, 'PS');
  2941. if (!$tmp_name)
  2942. return false;
  2943. if (!move_uploaded_file($_FILES[$name]['tmp_name'], $tmp_name))
  2944. return false;
  2945. // Evaluate the memory required to resize the image: if it's too much, you can't resize it.
  2946. if (!ImageManager::checkImageMemoryLimit($tmp_name))
  2947. $this->errors[] = Tools::displayError('Due to memory limit restrictions, this image cannot be loaded. Please increase your memory_limit value via your server\'s configuration settings. ');
  2948. // Copy new image
  2949. if (empty($this->errors) && !ImageManager::resize($tmp_name, _PS_IMG_DIR_.$dir.$id.'.'.$this->imageType, (int)$width, (int)$height, ($ext ? $ext : $this->imageType)))
  2950. $this->errors[] = Tools::displayError('An error occurred while uploading the image.');
  2951. if (count($this->errors))
  2952. return false;
  2953. if ($this->afterImageUpload())
  2954. {
  2955. unlink($tmp_name);
  2956. return true;
  2957. }
  2958. return false;
  2959. }
  2960. return true;
  2961. }
  2962. /**
  2963. * Delete multiple items
  2964. *
  2965. * @return boolean true if succcess
  2966. */
  2967. protected function processBulkDelete()
  2968. {
  2969. if (is_array($this->boxes) && !empty($this->boxes))
  2970. {
  2971. $object = new $this->className();
  2972. if (isset($object->noZeroObject))
  2973. {
  2974. $objects_count = count(call_user_func(array($this->className, $object->noZeroObject)));
  2975. // Check if all object will be deleted
  2976. if ($objects_count <= 1 || count($this->boxes) == $objects_count)
  2977. $this->errors[] = Tools::displayError('You need at least one object.').
  2978. ' <b>'.$this->table.'</b><br />'.
  2979. Tools::displayError('You cannot delete all of the items.');
  2980. }
  2981. else
  2982. {
  2983. $result = true;
  2984. foreach ($this->boxes as $id)
  2985. {
  2986. $to_delete = new $this->className($id);
  2987. $delete_ok = true;
  2988. if ($this->deleted)
  2989. {
  2990. $to_delete->deleted = 1;
  2991. if (!$to_delete->update())
  2992. {
  2993. $result = false;
  2994. $delete_ok = false;
  2995. }
  2996. }
  2997. else
  2998. if (!$to_delete->delete())
  2999. {
  3000. $result = false;
  3001. $delete_ok = false;
  3002. }
  3003. if ($delete_ok)
  3004. PrestaShopLogger::addLog(sprintf($this->l('%s deletion', 'AdminTab', false, false), $this->className), 1, null, $this->className, (int)$to_delete->id, true, (int)$this->context->employee->id);
  3005. else
  3006. $this->errors[] = sprintf(Tools::displayError('Can\'t delete #%d'), $id);
  3007. }
  3008. if ($result)
  3009. $this->redirect_after = self::$currentIndex.'&conf=2&token='.$this->token;
  3010. $this->errors[] = Tools::displayError('An error occurred while deleting this selection.');
  3011. }
  3012. }
  3013. else
  3014. $this->errors[] = Tools::displayError('You must select at least one element to delete.');
  3015. if (isset($result))
  3016. return $result;
  3017. else
  3018. return false;
  3019. }
  3020. protected function ajaxProcessOpenHelp()
  3021. {
  3022. $help_class_name = $_GET['controller'];
  3023. $popup_content = "<!doctype html>
  3024. <html>
  3025. <head>
  3026. <meta charset='UTF-8'>
  3027. <title>PrestaShop Help</title>
  3028. <link href='//help.prestashop.com/css/help.css' rel='stylesheet'>
  3029. <link href='//fonts.googleapis.com/css?family=Open+Sans:400,700' rel='stylesheet'>
  3030. <script src='"._PS_JS_DIR_."jquery/jquery-1.11.0.min.js'></script>
  3031. <script src='"._PS_JS_DIR_."admin.js'></script>
  3032. <script src='"._PS_JS_DIR_."tools.js'></script>
  3033. <script>
  3034. help_class_name='".addslashes($help_class_name)."';
  3035. iso_user = '".addslashes($this->context->language->iso_code)."'
  3036. </script>
  3037. <script src='themes/default/js/help.js'></script>
  3038. <script>
  3039. $(function(){
  3040. initHelp();
  3041. });
  3042. </script>
  3043. </head>
  3044. <body><div id='help-container' class='help-popup'></div></body>
  3045. </html>";
  3046. die($popup_content);
  3047. }
  3048. /**
  3049. * Enable multiple items
  3050. *
  3051. * @return boolean true if succcess
  3052. */
  3053. protected function processBulkEnableSelection()
  3054. {
  3055. return $this->processBulkStatusSelection(1);
  3056. }
  3057. /**
  3058. * Disable multiple items
  3059. *
  3060. * @return boolean true if succcess
  3061. */
  3062. protected function processBulkDisableSelection()
  3063. {
  3064. return $this->processBulkStatusSelection(0);
  3065. }
  3066. /**
  3067. * Toggle status of multiple items
  3068. *
  3069. * @return boolean true if succcess
  3070. */
  3071. protected function processBulkStatusSelection($status)
  3072. {
  3073. $result = true;
  3074. if (is_array($this->boxes) && !empty($this->boxes))
  3075. {
  3076. foreach ($this->boxes as $id)
  3077. {
  3078. $object = new $this->className((int)$id);
  3079. $object->active = (int)$status;
  3080. $result &= $object->update();
  3081. }
  3082. }
  3083. return $result;
  3084. }
  3085. protected function processBulkAffectZone()
  3086. {
  3087. $result = false;
  3088. if (is_array($this->boxes) && !empty($this->boxes))
  3089. {
  3090. $object = new $this->className();
  3091. $result = $object->affectZoneToSelection(Tools::getValue($this->table.'Box'), Tools::getValue('zone_to_affect'));
  3092. if ($result)
  3093. $this->redirect_after = self::$currentIndex.'&conf=28&token='.$this->token;
  3094. $this->errors[] = Tools::displayError('An error occurred while affecting a zone to the selection.');
  3095. }
  3096. else
  3097. $this->errors[] = Tools::displayError('You must select at least one element to affect a new zone.');
  3098. return $result;
  3099. }
  3100. /**
  3101. * Called before Add
  3102. *
  3103. * @param object $object Object
  3104. * @return boolean
  3105. */
  3106. protected function beforeAdd($object)
  3107. {
  3108. return true;
  3109. }
  3110. /**
  3111. * prepare the view to display the required fields form
  3112. */
  3113. public function displayRequiredFields()
  3114. {
  3115. if (!$this->tabAccess['add'] || !$this->tabAccess['delete'] === '1' || !$this->required_database)
  3116. return;
  3117. $helper = new Helper();
  3118. $helper->currentIndex = self::$currentIndex;
  3119. $helper->token = $this->token;
  3120. $helper->override_folder = $this->override_folder;
  3121. return $helper->renderRequiredFields($this->className, $this->identifier, $this->required_fields);
  3122. }
  3123. /**
  3124. * Create a template from the override file, else from the base file.
  3125. *
  3126. * @param string $tpl_name filename
  3127. * @return Template
  3128. */
  3129. public function createTemplate($tpl_name)
  3130. {
  3131. // Use override tpl if it exists
  3132. // If view access is denied, we want to use the default template that will be used to display an error
  3133. if ($this->viewAccess() && $this->override_folder)
  3134. {
  3135. if (file_exists($this->context->smarty->getTemplateDir(1).DIRECTORY_SEPARATOR.$this->override_folder.$tpl_name))
  3136. return $this->context->smarty->createTemplate($this->override_folder.$tpl_name, $this->context->smarty);
  3137. elseif (file_exists($this->context->smarty->getTemplateDir(0).'controllers'.DIRECTORY_SEPARATOR.$this->override_folder.$tpl_name))
  3138. return $this->context->smarty->createTemplate('controllers'.DIRECTORY_SEPARATOR.$this->override_folder.$tpl_name, $this->context->smarty);
  3139. }
  3140. return $this->context->smarty->createTemplate($this->context->smarty->getTemplateDir(0).$tpl_name, $this->context->smarty);
  3141. }
  3142. /**
  3143. * Shortcut to set up a json success payload
  3144. *
  3145. * @param $message success message
  3146. */
  3147. public function jsonConfirmation($message)
  3148. {
  3149. $this->json = true;
  3150. $this->confirmations[] = $message;
  3151. if ($this->status === '')
  3152. $this->status = 'ok';
  3153. }
  3154. /**
  3155. * Shortcut to set up a json error payload
  3156. *
  3157. * @param $message error message
  3158. */
  3159. public function jsonError($message)
  3160. {
  3161. $this->json = true;
  3162. $this->errors[] = $message;
  3163. if ($this->status === '')
  3164. $this->status = 'error';
  3165. }
  3166. public function isFresh($file, $timeout = 604800)
  3167. {
  3168. if (($time = @filemtime(_PS_ROOT_DIR_.$file)) && filesize(_PS_ROOT_DIR_.$file) > 0)
  3169. return ((time() - $time) < $timeout);
  3170. return false;
  3171. }
  3172. protected static $is_prestashop_up = true;
  3173. public function refresh($file_to_refresh, $external_file)
  3174. {
  3175. if (self::$is_prestashop_up && $content = Tools::file_get_contents($external_file))
  3176. return (bool)file_put_contents(_PS_ROOT_DIR_.$file_to_refresh, $content);
  3177. self::$is_prestashop_up = false;
  3178. return false;
  3179. }
  3180. public function fillModuleData(&$module, $output_type = 'link', $back = null)
  3181. {
  3182. $obj = null;
  3183. if ($module->onclick_option)
  3184. $obj = new $module->name();
  3185. // Fill module data
  3186. $module->logo = '../../img/questionmark.png';
  3187. if (@filemtime(_PS_ROOT_DIR_.DIRECTORY_SEPARATOR.basename(_PS_MODULE_DIR_).DIRECTORY_SEPARATOR.$module->name
  3188. .DIRECTORY_SEPARATOR.'logo.gif'))
  3189. $module->logo = 'logo.gif';
  3190. if (@filemtime(_PS_ROOT_DIR_.DIRECTORY_SEPARATOR.basename(_PS_MODULE_DIR_).DIRECTORY_SEPARATOR.$module->name
  3191. .DIRECTORY_SEPARATOR.'logo.png'))
  3192. $module->logo = 'logo.png';
  3193. $link_admin_modules = $this->context->link->getAdminLink('AdminModules', true);
  3194. $module->options['install_url'] = $link_admin_modules.'&install='.urlencode($module->name).'&tab_module='.$module->tab.'&module_name='.$module->name.'&anchor='.ucfirst($module->name);
  3195. $module->options['update_url'] = $link_admin_modules.'&update='.urlencode($module->name).'&tab_module='.$module->tab.'&module_name='.$module->name.'&anchor='.ucfirst($module->name);
  3196. $module->options['uninstall_url'] = $link_admin_modules.'&uninstall='.urlencode($module->name).'&tab_module='.$module->tab.'&module_name='.$module->name.'&anchor='.ucfirst($module->name);
  3197. $module->optionsHtml = $this->displayModuleOptions($module, $output_type, $back);
  3198. $module->options['uninstall_onclick'] = ((!$module->onclick_option) ?
  3199. ((empty($module->confirmUninstall)) ? 'return confirm(\''.$this->l('Do you really want to uninstall this module?').'\');' : 'return confirm(\''.addslashes($module->confirmUninstall).'\');') :
  3200. $obj->onclickOption('uninstall', $module->options['uninstall_url']));
  3201. if ((Tools::getValue('module_name') == $module->name || in_array($module->name, explode('|', Tools::getValue('modules_list')))) && (int)Tools::getValue('conf') > 0)
  3202. $module->message = $this->_conf[(int)Tools::getValue('conf')];
  3203. if ((Tools::getValue('module_name') == $module->name || in_array($module->name, explode('|', Tools::getValue('modules_list')))) && (int)Tools::getValue('conf') > 0)
  3204. unset($obj);
  3205. }
  3206. /**
  3207. * Display modules list
  3208. *
  3209. * @param $module
  3210. * @param $output_type (link or select)
  3211. * @param $back
  3212. *
  3213. * @return string
  3214. */
  3215. protected $translationsTab = array();
  3216. public function displayModuleOptions($module, $output_type = 'link', $back = null)
  3217. {
  3218. if (!isset($module->enable_device))
  3219. $module->enable_device = Context::DEVICE_COMPUTER | Context::DEVICE_TABLET | Context::DEVICE_MOBILE;
  3220. $this->translationsTab['confirm_uninstall_popup'] = (isset($module->confirmUninstall) ? $module->confirmUninstall : $this->l('Do you really want to uninstall this module?'));
  3221. if (!isset($this->translationsTab['Disable this module']))
  3222. {
  3223. $this->translationsTab['Disable this module'] = $this->l('Disable this module');
  3224. $this->translationsTab['Enable this module for all shops'] = $this->l('Enable this module for all shops');
  3225. $this->translationsTab['Disable'] = $this->l('Disable');
  3226. $this->translationsTab['Enable'] = $this->l('Enable');
  3227. $this->translationsTab['Disable on mobiles'] = $this->l('Disable on mobiles');
  3228. $this->translationsTab['Disable on tablets'] = $this->l('Disable on tablets');
  3229. $this->translationsTab['Disable on computers'] = $this->l('Disable on computers');
  3230. $this->translationsTab['Display on mobiles'] = $this->l('Display on mobiles');
  3231. $this->translationsTab['Display on tablets'] = $this->l('Display on tablets');
  3232. $this->translationsTab['Display on computers'] = $this->l('Display on computers');
  3233. $this->translationsTab['Reset'] = $this->l('Reset');
  3234. $this->translationsTab['Configure'] = $this->l('Configure');
  3235. $this->translationsTab['Delete'] = $this->l('Delete');
  3236. $this->translationsTab['Install'] = $this->l('Install');
  3237. $this->translationsTab['Uninstall'] = $this->l('Uninstall');
  3238. $this->translationsTab['Would you like to delete the content related to this module ?'] = $this->l('Would you like to delete the content related to this module ?');
  3239. $this->translationsTab['This action will permanently remove the module from the server. Are you sure you want to do this?'] = $this->l('This action will permanently remove the module from the server. Are you sure you want to do this?');
  3240. $this->translationsTab['Remove from Favorites'] = $this->l('Remove from Favorites');
  3241. $this->translationsTab['Mark as Favorite'] = $this->l('Mark as Favorite');
  3242. }
  3243. $link_admin_modules = $this->context->link->getAdminLink('AdminModules', true);
  3244. $modules_options = array();
  3245. $configure_module = array(
  3246. 'href' => $link_admin_modules.'&configure='.urlencode($module->name).'&tab_module='.$module->tab.'&module_name='.urlencode($module->name),
  3247. 'onclick' => $module->onclick_option && isset($module->onclick_option_content['configure']) ? $module->onclick_option_content['configure'] : '',
  3248. 'title' => '',
  3249. 'text' => $this->translationsTab['Configure'],
  3250. 'cond' => $module->id && isset($module->is_configurable) && $module->is_configurable,
  3251. 'icon' => 'wrench',
  3252. );
  3253. $desactive_module = array(
  3254. 'href' => $link_admin_modules.'&module_name='.urlencode($module->name).'&'.($module->active ? 'enable=0' : 'enable=1').'&tab_module='.$module->tab,
  3255. 'onclick' => $module->active && $module->onclick_option && isset($module->onclick_option_content['desactive']) ? $module->onclick_option_content['desactive'] : '' ,
  3256. 'title' => Shop::isFeatureActive() ? htmlspecialchars($module->active ? $this->translationsTab['Disable this module'] : $this->translationsTab['Enable this module for all shops']) : '',
  3257. 'text' => $module->active ? $this->translationsTab['Disable'] : $this->translationsTab['Enable'],
  3258. 'cond' => $module->id,
  3259. 'icon' => 'off',
  3260. );
  3261. $link_reset_module = $link_admin_modules.'&module_name='.urlencode($module->name).'&reset&tab_module='.$module->tab;
  3262. $is_reset_ready = false;
  3263. if (Validate::isModuleName($module->name))
  3264. if (method_exists(Module::getInstanceByName($module->name), 'reset'))
  3265. $is_reset_ready = true;
  3266. $reset_module = array(
  3267. 'href' => $link_reset_module,
  3268. 'onclick' => $module->onclick_option && isset($module->onclick_option_content['reset']) ? $module->onclick_option_content['reset'] : '',
  3269. 'title' => '',
  3270. 'text' => $this->translationsTab['Reset'],
  3271. 'cond' => $module->id && $module->active,
  3272. 'icon' => 'undo',
  3273. 'class' => ($is_reset_ready ? 'reset_ready' : '')
  3274. );
  3275. $delete_module = array(
  3276. 'href' => $link_admin_modules.'&delete='.urlencode($module->name).'&tab_module='.$module->tab.'&module_name='.urlencode($module->name),
  3277. 'onclick' => $module->onclick_option && isset($module->onclick_option_content['delete']) ? $module->onclick_option_content['delete'] : 'return confirm(\''.$this->translationsTab['This action will permanently remove the module from the server. Are you sure you want to do this?'].'\');',
  3278. 'title' => '',
  3279. 'text' => $this->translationsTab['Delete'],
  3280. 'cond' => true,
  3281. 'icon' => 'trash',
  3282. 'class' => 'text-danger'
  3283. );
  3284. $display_mobile = array(
  3285. 'href' => $link_admin_modules.'&module_name='.urlencode($module->name).'&'.($module->enable_device & Context::DEVICE_MOBILE ? 'disable_device' : 'enable_device').'='.Context::DEVICE_MOBILE.'&tab_module='.$module->tab,
  3286. 'onclick' => '',
  3287. 'title' => htmlspecialchars($module->enable_device & Context::DEVICE_MOBILE ? $this->translationsTab['Disable on mobiles'] : $this->translationsTab['Display on mobiles']),
  3288. 'text' => $module->enable_device & Context::DEVICE_MOBILE ? $this->translationsTab['Disable on mobiles'] : $this->translationsTab['Display on mobiles'],
  3289. 'cond' => $module->id,
  3290. 'icon' => 'mobile'
  3291. );
  3292. $display_tablet = array(
  3293. 'href' => $link_admin_modules.'&module_name='.urlencode($module->name).'&'.($module->enable_device & Context::DEVICE_TABLET ? 'disable_device' : 'enable_device').'='.Context::DEVICE_TABLET.'&tab_module='.$module->tab,
  3294. 'onclick' => '',
  3295. 'title' => htmlspecialchars($module->enable_device & Context::DEVICE_TABLET ? $this->translationsTab['Disable on tablets'] : $this->translationsTab['Display on tablets']),
  3296. 'text' => $module->enable_device & Context::DEVICE_TABLET ? $this->translationsTab['Disable on tablets'] : $this->translationsTab['Display on tablets'],
  3297. 'cond' => $module->id,
  3298. 'icon' => 'tablet'
  3299. );
  3300. $display_computer = array(
  3301. 'href' => $link_admin_modules.'&module_name='.urlencode($module->name).'&'.($module->enable_device & Context::DEVICE_COMPUTER ? 'disable_device' : 'enable_device').'='.Context::DEVICE_COMPUTER.'&tab_module='.$module->tab,
  3302. 'onclick' => '',
  3303. 'title' => htmlspecialchars($module->enable_device & Context::DEVICE_COMPUTER ? $this->translationsTab['Disable on computers'] : $this->translationsTab['Display on computers']),
  3304. 'text' => $module->enable_device & Context::DEVICE_COMPUTER ? $this->translationsTab['Disable on computers'] : $this->translationsTab['Display on computers'],
  3305. 'cond' => $module->id,
  3306. 'icon' => 'desktop'
  3307. );
  3308. $install = array(
  3309. 'href' => $link_admin_modules.'&install='.urlencode($module->name).'&tab_module='.$module->tab.'&module_name='.$module->name.'&anchor='.ucfirst($module->name).(!is_null($back) ? '&back='.urlencode($back) : ''),
  3310. 'onclick' => '',
  3311. 'title' => $this->translationsTab['Install'],
  3312. 'text' => $this->translationsTab['Install'],
  3313. 'cond' => $module->id,
  3314. 'icon' => 'plus-sign-alt'
  3315. );
  3316. $uninstall = array(
  3317. 'href' => $link_admin_modules.'&uninstall='.urlencode($module->name).'&tab_module='.$module->tab.'&module_name='.$module->name.'&anchor='.ucfirst($module->name).(!is_null($back) ? '&back='.urlencode($back) : ''),
  3318. 'onclick' => (isset($module->onclick_option_content['uninstall']) ? $module->onclick_option_content['uninstall'] : 'return confirm(\''.$this->translationsTab['confirm_uninstall_popup'].'\');'),
  3319. 'title' => $this->translationsTab['Uninstall'],
  3320. 'text' => $this->translationsTab['Uninstall'],
  3321. 'cond' => $module->id,
  3322. 'icon' => 'minus-sign-alt'
  3323. );
  3324. $remove_from_favorite = array(
  3325. 'href' => '#',
  3326. 'class' => 'action_unfavorite toggle_favorite',
  3327. 'onclick' =>'',
  3328. 'title' => $this->translationsTab['Remove from Favorites'],
  3329. 'text' => $this->translationsTab['Remove from Favorites'],
  3330. 'cond' => $module->id,
  3331. 'icon' => 'star',
  3332. 'data-value' => '0',
  3333. 'data-module' => $module->name
  3334. );
  3335. $mark_as_favorite = array(
  3336. 'href' => '#',
  3337. 'class' => 'action_favorite toggle_favorite',
  3338. 'onclick' => '',
  3339. 'title' => $this->translationsTab['Mark as Favorite'],
  3340. 'text' => $this->translationsTab['Mark as Favorite'],
  3341. 'cond' => $module->id,
  3342. 'icon' => 'star',
  3343. 'data-value' => '1',
  3344. 'data-module' => $module->name
  3345. );
  3346. $update = array(
  3347. 'href' => $module->options['update_url'],
  3348. 'onclick' => '',
  3349. 'title' => 'Update it!',
  3350. 'text' => 'Update it!',
  3351. 'icon' => 'refresh',
  3352. 'cond' => $module->id,
  3353. );
  3354. $divider = array(
  3355. 'href' => '#',
  3356. 'onclick' => '',
  3357. 'title' => 'divider',
  3358. 'text' => 'divider',
  3359. 'cond' => $module->id,
  3360. );
  3361. if (isset($module->version_addons) && $module->version_addons)
  3362. $modules_options[] = $update;
  3363. if ($module->active)
  3364. {
  3365. $modules_options[] = $configure_module;
  3366. $modules_options[] = $desactive_module;
  3367. $modules_options[] = $display_mobile;
  3368. $modules_options[] = $display_tablet;
  3369. $modules_options[] = $display_computer;
  3370. }
  3371. else
  3372. {
  3373. $modules_options[] = $desactive_module;
  3374. $modules_options[] = $configure_module;
  3375. }
  3376. $modules_options[] = $reset_module;
  3377. if ($output_type == 'select')
  3378. {
  3379. if (!$module->id)
  3380. $modules_options[] = $install;
  3381. else
  3382. $modules_options[] = $uninstall;
  3383. }
  3384. elseif ($output_type == 'array')
  3385. if ($module->id)
  3386. $modules_options[] = $uninstall;
  3387. if (isset($module->preferences) && isset($module->preferences['favorite']) && $module->preferences['favorite'] == 1)
  3388. {
  3389. $remove_from_favorite['style'] = '';
  3390. $mark_as_favorite['style'] = 'display:none;';
  3391. $modules_options[] = $remove_from_favorite;
  3392. $modules_options[] = $mark_as_favorite;
  3393. }
  3394. else
  3395. {
  3396. $mark_as_favorite['style'] = '';
  3397. $remove_from_favorite['style'] = 'display:none;';
  3398. $modules_options[] = $remove_from_favorite;
  3399. $modules_options[] = $mark_as_favorite;
  3400. }
  3401. if ($module->id == 0)
  3402. {
  3403. $install['cond'] = 1;
  3404. $install['flag_install'] = 1;
  3405. $modules_options[] = $install;
  3406. }
  3407. $modules_options[] = $divider;
  3408. $modules_options[] = $delete_module;
  3409. $return = '';
  3410. foreach ($modules_options as $option_name => $option)
  3411. {
  3412. if ($option['cond'])
  3413. {
  3414. if ($output_type == 'link')
  3415. {
  3416. $return .= '<li><a class="'.$option_name.' action_module';
  3417. $return .= '" href="'.$option['href'].(!is_null($back) ? '&back='.urlencode($back) : '').'"';
  3418. $return .= ' onclick="'.$option['onclick'].'" title="'.$option['title'].'"><i class="icon-'.(isset($option['icon']) && $option['icon'] ? $option['icon']:'cog' ).'"></i>&nbsp;'.$option['text'].'</a></li>';
  3419. }
  3420. elseif ($output_type == 'array')
  3421. {
  3422. if (!is_array($return))
  3423. $return = array();
  3424. $html = '<a class="';
  3425. $is_install = isset($option['flag_install']) ? true : false;
  3426. if (isset($option['class']))
  3427. $html .= $option['class'];
  3428. if ($is_install)
  3429. $html .= ' btn btn-success';
  3430. if (!$is_install && count($return) == 0)
  3431. $html .= ' btn btn-default';
  3432. $html .= '"';
  3433. if (isset($option['data-value']))
  3434. $html .= ' data-value="'.$option['data-value'].'"';
  3435. if (isset($option['data-module']))
  3436. $html .= ' data-module="'.$option['data-module'].'"';
  3437. if (isset($option['style']))
  3438. $html .= ' style="'.$option['style'].'"';
  3439. $html .= ' href="'.htmlentities($option['href']).(!is_null($back) ? '&back='.urlencode($back) : '').'" onclick="'.$option['onclick'].'" title="'.$option['title'].'"><i class="icon-'.(isset($option['icon']) && $option['icon'] ? $option['icon']:'cog' ).'"></i> '.$option['text'].'</a>';
  3440. $return[] = $html;
  3441. }
  3442. elseif ($output_type == 'select')
  3443. $return .= '<option id="'.$option_name.'" data-href="'.htmlentities($option['href']).(!is_null($back) ? '&back='.urlencode($back) : '').'" data-onclick="'.$option['onclick'].'">'.$option['text'].'</option>';
  3444. }
  3445. }
  3446. if ($output_type == 'select')
  3447. $return = '<select id="select_'.$module->name.'">'.$return.'</select>';
  3448. return $return;
  3449. }
  3450. public function ajaxProcessGetModuleQuickView()
  3451. {
  3452. $modules = Module::getModulesOnDisk();
  3453. foreach ($modules as $module)
  3454. if ($module->name == Tools::getValue('module'))
  3455. break;
  3456. $url = $module->url;
  3457. if (isset($module->type) && ($module->type == 'addonsPartner' || $module->type == 'addonsNative'))
  3458. $url = $this->context->link->getAdminLink('AdminModules').'&install='.urlencode($module->name).'&tab_module='.$module->tab.'&module_name='.$module->name.'&anchor='.ucfirst($module->name);
  3459. $this->context->smarty->assign(array(
  3460. 'displayName' => $module->displayName,
  3461. 'image' => $module->image,
  3462. 'nb_rates' => (int)$module->nb_rates[0],
  3463. 'avg_rate' => (int)$module->avg_rate[0],
  3464. 'badges' => $module->badges,
  3465. 'compatibility' => $module->compatibility,
  3466. 'description_full' => $module->description_full,
  3467. 'additional_description' => $module->additional_description,
  3468. 'is_addons_partner' => (isset($module->type) && ($module->type == 'addonsPartner' || $module->type == 'addonsNative')),
  3469. 'url' => $url,
  3470. 'price' => $module->price
  3471. ));
  3472. // Fetch the translations in the right place - they are not defined by our current controller!
  3473. Context::getContext()->override_controller_name_for_translations = 'AdminModules';
  3474. $this->smartyOutputContent('controllers/modules/quickview.tpl');
  3475. die(1);
  3476. }
  3477. }