1<?php
2/**
3 * Copyright (C) 2017-2019 thirty bees
4 * Copyright (C) 2007-2016 PrestaShop
5 *
6 * thirty bees is an extension to the PrestaShop software by PrestaShop SA.
7 *
8 * NOTICE OF LICENSE
9 *
10 * This source file is subject to the Open Software License (OSL 3.0)
11 * that is bundled with this package in the file LICENSE.md.
12 * It is also available through the world-wide-web at this URL:
13 * https://opensource.org/licenses/osl-3.0.php
14 * If you did not receive a copy of the license and are unable to
15 * obtain it through the world-wide-web, please send an email
16 * to license@thirtybees.com so we can send you a copy immediately.
17 *
18 * @author    thirty bees <contact@thirtybees.com>
19 * @author    PrestaShop SA <contact@prestashop.com>
20 * @copyright 2017-2019 thirty bees
21 * @copyright 2007-2016 PrestaShop SA
22 * @license   https://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
23 * PrestaShop is an internationally registered trademark of PrestaShop SA.
24 */
25
26namespace TbUpdaterModule;
27
28/**
29 * Class HookCore
30 *
31 * @since 1.0.0
32 */
33class Hook extends ObjectModel
34{
35    // @codingStandardsIgnoreStart
36    /**
37     * @var array List of executed hooks on this page
38     */
39    public static $executed_hooks = [];
40    public static $native_module;
41    /**
42     * @deprecated 1.0.0
43     */
44    protected static $_hook_modules_cache = null;
45    /**
46     * @deprecated 1.0.0
47     */
48    protected static $_hook_modules_cache_exec = null;
49    /**
50     * @var string Hook name identifier
51     */
52    public $name;
53    /**
54     * @var string Hook title (displayed in BO)
55     */
56    public $title;
57    /**
58     * @var string Hook description
59     */
60    public $description;
61    /**
62     * @var bool
63     */
64    public $position = false;
65    /**
66     * @var bool Is this hook usable with live edit ?
67     */
68    public $live_edit = false;
69    // @codingStandardsIgnoreEnd
70
71    /**
72     * @see ObjectModel::$definition
73     */
74    public static $definition = [
75        'table'   => 'hook',
76        'primary' => 'id_hook',
77        'fields'  => [
78            'name'        => ['type' => self::TYPE_STRING, 'validate' => 'isHookName', 'required' => true, 'size' => 64],
79            'title'       => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName'],
80            'description' => ['type' => self::TYPE_HTML, 'validate' => 'isCleanHtml'],
81            'position'    => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
82            'live_edit'   => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
83        ],
84    ];
85
86    /**
87     * Return Hooks List
88     *
89     * @param bool $position
90     *
91     * @return array Hooks List
92     *
93     * @since   1.0.0
94     * @version 1.0.0 Initial version
95     */
96    public static function getHooks($position = false)
97    {
98        return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
99            '
100			SELECT * FROM `'._DB_PREFIX_.'hook` h
101			'.($position ? 'WHERE h.`position` = 1' : '').'
102			ORDER BY `name`'
103        );
104    }
105
106    /**
107     * Return hook ID from name
108     *
109     * @since   1.0.0
110     * @version 1.0.0 Initial version
111     */
112    public static function getNameById($hookId)
113    {
114        $cacheId = 'hook_namebyid_'.$hookId;
115        if (!Cache::isStored($cacheId)) {
116            $result = Db::getInstance()->getValue(
117                '
118							SELECT `name`
119							FROM `'._DB_PREFIX_.'hook`
120							WHERE `id_hook` = '.(int) $hookId
121            );
122            Cache::store($cacheId, $result);
123
124            return $result;
125        }
126
127        return Cache::retrieve($cacheId);
128    }
129
130    /**
131     * Return hook live edit bool from ID
132     *
133     * @since   1.0.0
134     * @version 1.0.0 Initial version
135     */
136    public static function getLiveEditById($hookId)
137    {
138        $cacheId = 'hook_live_editbyid_'.$hookId;
139        if (!Cache::isStored($cacheId)) {
140            $result = Db::getInstance()->getValue(
141                '
142							SELECT `live_edit`
143							FROM `'._DB_PREFIX_.'hook`
144							WHERE `id_hook` = '.(int) $hookId
145            );
146            Cache::store($cacheId, $result);
147
148            return $result;
149        }
150
151        return Cache::retrieve($cacheId);
152    }
153
154    /**
155     * Return Hooks List
156     *
157     * @since   1.5.0
158     *
159     * @param int $idHook
160     * @param int $idModule
161     *
162     * @return array Modules List
163     *
164     * @since   1.0.0
165     * @version 1.0.0 Initial version
166     */
167    public static function getModulesFromHook($idHook, $idModule = null)
168    {
169        $hmList = Hook::getHookModuleList();
170        $moduleList = (isset($hmList[$idHook])) ? $hmList[$idHook] : [];
171
172        if ($idModule) {
173            return (isset($moduleList[$idModule])) ? [$moduleList[$idModule]] : [];
174        }
175
176        return $moduleList;
177    }
178
179    /**
180     * Get list of all registered hooks with modules
181     *
182     * @return array
183     *
184     * @since   1.0.0
185     * @version 1.0.0 Initial version
186     */
187    public static function getHookModuleList()
188    {
189        $cacheId = 'hook_module_list';
190        if (!Cache::isStored($cacheId)) {
191            $results = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
192                '
193			SELECT h.id_hook, h.name AS h_name, title, description, h.position, live_edit, hm.position AS hm_position, m.id_module, m.name, active
194			FROM `'._DB_PREFIX_.'hook_module` hm
195			STRAIGHT_JOIN `'._DB_PREFIX_.'hook` h ON (h.id_hook = hm.id_hook AND hm.id_shop = '.(int) Context::getContext()->shop->id.')
196			STRAIGHT_JOIN `'._DB_PREFIX_.'module` AS m ON (m.id_module = hm.id_module)
197			ORDER BY hm.position'
198            );
199            $list = [];
200            foreach ($results as $result) {
201                if (!isset($list[$result['id_hook']])) {
202                    $list[$result['id_hook']] = [];
203                }
204
205                $list[$result['id_hook']][$result['id_module']] = [
206                    'id_hook'     => $result['id_hook'],
207                    'title'       => $result['title'],
208                    'description' => $result['description'],
209                    'hm.position' => $result['position'],
210                    'live_edit'   => $result['live_edit'],
211                    'm.position'  => $result['hm_position'],
212                    'id_module'   => $result['id_module'],
213                    'name'        => $result['name'],
214                    'active'      => $result['active'],
215                ];
216            }
217            Cache::store($cacheId, $list);
218
219            // @todo remove this in 1.6, we keep it in 1.5 for retrocompatibility
220            Hook::$_hook_modules_cache = $list;
221
222            return $list;
223        }
224
225        return Cache::retrieve($cacheId);
226    }
227
228    /**
229     * @deprecated 1.0.0
230     *
231     * @param $newOrderStatusId
232     * @param $idOrder
233     *
234     * @return bool|string
235     */
236    public static function updateOrderStatus($newOrderStatusId, $idOrder)
237    {
238        Tools::displayAsDeprecated();
239        $order = new Order((int) $idOrder);
240        $new_os = new OrderState((int) $newOrderStatusId, $order->id_lang);
241
242        $return = ((int) $new_os->id == Configuration::get('PS_OS_PAYMENT')) ? Hook::exec('paymentConfirm', ['id_order' => (int) ($order->id)]) : true;
243        $return = Hook::exec('updateOrderStatus', ['newOrderStatus' => $new_os, 'id_order' => (int) ($order->id)]) && $return;
244
245        return $return;
246    }
247
248    /**
249     * Execute modules for specified hook
250     *
251     * @param string $hookName        Hook Name
252     * @param array  $hookArgs        Parameters for the functions
253     * @param int    $idModule        Execute hook for this module only
254     * @param bool   $arrayReturn     If specified, module output will be set by name in an array
255     * @param bool   $checkExceptions Check permission exceptions
256     * @param bool   $usePush         Force change to be refreshed on Dashboard widgets
257     * @param int    $idShop          If specified, hook will be execute the shop with this ID
258     *
259     * @throws \Exception
260     *
261     * @return string|array modules output
262     *
263     * @since   1.0.0
264     * @version 1.0.0 Initial version
265     */
266    public static function exec(
267        $hookName,
268        $hookArgs = [],
269        $idModule = null,
270        $arrayReturn = false,
271        $checkExceptions = true,
272        $usePush = false,
273        $idShop = null
274    ) {
275        if (!Configuration::get('TB_PAGE_CACHE_ENABLED')) {
276            return static::execWithoutCache($hookName, $hookArgs, $idModule, $arrayReturn, $checkExceptions, $usePush, $idShop);
277        }
278
279        $activehooks = json_decode(Configuration::get('TB_PAGE_CACHE_HOOKS'), true);
280
281        $found = false;
282        if (is_array($activehooks)) {
283            foreach ($activehooks as $hookArr) {
284                if (is_array($hookArr) && in_array($hookName, $hookArr)) {
285                    $found = true;
286                    break;
287                }
288            }
289        }
290
291        if (!$found) {
292            return static::execWithoutCache($hookName, $hookArgs, $idModule, $arrayReturn, $checkExceptions, $usePush, $idShop);
293        }
294
295        if (!$moduleList = static::getHookModuleExecList($hookName)) {
296            return '';
297        }
298
299        if ($arrayReturn) {
300            $return = array();
301        } else {
302            $return = '';
303        }
304
305        if (!$idModule) {
306            foreach ($moduleList as $m) {
307                try {
308                    $data = static::execWithoutCache($hookName, $hookArgs, $m['id_module'], $arrayReturn, $checkExceptions, $usePush, $idShop);
309                } catch (Exception $e) {
310                    $data = sprintf(Tools::displayError('Error while displaying module "%s"'), Module::getInstanceById($m['id_module'])->displayName);
311                }
312                if (is_array($data)) {
313                    $data = array_shift($data);
314                }
315                if (is_array($data)) {
316                    $return[$m['module']] = $data;
317                } else {
318                    if (isset($activehooks[$m['id_module']]) && in_array($hookName, $activehooks[$m['id_module']])) {
319                        $dataWrapped = '<!--[hook '.$hookName.'] [id_module '.$m['id_module'].']-->'.$data.'<!--[hook '.$hookName.'] [id_module '.$m['id_module'].']-->';
320                    } else {
321                        $dataWrapped = $data;
322                    }
323
324                    if ($arrayReturn) {
325                        $return[$m['module']] = $dataWrapped;
326                    } else {
327                        $return .= $dataWrapped;
328                    }
329                }
330            }
331        } else {
332            try {
333                $return = static::execWithoutCache($hookName, $hookArgs, $idModule, $arrayReturn, $checkExceptions, $usePush, $idShop);
334            } catch (Exception $e) {
335                $return = sprintf(Tools::displayError('Error while displaying module "%s"'), Module::getInstanceById($idModule)->displayName);
336            }
337        }
338
339        return $return;
340    }
341
342    /**
343     * Execute modules for specified hook
344     *
345     * @param string $hookName        Hook Name
346     * @param array  $hookArgs        Parameters for the functions
347     * @param int    $idModule        Execute hook for this module only
348     * @param bool   $arrayReturn     If specified, module output will be set by name in an array
349     * @param bool   $checkExceptions Check permission exceptions
350     * @param bool   $usePush         Force change to be refreshed on Dashboard widgets
351     * @param int    $idShop          If specified, hook will be execute the shop with this ID
352     *
353     * @throws \Exception
354     *
355     * @return string/array modules output
356     *
357     * @since   1.0.0
358     * @version 1.0.0 Initial version
359     */
360    public static function execWithoutCache(
361        $hookName,
362        $hookArgs = [],
363        $idModule = null,
364        $arrayReturn = false,
365        $checkExceptions = true,
366        $usePush = false,
367        $idShop = null
368    ) {
369        if (defined('TB_INSTALLATION_IN_PROGRESS')) {
370            return;
371        }
372
373        static $disableNonNativeModules = null;
374        if ($disableNonNativeModules === null) {
375            $disableNonNativeModules = (bool) Configuration::get('PS_DISABLE_NON_NATIVE_MODULE');
376        }
377
378        // Check arguments validity
379        if (($idModule && !is_numeric($idModule)) || !Validate::isHookName($hookName)) {
380            throw new \Exception('Invalid id_module or hook_name');
381        }
382
383        // If no modules associated to hook_name or recompatible hook name, we stop the function
384
385        if (!$moduleList = Hook::getHookModuleExecList($hookName)) {
386            return '';
387        }
388
389        // Check if hook exists
390        if (!$idHook = Hook::getIdByName($hookName)) {
391            return false;
392        }
393
394        // Store list of executed hooks on this page
395        Hook::$executed_hooks[$idHook] = $hookName;
396
397        $liveEdit = false;
398        $context = Context::getContext();
399        if (!isset($hookArgs['cookie']) || !$hookArgs['cookie']) {
400            $hookArgs['cookie'] = $context->cookie;
401        }
402        if (!isset($hookArgs['cart']) || !$hookArgs['cart']) {
403            $hookArgs['cart'] = $context->cart;
404        }
405
406        $retroHookName = Hook::getRetroHookName($hookName);
407
408        // Look on modules list
409        $altern = 0;
410        if ($arrayReturn) {
411            $output = [];
412        } else {
413            $output = '';
414        }
415
416        if ($disableNonNativeModules && !isset(Hook::$native_module)) {
417            Hook::$native_module = Module::getNativeModuleList();
418        }
419
420        $differentShop = false;
421        if ($idShop !== null && Validate::isUnsignedId($idShop) && $idShop != $context->shop->getContextShopID()) {
422            $oldContext = $context->shop->getContext();
423            $oldShop = clone $context->shop;
424            $shop = new Shop((int) $idShop);
425            if (Validate::isLoadedObject($shop)) {
426                $context->shop = $shop;
427                $context->shop->setContext(Shop::CONTEXT_SHOP, $shop->id);
428                $differentShop = true;
429            }
430        }
431
432        foreach ($moduleList as $array) {
433            // Check errors
434            if ($idModule && $idModule != $array['id_module']) {
435                continue;
436            }
437
438            if ((bool) $disableNonNativeModules && Hook::$native_module && count(Hook::$native_module) && !in_array($array['module'], Hook::$native_module)) {
439                continue;
440            }
441
442            // Check permissions
443            if ($checkExceptions) {
444                $exceptions = Module::getExceptionsStatic($array['id_module'], $array['id_hook']);
445
446                $controller = Dispatcher::getInstance()->getController();
447                $controllerObj = Context::getContext()->controller;
448
449                //check if current controller is a module controller
450                if (isset($controllerObj->module) && Validate::isLoadedObject($controllerObj->module)) {
451                    $controller = 'module-'.$controllerObj->module->name.'-'.$controller;
452                }
453
454                if (in_array($controller, $exceptions)) {
455                    continue;
456                }
457
458                //Backward compatibility of controller names
459                $matchingName = [
460                    'authentication'     => 'auth',
461                    'productscomparison' => 'compare',
462                ];
463                if (isset($matchingName[$controller]) && in_array($matchingName[$controller], $exceptions)) {
464                    continue;
465                }
466                if (Validate::isLoadedObject($context->employee) && !Module::getPermissionStatic($array['id_module'], 'view', $context->employee)) {
467                    continue;
468                }
469            }
470
471            if (!($moduleInstance = Module::getInstanceByName($array['module']))) {
472                continue;
473            }
474
475            if ($usePush && !$moduleInstance->allow_push) {
476                continue;
477            }
478            // Check which / if method is callable
479            $hookCallable = is_callable([$moduleInstance, 'hook'.$hookName]);
480            $hookRetroCallable = is_callable([$moduleInstance, 'hook'.$retroHookName]);
481
482            if (($hookCallable || $hookRetroCallable) && Module::preCall($moduleInstance->name)) {
483                $hookArgs['altern'] = ++$altern;
484
485                if ($usePush && isset($moduleInstance->push_filename) && file_exists($moduleInstance->push_filename)) {
486                    Tools::waitUntilFileIsModified($moduleInstance->push_filename, $moduleInstance->push_time_limit);
487                }
488
489                // Call hook method
490                if ($hookCallable) {
491                    $display = Hook::coreCallHook($moduleInstance, 'hook'.$hookName, $hookArgs);
492                } elseif ($hookRetroCallable) {
493                    $display = Hook::coreCallHook($moduleInstance, 'hook'.$retroHookName, $hookArgs);
494                }
495
496                // Live edit
497                if (!$arrayReturn && $array['live_edit'] && Tools::isSubmit('live_edit') && Tools::getValue('ad')
498                    && Tools::getValue('liveToken') == Tools::getAdminToken(
499                        'AdminModulesPositions'
500                        .(int) Tab::getIdFromClassName('AdminModulesPositions').(int) Tools::getValue('id_employee')
501                    )
502                ) {
503                    $liveEdit = true;
504                    $output .= static::wrapLiveEdit($display, $moduleInstance, $array['id_hook']);
505                } elseif ($arrayReturn) {
506                    $output[$moduleInstance->name] = $display;
507                } else {
508                    $output .= $display;
509                }
510            }
511        }
512
513        if ($differentShop) {
514            $context->shop = $oldShop;
515            $context->shop->setContext($oldContext, $shop->id);
516        }
517
518        if ($arrayReturn) {
519            return $output;
520        } else {
521            return ($liveEdit ? '<script type="text/javascript">hooks_list.push(\''.$hookName.'\');</script>
522				<div id="'.$hookName.'" class="dndHook" style="min-height:50px">' : '').$output.($liveEdit ? '</div>' : '');
523        }// Return html string
524    }
525
526    /**
527     * Get list of modules we can execute per hook
528     *
529     * @param string $hookName Get list of modules for this hook if given
530     *
531     * @return array
532     *
533     * @since   1.0.0
534     * @version 1.0.0 Initial version
535     */
536    public static function getHookModuleExecList($hookName = null)
537    {
538        $context = Context::getContext();
539        $cacheId = 'hook_module_exec_list_'.(isset($context->shop->id) ? '_'.$context->shop->id : '').((isset($context->customer)) ? '_'.$context->customer->id : '');
540        if (!Cache::isStored($cacheId) || $hookName == 'displayPayment' || $hookName == 'displayPaymentEU' || $hookName == 'displayBackOfficeHeader') {
541            $frontend = true;
542            $groups = [];
543            $useGroups = Group::isFeatureActive();
544            if (isset($context->employee)) {
545                $frontend = false;
546            } else {
547                // Get groups list
548                if ($useGroups) {
549                    if (isset($context->customer) && $context->customer->isLogged()) {
550                        $groups = $context->customer->getGroups();
551                    } elseif (isset($context->customer) && $context->customer->isLogged(true)) {
552                        $groups = [(int) Configuration::get('PS_GUEST_GROUP')];
553                    } else {
554                        $groups = [(int) Configuration::get('PS_UNIDENTIFIED_GROUP')];
555                    }
556                }
557            }
558
559            // SQL Request
560            $sql = new DbQuery();
561            $sql->select('h.`name` as hook, m.`id_module`, h.`id_hook`, m.`name` as module, h.`live_edit`');
562            $sql->from('module', 'm');
563            if ($hookName != 'displayBackOfficeHeader') {
564                $sql->join(Shop::addSqlAssociation('module', 'm', true, 'module_shop.enable_device & '.(int) Context::getContext()->getDevice()));
565                $sql->innerJoin('module_shop', 'ms', 'ms.`id_module` = m.`id_module`');
566            }
567            $sql->innerJoin('hook_module', 'hm', 'hm.`id_module` = m.`id_module`');
568            $sql->innerJoin('hook', 'h', 'hm.`id_hook` = h.`id_hook`');
569            if ($hookName != 'displayPayment' && $hookName != 'displayPaymentEU') {
570                $sql->where('h.`name` != "displayPayment" AND h.`name` != "displayPaymentEU"');
571            } // For payment modules, we check that they are available in the contextual country
572            elseif ($frontend) {
573                if (Validate::isLoadedObject($context->country)) {
574                    $sql->where('((h.`name` = "displayPayment" OR h.`name` = "displayPaymentEU") AND (SELECT `id_country` FROM `'._DB_PREFIX_.'module_country` mc WHERE mc.`id_module` = m.`id_module` AND `id_country` = '.(int) $context->country->id.' AND `id_shop` = '.(int) $context->shop->id.' LIMIT 1) = '.(int) $context->country->id.')');
575                }
576                if (Validate::isLoadedObject($context->currency)) {
577                    $sql->where('((h.`name` = "displayPayment" OR h.`name` = "displayPaymentEU") AND (SELECT `id_currency` FROM `'._DB_PREFIX_.'module_currency` mcr WHERE mcr.`id_module` = m.`id_module` AND `id_currency` IN ('.(int) $context->currency->id.', -1, -2) LIMIT 1) IN ('.(int) $context->currency->id.', -1, -2))');
578                }
579                if (Validate::isLoadedObject($context->cart)) {
580                    $carrier = new Carrier($context->cart->id_carrier);
581                    if (Validate::isLoadedObject($carrier)) {
582                        $sql->where('((h.`name` = "displayPayment" OR h.`name` = "displayPaymentEU") AND (SELECT `id_reference` FROM `'._DB_PREFIX_.'module_carrier` mcar WHERE mcar.`id_module` = m.`id_module` AND `id_reference` = '.(int) $carrier->id_reference.' AND `id_shop` = '.(int) $context->shop->id.' LIMIT 1) = '.(int) $carrier->id_reference.')');
583                    }
584                }
585            }
586            if (Validate::isLoadedObject($context->shop)) {
587                $sql->where('hm.`id_shop` = '.(int) $context->shop->id);
588            }
589
590            if ($frontend) {
591                if ($useGroups) {
592                    $sql->leftJoin('module_group', 'mg', 'mg.`id_module` = m.`id_module`');
593                    if (Validate::isLoadedObject($context->shop)) {
594                        $sql->where('mg.`id_shop` = '.((int) $context->shop->id).(count($groups) ? ' AND  mg.`id_group` IN ('.implode(', ', $groups).')' : ''));
595                    } elseif (count($groups)) {
596                        $sql->where('mg.`id_group` IN ('.implode(', ', $groups).')');
597                    }
598                }
599            }
600
601            $sql->groupBy('hm.id_hook, hm.id_module');
602            $sql->orderBy('hm.`position`');
603
604            $list = [];
605            if ($result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql)) {
606                foreach ($result as $row) {
607                    $row['hook'] = strtolower($row['hook']);
608                    if (!isset($list[$row['hook']])) {
609                        $list[$row['hook']] = [];
610                    }
611
612                    $list[$row['hook']][] = [
613                        'id_hook'   => $row['id_hook'],
614                        'module'    => $row['module'],
615                        'id_module' => $row['id_module'],
616                        'live_edit' => $row['live_edit'],
617                    ];
618                }
619            }
620            if ($hookName != 'displayPayment' && $hookName != 'displayPaymentEU' && $hookName != 'displayBackOfficeHeader') {
621                Cache::store($cacheId, $list);
622                // @todo remove this in 1.6, we keep it in 1.5 for backward compatibility
623                static::$_hook_modules_cache_exec = $list;
624            }
625        } else {
626            $list = Cache::retrieve($cacheId);
627        }
628
629        // If hook_name is given, just get list of modules for this hook
630        if ($hookName) {
631            $retroHookName = strtolower(Hook::getRetroHookName($hookName));
632            $hookName = strtolower($hookName);
633
634            $return = [];
635            $insertedModules = [];
636            if (isset($list[$hookName])) {
637                $return = $list[$hookName];
638            }
639            foreach ($return as $module) {
640                $insertedModules[] = $module['id_module'];
641            }
642            if (isset($list[$retroHookName])) {
643                foreach ($list[$retroHookName] as $retroModuleCall) {
644                    if (!in_array($retroModuleCall['id_module'], $insertedModules)) {
645                        $return[] = $retroModuleCall;
646                    }
647                }
648            }
649
650            return (count($return) > 0 ? $return : false);
651        } else {
652            return $list;
653        }
654    }
655
656    /**
657     * Return backward compatibility hook name
658     *
659     *
660     * @param string $hookName Hook name
661     *
662     * @return int Hook ID
663     *
664     * @since   1.0.0
665     * @version 1.0.0 Initial version
666     */
667    public static function getRetroHookName($hookName)
668    {
669        $aliasList = Hook::getHookAliasList();
670        if (isset($aliasList[strtolower($hookName)])) {
671            return $aliasList[strtolower($hookName)];
672        }
673
674        $retroHookName = array_search($hookName, $aliasList);
675        if ($retroHookName === false) {
676            return '';
677        }
678
679        return $retroHookName;
680    }
681
682    /**
683     * Get list of hook alias
684     *
685     *
686     * @return array
687     *
688     * @since   1.0.0
689     * @version 1.0.0 Initial version
690     */
691    public static function getHookAliasList()
692    {
693        $cacheId = 'hook_alias';
694        if (!Cache::isStored($cacheId)) {
695            $hookAliasList = Db::getInstance()->executeS('SELECT * FROM `'._DB_PREFIX_.'hook_alias`');
696            $hookAlias = [];
697            if ($hookAliasList) {
698                foreach ($hookAliasList as $ha) {
699                    $hookAlias[strtolower($ha['alias'])] = $ha['name'];
700                }
701            }
702            Cache::store($cacheId, $hookAlias);
703
704            return $hookAlias;
705        }
706
707        return Cache::retrieve($cacheId);
708    }
709
710    /**
711     * Return hook ID from name
712     *
713     * @param string $hookName Hook name
714     *
715     * @return int Hook ID
716     *
717     * @since   1.0.0
718     * @version 1.0.0 Initial version
719     */
720    public static function getIdByName($hookName)
721    {
722        $hookName = strtolower($hookName);
723        if (!Validate::isHookName($hookName)) {
724            return false;
725        }
726
727        $cacheId = 'hook_idsbyname';
728        if (!Cache::isStored($cacheId)) {
729            // Get all hook ID by name and alias
730            $hookIds = [];
731            $db = Db::getInstance();
732            $result = $db->ExecuteS(
733                '
734			SELECT `id_hook`, `name`
735			FROM `'._DB_PREFIX_.'hook`
736			UNION
737			SELECT `id_hook`, ha.`alias` AS name
738			FROM `'._DB_PREFIX_.'hook_alias` ha
739			INNER JOIN `'._DB_PREFIX_.'hook` h ON ha.name = h.name', false
740            );
741            while ($row = $db->nextRow($result)) {
742                $hookIds[strtolower($row['name'])] = $row['id_hook'];
743            }
744            Cache::store($cacheId, $hookIds);
745        } else {
746            $hookIds = Cache::retrieve($cacheId);
747        }
748
749        return (isset($hookIds[$hookName]) ? $hookIds[$hookName] : false);
750    }
751
752    /**
753     * @param $module
754     * @param $method
755     * @param $params
756     *
757     * @return mixed
758     *
759     * @since   1.0.0
760     * @version 1.0.0 Initial version
761     */
762    public static function coreCallHook($module, $method, $params)
763    {
764        // Define if we will log modules performances for this session
765        if (Module::$_log_modules_perfs === null) {
766            $modulo = _PS_DEBUG_PROFILING_ ? 1 : Configuration::get('PS_log_modules_perfs_MODULO');
767            Module::$_log_modules_perfs = ($modulo && mt_rand(0, $modulo - 1) == 0);
768            if (Module::$_log_modules_perfs) {
769                Module::$_log_modules_perfs_session = mt_rand();
770            }
771        }
772
773        // Immediately return the result if we do not log performances
774        if (!Module::$_log_modules_perfs) {
775            return $module->{$method}($params);
776        }
777
778        // Store time and memory before and after hook call and save the result in the database
779        $timeStart = microtime(true);
780        $memoryStart = memory_get_usage(true);
781
782        // Call hook
783        $r = $module->{$method}($params);
784
785        $timeEnd = microtime(true);
786        $memoryEnd = memory_get_usage(true);
787
788        Db::getInstance()->execute(
789            '
790		INSERT INTO '._DB_PREFIX_.'modules_perfs (session, module, method, time_start, time_end, memory_start, memory_end)
791		VALUES ('.(int) Module::$_log_modules_perfs_session.', "'.pSQL($module->name).'", "'.pSQL($method).'", "'.pSQL($timeStart).'", "'.pSQL($timeEnd).'", '.(int) $memoryStart.', '.(int) $memoryEnd.')'
792        );
793
794        return $r;
795    }
796
797    /**
798     * @param $display
799     * @param $moduleInstance
800     * @param $idHook
801     *
802     * @return string
803     *
804     * @since   1.0.0
805     * @version 1.0.0 Initial version
806     */
807    public static function wrapLiveEdit($display, $moduleInstance, $idHook)
808    {
809        return '<script type="text/javascript"> modules_list.push(\''.Tools::safeOutput($moduleInstance->name).'\');</script>
810				<div id="hook_'.(int) $idHook.'_module_'.(int) $moduleInstance->id.'_moduleName_'.str_replace('_', '-', Tools::safeOutput($moduleInstance->name)).'"
811				class="dndModule" style="border: 1px dotted red;'.(!strlen($display) ? 'height:50px;' : '').'">
812					<span style="font-family: Georgia;font-size:13px;font-style:italic;">
813						<img style="padding-right:5px;" src="'._MODULE_DIR_.Tools::safeOutput($moduleInstance->name).'/logo.gif">'
814            .Tools::safeOutput($moduleInstance->displayName).'<span style="float:right">
815				<a href="#" id="'.(int) $idHook.'_'.(int) $moduleInstance->id.'" class="moveModule">
816					<img src="'._PS_ADMIN_IMG_.'arrow_out.png"></a>
817				<a href="#" id="'.(int) $idHook.'_'.(int) $moduleInstance->id.'" class="unregisterHook">
818					<img src="'._PS_ADMIN_IMG_.'delete.gif"></a></span>
819				</span>'.$display.'</div>';
820    }
821
822    /**
823     * @deprecated 1.0.0
824     *
825     * @param int $newOrderStatusId
826     * @param int $idOrder
827     *
828     * @return string
829     */
830    public static function postUpdateOrderStatus($newOrderStatusId, $idOrder)
831    {
832        Tools::displayAsDeprecated();
833        $order = new Order((int) $idOrder);
834        $newOs = new OrderState((int) $newOrderStatusId, $order->id_lang);
835        $return = Hook::exec('postUpdateOrderStatus', ['newOrderStatus' => $newOs, 'id_order' => (int) ($order->id)]);
836
837        return $return;
838    }
839
840    /**
841     * @deprecated 1.0.0
842     *
843     * @param int $idOrder
844     *
845     * @return bool|string
846     */
847    public static function orderConfirmation($idOrder)
848    {
849        Tools::displayAsDeprecated();
850        if (Validate::isUnsignedId($idOrder)) {
851            $params = [];
852            $order = new Order((int) $idOrder);
853            $currency = new Currency((int) $order->id_currency);
854
855            if (Validate::isLoadedObject($order)) {
856                $cart = new Cart((int) $order->id_cart);
857                $params['total_to_pay'] = $cart->getOrderTotal();
858                $params['currency'] = $currency->sign;
859                $params['objOrder'] = $order;
860                $params['currencyObj'] = $currency;
861
862                return Hook::exec('orderConfirmation', $params);
863            }
864        }
865
866        return false;
867    }
868
869    /**
870     * @deprecated 1.0.0
871     *
872     * @param $idOrder
873     * @param $id_module
874     *
875     * @return bool|string
876     */
877    public static function paymentReturn($idOrder, $id_module)
878    {
879        Tools::displayAsDeprecated();
880        if (Validate::isUnsignedId($idOrder) && Validate::isUnsignedId($id_module)) {
881            $params = [];
882            $order = new Order((int) ($idOrder));
883            $currency = new Currency((int) ($order->id_currency));
884
885            if (Validate::isLoadedObject($order)) {
886                $cart = new Cart((int) $order->id_cart);
887                $params['total_to_pay'] = $cart->getOrderTotal();
888                $params['currency'] = $currency->sign;
889                $params['objOrder'] = $order;
890                $params['currencyObj'] = $currency;
891
892                return Hook::exec('paymentReturn', $params, (int) ($id_module));
893            }
894        }
895
896        return false;
897    }
898
899    /**
900     * @deprecated 1.0.0
901     *
902     * @param $pdf
903     * @param $id_order
904     *
905     * @return bool|string
906     */
907    public static function PDFInvoice($pdf, $id_order)
908    {
909        Tools::displayAsDeprecated();
910        if (!is_object($pdf) || !Validate::isUnsignedId($id_order)) {
911            return false;
912        }
913
914        return Hook::exec('PDFInvoice', ['pdf' => $pdf, 'id_order' => $id_order]);
915    }
916
917    /**
918     * @deprecated 1.0.0
919     *
920     * @param $module
921     *
922     * @return string
923     */
924    public static function backBeforePayment($module)
925    {
926        Tools::displayAsDeprecated();
927        if ($module) {
928            return Hook::exec('backBeforePayment', ['module' => strval($module)]);
929        }
930    }
931
932    /**
933     * @deprecated 1.0.0
934     *
935     * @param $idCarrier
936     * @param $carrier
937     *
938     * @return bool|string
939     */
940    public static function updateCarrier($idCarrier, $carrier)
941    {
942        Tools::displayAsDeprecated();
943        if (!Validate::isUnsignedId($idCarrier) || !is_object($carrier)) {
944            return false;
945        }
946
947        return Hook::exec('updateCarrier', ['id_carrier' => $idCarrier, 'carrier' => $carrier]);
948    }
949
950    /**
951     * Preload hook modules cache
952     *
953     * @deprecated 1.0.0 use Hook::getHookModuleList() instead
954     *
955     * @return bool preload_needed
956     */
957    public static function preloadHookModulesCache()
958    {
959        Tools::displayAsDeprecated('Use Hook::getHookModuleList() instead');
960
961        if (!is_null(static::$_hook_modules_cache)) {
962            return false;
963        }
964
965        static::$_hook_modules_cache = Hook::getHookModuleList();
966
967        return true;
968    }
969
970    /**
971     * Return hook ID from name
972     *
973     * @param string $hookName Hook name
974     *
975     * @return int Hook ID
976     *
977     * @deprecated 1.0.0 use Hook::getIdByName() instead
978     */
979    public static function get($hookName)
980    {
981        Tools::displayAsDeprecated('Use Hook::getIdByName() instead');
982        if (!Validate::isHookName($hookName)) {
983            die(Tools::displayError());
984        }
985
986        $result = Db::getInstance()->getRow(
987            '
988		SELECT `id_hook`, `name`
989		FROM `'._DB_PREFIX_.'hook`
990		WHERE `name` = \''.pSQL($hookName).'\''
991        );
992
993        return ($result ? $result['id_hook'] : false);
994    }
995
996    /**
997     * Called when quantity of a product is updated.
998     *
999     * @deprecated 1.0.0
1000     *
1001     * @param Cart     $cart
1002     * @param Order    $order
1003     * @param Customer $customer
1004     * @param Currency $currency
1005     * @param          $orderStatus
1006     *
1007     * @throws \Exception
1008     *
1009     * @return string
1010     */
1011    public static function newOrder($cart, $order, $customer, $currency, $orderStatus)
1012    {
1013        Tools::displayAsDeprecated();
1014
1015        return Hook::exec(
1016            'newOrder', [
1017                'cart'        => $cart,
1018                'order'       => $order,
1019                'customer'    => $customer,
1020                'currency'    => $currency,
1021                'orderStatus' => $orderStatus,
1022            ]
1023        );
1024    }
1025
1026    /**
1027     * @deprecated 1.0.0
1028     *
1029     * @param      $product
1030     * @param null $order
1031     *
1032     * @return string
1033     */
1034    public static function updateQuantity($product, $order = null)
1035    {
1036        Tools::displayAsDeprecated();
1037
1038        return Hook::exec('updateQuantity', ['product' => $product, 'order' => $order]);
1039    }
1040
1041    /**
1042     * @deprecated 1.0.0
1043     *
1044     * @param $product
1045     * @param $category
1046     *
1047     * @return string
1048     */
1049    public static function productFooter($product, $category)
1050    {
1051        Tools::displayAsDeprecated();
1052
1053        return Hook::exec('productFooter', ['product' => $product, 'category' => $category]);
1054    }
1055
1056    /**
1057     * @deprecated 1.0.0
1058     *
1059     * @param $product
1060     *
1061     * @return string
1062     */
1063    public static function productOutOfStock($product)
1064    {
1065        Tools::displayAsDeprecated();
1066
1067        return Hook::exec('productOutOfStock', ['product' => $product]);
1068    }
1069
1070    /**
1071     * @deprecated 1.0.0
1072     *
1073     * @param $product
1074     *
1075     * @return string
1076     */
1077    public static function addProduct($product)
1078    {
1079        Tools::displayAsDeprecated();
1080
1081        return Hook::exec('addProduct', ['product' => $product]);
1082    }
1083
1084    /**
1085     * @deprecated 1.0.0
1086     *
1087     * @param $product
1088     *
1089     * @return string
1090     */
1091    public static function updateProduct($product)
1092    {
1093        Tools::displayAsDeprecated();
1094
1095        return Hook::exec('updateProduct', ['product' => $product]);
1096    }
1097
1098    /**
1099     * @deprecated 1.0.0
1100     *
1101     * @param $product
1102     *
1103     * @return string
1104     */
1105    public static function deleteProduct($product)
1106    {
1107        Tools::displayAsDeprecated();
1108
1109        return Hook::exec('deleteProduct', ['product' => $product]);
1110    }
1111
1112    /**
1113     * @deprecated 1.0.0
1114     */
1115    public static function updateProductAttribute($idProductAttribute)
1116    {
1117        Tools::displayAsDeprecated();
1118
1119        return Hook::exec('updateProductAttribute', ['id_product_attribute' => $idProductAttribute]);
1120    }
1121
1122    /**
1123     * @param bool $autodate
1124     * @param bool $nullValues
1125     *
1126     * @return bool
1127     *
1128     * @since   1.0.0
1129     * @version 1.0.0 Initial version
1130     */
1131    public function add($autodate = true, $nullValues = false)
1132    {
1133        Cache::clean('hook_idsbyname');
1134
1135        return parent::add($autodate, $nullValues);
1136    }
1137}
1138