/
var
/
www
/
html
/
sugardemo
/
include
/
utils
/
Upload File
HOME
<?php /* * Your installation or use of this SugarCRM file is subject to the applicable * terms available at * http://support.sugarcrm.com/Resources/Master_Subscription_Agreements/. * If you do not agree to all of the applicable terms or do not have the * authority to bind the entity as an authorized representative, then do not * install or use this SugarCRM file. * * Copyright (C) SugarCRM Inc. All rights reserved. */ use Sugarcrm\Sugarcrm\DependencyInjection\Container; use Sugarcrm\Sugarcrm\Security\Context; use Sugarcrm\Sugarcrm\Security\Subject\LogicHook as Subject; /** * Predefined logic hooks * after_ui_frame * after_ui_footer * after_save * before_save * before_retrieve * after_retrieve * process_record * before_delete * after_delete * before_restore * after_restore * server_roundtrip * before_logout * after_logout * before_login * after_login * login_failed * after_session_start * after_entry_point * before_filter * * @api */ class LogicHook { /** * @var SugarBean */ public $bean = null; /** * Static Function which returns and instance of LogicHook * * @return unknown */ public static function initialize() { if (empty($GLOBALS['logic_hook'])) { $GLOBALS['logic_hook'] = new LogicHook(); } return $GLOBALS['logic_hook']; } public function setBean($bean) { $this->bean = $bean; return $this; } protected $hook_map = []; protected $hookscan = []; public function getHooksMap() { return $this->hook_map; } public function getHooksList() { return $this->hookscan; } public function scanHooksDir($extpath) { if (is_dir($extpath)) { $dir = dir($extpath); while ($entry = $dir->read()) { if ($entry != '.' && $entry != '..' && strtolower(substr($entry, -4)) == '.php' && is_file($extpath . '/' . $entry)) { unset($hook_array); include $extpath . '/' . $entry; if (!empty($hook_array)) { foreach ($hook_array as $type => $hookg) { foreach ($hookg as $index => $hook) { $this->hookscan[$type][] = $hook; $idx = safeCount($this->hookscan[$type]) - 1; $this->hook_map[$type][$idx] = ['file' => $extpath . '/' . $entry, 'index' => $index]; } } } } } } } protected static $hooks = []; public static function refreshHooks() { self::$hooks = []; } public function loadHooks($module_dir) { $hook_array = []; if (!empty($module_dir)) { $custom = "custom/modules/$module_dir"; } else { $custom = 'custom/modules'; } foreach (SugarAutoLoader::existing( "$custom/logic_hooks.php", SugarAutoLoader::loadExtension('logichooks', empty($module_dir) ? 'application' : $module_dir) ) as $file) { if (isset($GLOBALS['log'])) { $GLOBALS['log']->debug('Including hook file: ' . $file); } include $file; } return $hook_array; } public function getHooks($module_dir, $refresh = false) { if ($refresh || !isset(self::$hooks[$module_dir])) { self::$hooks[$module_dir] = $this->loadHooks($module_dir); } return self::$hooks[$module_dir]; } /** * Provide a means for developers to create upgrade safe business logic hooks. * If the bean is null, then we assume this call was not made from a SugarBean Object and * therefore we do not pass it to the method call. * * @param string $module_dir * @param string $event * @param array $arguments */ public function call_custom_logic($module_dir, $event, $arguments = []) { $origBean = $this->bean; if ($origBean === null) { $bean = BeanFactory::newBean($module_dir); if ($bean instanceof SugarBean) { $this->setBean($bean); } } if (isset($GLOBALS['log'])) { $GLOBALS['log']->debug("Hook called: $module_dir::$event"); } $modules = [null]; if ($module_dir) { array_unshift($modules, $module_dir); } foreach ($modules as $module) { $hooks = $this->getHooks($module); $this->process_hooks($hooks, $event, $arguments); } if ($origBean === null) { $this->setBean($origBean); } } /** * Apply sorting to the hooks using the sort index. Hooks with matching * sort indexes will be processed in no particular order. * * @param array $hookArray * @return array Sorted indices */ protected function getProcessOrder(array $hookArray) { $sortedIndices = []; foreach ($hookArray as $idx => $hookDetails) { $sortedIndices[$idx] = $hookDetails[0]; } asort($sortedIndices); return array_keys($sortedIndices); } /** * Prepare availability of given class * @param string $class Fully qualified class name * @param string $file Optional filename, only used for non namespaced classes * @return boolean */ protected function loadHookClass($class, $file = null) { // Ignore file parameter if class is namespaced if (false !== strpos($class, '\\')) { $file = null; } // If no file is given, only autoloader can help us out if (empty($file)) { return class_exists($class); } // Chech if file exists if (!SugarAutoLoader::load($file)) { return false; } // Finally check if our class is available return class_exists($class); } /** * Log wrapper * @param string $level Log level * @param string $msg Log message */ protected function log($level, $msg) { if (!empty($GLOBALS['log'])) { $GLOBALS['log']->{$level}($msg); } } /** * This is called from call_custom_logic and actually performs the action as defined in the * logic hook. If the bean is null, then we assume this call was not made from a SugarBean Object and * therefore we do not pass it to the method call. * * @param array $hookArray * @param string $event * @param array $arguments * * @throws SugarApiExceptionInvalidParameter Throw stock or custom exception and interrupt save operation */ public function process_hooks($hookArray, $event, $arguments) { // Skip if event is unknown if (empty($hookArray[$event])) { return; } // Apply sorting $processOrder = $this->getProcessOrder($hookArray[$event]); foreach ($processOrder as $hookIndex) { $hookDetails = $hookArray[$event][$hookIndex]; $hookFile = $hookDetails[2]; $hookClass = $hookDetails[3]; $hookFunc = $hookDetails[4]; $hookLabel = $hookDetails[5] ?? null; if (!$this->loadHookClass($hookClass, $hookFile)) { $this->log('error', "Unable to load custom logic class '$hookClass'"); continue; } $context = Container::getInstance()->get(Context::class); $subject = new Subject($hookClass, $hookFunc, $hookLabel); $context->activateSubject($subject); try { if (strcasecmp($hookClass, $hookFunc) === 0) { $this->log('debug', "Creating new instance of hook class '$hookClass' with parameters"); if (!is_null($this->bean)) { new $hookClass($this->bean, $event, $arguments); } else { new $hookClass($event, $arguments); } } else { $this->log('debug', "Creating new instance of hook class '$hookClass' without parameters"); if (!method_exists($hookClass, $hookFunc)) { $this->log('error', "Method $hookFunc does not exist in class $hookClass"); continue; } $hookObject = new $hookClass(); if (!is_callable([$hookObject, $hookFunc])) { $this->log('error', "Method $hookFunc of class $hookClass cannot be called"); continue; } if (!is_null($this->bean)) { // & is here because of BR-1345 and old broken hooks that use &$bean in args $params = array_merge([&$this->bean, $event, $arguments], array_slice($hookDetails, 5)); call_user_func_array([$hookObject, $hookFunc], $params); } else { $hookObject->$hookFunc($event, $arguments); } } } catch (SugarApiExceptionInvalidParameter $e) { throw $e; } catch (Exception $e) { if (!defined('SUGAR_PHPUNIT_RUNNER')) { throw $e; } } catch (Throwable $e) { $this->log('fatal', 'Error executing hook: ' . $e->getMessage()); } finally { $context->deactivateSubject($subject); } if ($this->bean && $event === 'before_save') { $this->bean->enqueueAuditedStateChanges($subject); } } } }