<commit_before><?php
/*
 * @copyright Copyright (c) 2016, Afterlogic Corp.
 * @license AGPL-3.0
 *
 * This code is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License, version 3,
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 * 
 */

/**
 * @package Api
 */
class CApiModuleManager
{
    /**
     * This array contains a list of modules
     *
     * @var array
     */
	protected $_aModules = array();
	
    /**
     * This array contains a list of modules
     *
     * @var array
     */
	protected $_aAllowedModulesName = array();

	/**
     * This array contains a list of callbacks we should call when certain events are triggered
     *
     * @var array
     */
    protected $_aSubscriptions = array();
	
    /**
     * @var array
     */    
	protected $_aObjects = array();
	
	/**
	 * @var array
	 */
	private $_aTemplates;
	
	/**
	 * @var array
	 */
	private $_aResults;
	
	/**
	 * 
	 * @return \self
	 */
	public static function createInstance()
	{
		return new self();
	}
	
	/**
	 * 
	 * @return string
	 */
	public function init()
	{
		$sModulesPath = $this->GetModulesPath();
		
		$oCoreModule = $this->loadModule('Core', $sModulesPath);
		if ($oCoreModule !== false)
		{
			\CApi::authorise();
			
			$oCoreModule->initialize();
			$sTenant = $oCoreModule->GetTenantName();
			$aModulePath = array(
				$sModulesPath
			);
			if (!empty(trim($sTenant)))
			{
				$sTenantModulesPath = $this->GetTenantModulesPath($sTenant);
				array_unshift($aModulePath, $sTenantModulesPath);
			}
			$aModulesPath = array();
			foreach ($aModulePath as $sModulesPath)
			{
				if (@is_dir($sModulesPath))
				{
					if (false !== ($rDirHandle = @opendir($sModulesPath)))
					{
						while (false !== ($sFileItem = @readdir($rDirHandle)))
						{
							if (0 < strlen($sFileItem) && '.' !== $sFileItem{0} && preg_match('/^[a-zA-Z0-9\-]+$/', $sFileItem))
							{
								$aModulesPath[$sModulesPath][] = $sFileItem;
							}
						}

						@closedir($rDirHandle);
					}
				}
			}
			foreach ($aModulesPath as $aModulePath)
			{
				foreach ($aModulePath as $sModuleName)
				{
					$oModuleSettings = \CApi::GetModuleManager()->GetModuleSettings($sModuleName);
					if (!$oModuleSettings->GetConf('Disabled', false))
					{
						$this->_aAllowedModulesName[strtolower($sModuleName)] = $sModuleName;
						$this->loadModule($sModuleName, $sModulesPath);
					}
				}
			}
			foreach ($this->_aModules as $oModule)
			{
				if ($oModule instanceof \AApiModule)
				{
					$oModule->initialize();
				}
			}
		}
		else
		{
			echo 'Can\'t load \'Core\' Module';
			return '';
		}
	}
	
	/**
	 * 
	 * @param string $sModuleName
	 * @return boolean
	 */
	public function isModuleLoaded($sModuleName)
	{
		return array_key_exists(strtolower($sModuleName), $this->_aModules);
	}

	/**
	 * 
	 * @param string $sModuleName
	 * @param string $sConfigName
	 * @param string $sDefaultValue
	 * @return mixed
	 */
	public function getModuleConfigValue($sModuleName, $sConfigName, $sDefaultValue = null)
	{
		$mResult = $sDefaultValue;
		$oModuleConfig = $this->GetModuleConfig($sModuleName);
		if ($oModuleConfig)
		{
			$mResult = $oModuleConfig->GetConf($sConfigName, $sDefaultValue);
		}
		
		return $mResult;
	}

	/**
	 * 
	 * @param string $sModuleName
	 * @param string $sModulePath
	 * @return \AApiModule
	 */
	public function loadModule($sModuleName, $sModulePath)
	{
		$mResult = false;
		$aArgs = array($sModuleName, $sModulePath);
		$this->broadcastEvent(
			$sModuleName, 
			'loadModule' . \AApiModule::$Delimiter . 'before', 
			$aArgs
		);
		
		$sModuleFilePath = $sModulePath.$sModuleName.'/module.php';
		if (@file_exists($sModuleFilePath) && !$this->isModuleLoaded($sModuleName))
		{
		   include_once $sModuleFilePath;
		   $sModuleClassName = $sModuleName . 'Module';
		   if (class_exists($sModuleClassName))
		   {
			   $oModule = call_user_func(
					array($sModuleClassName, 'createInstance'), 
					$sModuleName, 
					$sModulePath
				);
			   if ($oModule instanceof \AApiModule)
			   {
					$this->_aModules[strtolower($sModuleName)] = $oModule;
					$mResult = $oModule;
			   }
		   }
		}

		$this->broadcastEvent(
			$sModuleName, 
			'loadModule' . \AApiModule::$Delimiter . 'after', 
			$aArgs,
			$mResult
		);
		
		return $mResult;
	}

    /**
	 * 
	 * @return array
	 */
	public function getEvents() 
	{
		return $this->_aSubscriptions;
	}	
	
    /**
     * Subscribe to an event.
     *
     * When the event is triggered, we'll call all the specified callbacks.
     * It is possible to control the order of the callbacks through the
     * priority argument.
     *
     * This is for example used to make sure that the authentication plugin
     * is triggered before anything else. If it's not needed to change this
     * number, it is recommended to ommit.
     *
     * @param string $sEvent
     * @param callback $fCallback
     * @param int $iPriority
     * @return void
     */
    public function subscribeEvent($sEvent, $fCallback, $iPriority = 100) 
	{
        if (!isset($this->_aSubscriptions[$sEvent])) 
		{
            $this->_aSubscriptions[$sEvent] = array();
        }
        while(isset($this->_aSubscriptions[$sEvent][$iPriority]))	
		{
			$iPriority++;
		}
        $this->_aSubscriptions[$sEvent][$iPriority] = $fCallback;
        ksort($this->_aSubscriptions[$sEvent]);
    }	
	
    /**
     * Broadcasts an event
     *
     * This method will call all subscribers. If one of the subscribers returns false, the process stops.
     *
     * The arguments parameter will be sent to all subscribers
     *
     * @param string $sEvent
     * @param array $aArguments
     * @param mixed $mResult
     * @return boolean
     */
    public function broadcastEvent($sModule, $sEvent, &$aArguments = array(), &$mResult = null, &$bCountinue = true) 
	{
		$bResult = false;
		$aSubscriptions = array();
		if (isset($this->_aSubscriptions[$sEvent])) 
		{
			$aSubscriptions = array_merge(
				$aSubscriptions, 
				$this->_aSubscriptions[$sEvent]
			);
        }
		$sEvent = $sModule . \AApiModule::$Delimiter . $sEvent;
		if (isset($this->_aSubscriptions[$sEvent])) 
		{
			$aSubscriptions = array_merge(
				$aSubscriptions, 
				$this->_aSubscriptions[$sEvent]
			);
		}
		
		foreach($aSubscriptions as $fCallback) 
		{
			if (is_callable($fCallback))
			{
				\CApi::Log('Execute subscription: '. $fCallback[0]->GetName() . \AApiModule::$Delimiter . $fCallback[1]);
				$mCallBackResult = call_user_func_array(
					$fCallback, 
					array(
						&$aArguments,
						&$mResult
					)
				);

				\CApi::GetModuleManager()->AddResult(
					$fCallback[0]->GetName(), 
					$sEvent, 
					$mCallBackResult
				);

				if ($mCallBackResult) 
				{
					$bResult = $mCallBackResult;
					break;
				}
			}
		}

        return $bResult;
    }	
	
	/**
	 * @param string $sParsedTemplateID
	 * @param string $sParsedPlace
	 * @param string $sTemplateFileName
	 * @param string $sModuleName
	 */
	public function includeTemplate($sParsedTemplateID, $sParsedPlace, $sTemplateFileName, $sModuleName = '')
	{
		if (!isset($this->_aTemplates[$sParsedTemplateID]))
		{
			$this->_aTemplates[$sParsedTemplateID] = array();
		}

		$this->_aTemplates[$sParsedTemplateID][] = array(
			$sParsedPlace, 
			$sTemplateFileName, 
			$sModuleName
		);
	}	
	
	/**
	 * 
	 * @param string $sTemplateID
	 * @param string $sTemplateSource
	 * @return string
	 */
	public function ParseTemplate($sTemplateID, $sTemplateSource)
	{
		if (isset($this->_aTemplates[$sTemplateID]) && is_array($this->_aTemplates[$sTemplateID]))
		{
			foreach ($this->_aTemplates[$sTemplateID] as $aItem)
			{
				if (!empty($aItem[0]) && !empty($aItem[1]) && file_exists($aItem[1]))
				{
					$sTemplateHtml = file_get_contents($aItem[1]);
					if (!empty($aItem[2]))
					{
						$sTemplateHtml = str_replace('%ModuleName%', $aItem[2], $sTemplateHtml);
						$sTemplateHtml = str_replace('%MODULENAME%', strtoupper($aItem[2]), $sTemplateHtml);
					}
					$sTemplateSource = str_replace('{%INCLUDE-START/'.$aItem[0].'/INCLUDE-END%}',
						$sTemplateHtml.'{%INCLUDE-START/'.$aItem[0].'/INCLUDE-END%}', $sTemplateSource);
				}
			}
		}

		return $sTemplateSource;
	}	
	
	public function setObjectMap()
	{
	}

	/**
	 * @return string
	 */
	public function GetModulesPath()
	{
		return AURORA_APP_ROOT_PATH.'modules/';
	}

	/**
	 * @return string
	 */
	public function GetTenantModulesPath($sTenant)
	{
		return AURORA_APP_ROOT_PATH.'tenants/' . $sTenant . '/modules/';
	}

	/**
	 * @return array
	 */
	public function GetAllowedModulesName()
	{
		return $this->_aAllowedModulesName;
	}

	/**
	 * @return array
	 */
	public function GetModules()
	{
		return $this->_aModules;
	}
	
	public function GetModuleSettings($sModuleName)
	{
		if (!isset($this->aModulesSettings[strtolower($sModuleName)]))
		{
			$this->aModulesSettings[strtolower($sModuleName)] = new \CApiModuleSettings($sModuleName);
		}
		
		return $this->aModulesSettings[strtolower($sModuleName)];
	}
	
	/**
	 * @param string $sModuleName
	 * @return AApiModule
	 */
	public function GetModule($sModuleName)
	{
		$sModuleName = strtolower($sModuleName);
		return (isset($this->_aModules[$sModuleName]) &&  $this->_aModules[$sModuleName] instanceof \AApiModule) ? $this->_aModules[$sModuleName] : false;
	}
	
	
	/**
	 * @return \AApiModule
	 */
	public function GetModuleFromRequest()
	{
		$sModule = '';
		$oHttp = \MailSo\Base\Http::SingletonInstance();
		if ($oHttp->IsPost()) 
		{
			$sModule = $oHttp->GetPost('Module', null);
		} 
		else 
		{
			$aPath = \System\Service::GetPaths();
			$sModule = (isset($aPath[1])) ? $aPath[1] : '';
		}
		return $this->GetModule($sModule);
	}
	
	/**
	 * 
	 * @param string $sEntryName
	 * @return array
	 */
	public function GetModulesByEntry($sEntryName)
	{
		$aModules = array();
		$oResult = $this->GetModuleFromRequest();
		
		if ($oResult && !$oResult->HasEntry($sEntryName)) 
		{
			$oResult = false;
		}
		if ($oResult === false) 
		{
			foreach ($this->_aModules as $oModule) 
			{
				if ($oModule instanceof AApiModule && $oModule->HasEntry($sEntryName)) 
				{
					$aModules[] = $oModule;
				}
			}
		}
		else
		{
			$aModules = array(
				$oResult
			);
		}
		
		return $aModules;
	}
	
	/**
	 * @param string $sModuleName
	 * @return bool
	 */
	public function ModuleExists($sModuleName)
	{
		return ($this->GetModule($sModuleName)) ? true  : false;
	}
	
	/**
	 * 
	 * @param string $sEntryName
	 * @return mixed
	 */
	public function RunEntry($sEntryName)
	{
		$mResult = false;
		$oModule = $this->GetModuleFromRequest();
		if ($oModule instanceof \AApiModule && $oModule->HasEntry($sEntryName)) 
		{
			$mResult = $oModule->RunEntry($sEntryName);
		}
		
		return $mResult;
	}

	/**
	 * @return string
	 */
	public function Hash()
	{
		$sResult = md5(CApi::Version());
		foreach ($this->_aModules as $oModule) {
			
			$sResult = md5($sResult.$oModule->GetPath().$oModule->GetName().$oModule->GetHash());
		}

		return $sResult;
	}
	
	/**
	 * 
	 * @param string $sModule
	 * @param string $sMethod
	 * @param mixed $mResult
	 */
	public function AddResult($sModule, $sMethod, &$mResult, $iErrorCode = 0)
	{
		$aResult = array(
			'Module' => $sModule,
			'Method' => $sMethod,
			'Result' => $mResult
		);
		
		if ($iErrorCode > 0)
		{
			$aResult['ErrorCode'] = $iErrorCode;
		}
		
		$this->_aResults[] = $aResult;
	}
	
	public function GetResults()
	{
		return $this->_aResults;
	}
	
	public function GetResult($sModule, $sMethod)
	{
		foreach($this->_aResults as $aResult)
		{
			if ($aResult['Module'] === $sModule && $aResult['Method'] === $sMethod)
			{
				return array($aResult);
			}
		}
	}	
}

/**
 * @package Api
 */
class CApiModuleDecorator
{
    /**
	 *
	 * @var \AApiModule
	 */
	protected $oModule;

    /**
	 * 
	 * @param string $sModuleName
	 */
	public function __construct($sModuleName) 
	{
		$this->oModule = \CApi::GetModule($sModuleName);
    }	
	
	/**
	 * 
	 * @param string $sMethodName
	 * @param array $aArguments
	 * @return mixed
	 */
	public function __call($sMethodName, $aArguments) 
	{
		$mResult = false;
		if ($this->oModule instanceof AApiModule)
		{
			$mResult = $this->oModule->CallMethod($sMethodName, $aArguments);
		}
		return $mResult;
	}
}


/**
 * @package Api
 */
abstract class AApiModule
{
	/**
	 * @var string
	 */
	protected $sName;

	/**
	 * @var string
	 */
	protected $sPath;

	/**
	 * @var string
	 */
	protected $sVersion;
	
	/**
	 * @var array
	 */
	protected $aManagersCache = array();	

	/**
	 * @var array
	 */
	protected $aEntries = array();	

	/**
	 * @var array
	 */
	protected $aParameters;

	/**
	 * @var array
	 */
	protected $aObjects = array();	

	/**
	 * @var \CApiCapabilityManager
	 */
	public $oApiCapabilityManager = null;
	
	/**
	 * @var \MailSo\Base\Http
	 */
	public $oHttp;	
	
	/**
	 * @var array
	 */
	protected $aConfig;
	
    /**
     *
     * @var CApiSettings
     */
	protected $oModuleSettings = null;	
	
    /**
     *
     * @var array
     */
	protected $aSettingsMap = array();		
	
    /**
     *
     * @var string
     */
	public static $Delimiter = '::';
	
    /**
     *
     * @var bool
     */
	protected $bInitialized = false;
	
    /**
     *
     * @var array
     */	
	protected $aRequireModules = array();
	
	/**
	 * @param string $sVersion
	 */
	public function __construct($sName, $sPath, $sVersion = '1.0')
	{
		$this->sVersion = (string) $sVersion;

		$this->sName = $sName;
		$this->sPath = $sPath.$sName;
		$this->aParameters = array();
		$this->oApiCapabilityManager = \CApi::GetSystemManager('capability');
		$this->oHttp = \MailSo\Base\Http::SingletonInstance();
		
		$this->aEntries = array(
			'api' => 'EntryApi',
			'download' => 'EntryDownload'
		);
	}
	
	/**
	 * 
	 * @param string $sName
	 * @param string $sPath
	 * @param string $sVersion
	 * @return \AApiModule
	 */
	public static function createInstance($sName, $sPath, $sVersion = '1.0')
	{
		return new static($sName, $sPath, $sVersion);
	}	

	/**
	 * 
	 * @return boolean
	 */
	public function isInitialized()
	{
		return (bool) $this->bInitialized;
	}

	
	/**
	 * 
	 * @return boolean
	 */
	public function initialize()
	{
		$mResult = true;
		if (!$this->isInitialized())
		{
			foreach ($this->aRequireModules as $sModule)
			{
				$mResult = false;
				$oModule = \CApi::GetModule($sModule);
				if ($oModule)
				{
					if (!$oModule->isInitialized())
					{
						$mResult = $oModule->initialize();
					}
					else 
					{
						$mResult = true;
					}
				}
				if (!$mResult)
				{
					break;
				}
			}
			if ($mResult)
			{
				$this->bInitialized = true;
				$this->init();
			}
		}
		
		return $mResult;
	}
	
	/**
	 * 
	 */
	public function init() {}<commit_msg>fixed getting settings of modules<commit_after><?php
/*
 * @copyright Copyright (c) 2016, Afterlogic Corp.
 * @license AGPL-3.0
 *
 * This code is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License, version 3,
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 * 
 */

/**
 * @package Api
 */
class CApiModuleManager
{
    /**
     * This array contains a list of modules
     *
     * @var array
     */
	protected $_aModules = array();
	
    /**
     * This array contains a list of modules
     *
     * @var array
     */
	protected $_aAllowedModulesName = array();

	/**
     * This array contains a list of callbacks we should call when certain events are triggered
     *
     * @var array
     */
    protected $_aSubscriptions = array();
	
    /**
     * @var array
     */    
	protected $_aObjects = array();
	
	/**
	 * @var array
	 */
	private $_aTemplates;
	
	/**
	 * @var array
	 */
	private $_aResults;
	
	/**
	 * 
	 * @return \self
	 */
	public static function createInstance()
	{
		return new self();
	}
	
	/**
	 * 
	 * @return string
	 */
	public function init()
	{
		$sModulesPath = $this->GetModulesPath();
		
		$oCoreModule = $this->loadModule('Core', $sModulesPath);
		if ($oCoreModule !== false)
		{
			\CApi::authorise();
			
			$oCoreModule->initialize();
			$sTenant = $oCoreModule->GetTenantName();
			$aModulePath = array(
				$sModulesPath
			);
			if (!empty(trim($sTenant)))
			{
				$sTenantModulesPath = $this->GetTenantModulesPath($sTenant);
				array_unshift($aModulePath, $sTenantModulesPath);
			}
			$aModulesPath = array();
			foreach ($aModulePath as $sModulesPath)
			{
				if (@is_dir($sModulesPath))
				{
					if (false !== ($rDirHandle = @opendir($sModulesPath)))
					{
						while (false !== ($sFileItem = @readdir($rDirHandle)))
						{
							if (0 < strlen($sFileItem) && '.' !== $sFileItem{0} && preg_match('/^[a-zA-Z0-9\-]+$/', $sFileItem))
							{
								$aModulesPath[$sModulesPath][] = $sFileItem;
							}
						}

						@closedir($rDirHandle);
					}
				}
			}
			foreach ($aModulesPath as $aModulePath)
			{
				foreach ($aModulePath as $sModuleName)
				{
					$oModuleSettings = \CApi::GetModuleManager()->GetModuleSettings($sModuleName);
					if (!$oModuleSettings->GetConf('Disabled', false))
					{
						$this->_aAllowedModulesName[strtolower($sModuleName)] = $sModuleName;
						$this->loadModule($sModuleName, $sModulesPath);
					}
				}
			}
			foreach ($this->_aModules as $oModule)
			{
				if ($oModule instanceof \AApiModule)
				{
					$oModule->initialize();
				}
			}
		}
		else
		{
			echo 'Can\'t load \'Core\' Module';
			return '';
		}
	}
	
	/**
	 * 
	 * @param string $sModuleName
	 * @return boolean
	 */
	public function isModuleLoaded($sModuleName)
	{
		return array_key_exists(strtolower($sModuleName), $this->_aModules);
	}

	/**
	 * 
	 * @param string $sModuleName
	 * @param string $sConfigName
	 * @param string $sDefaultValue
	 * @return mixed
	 */
	public function getModuleConfigValue($sModuleName, $sConfigName, $sDefaultValue = null)
	{
		$mResult = $sDefaultValue;
		$oModuleConfig = $this->GetModuleConfig($sModuleName);
		if ($oModuleConfig)
		{
			$mResult = $oModuleConfig->GetConf($sConfigName, $sDefaultValue);
		}
		
		return $mResult;
	}

	/**
	 * 
	 * @param string $sModuleName
	 * @param string $sModulePath
	 * @return \AApiModule
	 */
	public function loadModule($sModuleName, $sModulePath)
	{
		$mResult = false;
		$aArgs = array($sModuleName, $sModulePath);
		$this->broadcastEvent(
			$sModuleName, 
			'loadModule' . \AApiModule::$Delimiter . 'before', 
			$aArgs
		);
		
		$sModuleFilePath = $sModulePath.$sModuleName.'/module.php';
		if (@file_exists($sModuleFilePath) && !$this->isModuleLoaded($sModuleName))
		{
		   include_once $sModuleFilePath;
		   $sModuleClassName = $sModuleName . 'Module';
		   if (class_exists($sModuleClassName))
		   {
			   $oModule = call_user_func(
					array($sModuleClassName, 'createInstance'), 
					$sModuleName, 
					$sModulePath
				);
			   if ($oModule instanceof \AApiModule)
			   {
					$this->_aModules[strtolower($sModuleName)] = $oModule;
					$mResult = $oModule;
			   }
		   }
		}

		$this->broadcastEvent(
			$sModuleName, 
			'loadModule' . \AApiModule::$Delimiter . 'after', 
			$aArgs,
			$mResult
		);
		
		return $mResult;
	}

    /**
	 * 
	 * @return array
	 */
	public function getEvents() 
	{
		return $this->_aSubscriptions;
	}	
	
    /**
     * Subscribe to an event.
     *
     * When the event is triggered, we'll call all the specified callbacks.
     * It is possible to control the order of the callbacks through the
     * priority argument.
     *
     * This is for example used to make sure that the authentication plugin
     * is triggered before anything else. If it's not needed to change this
     * number, it is recommended to ommit.
     *
     * @param string $sEvent
     * @param callback $fCallback
     * @param int $iPriority
     * @return void
     */
    public function subscribeEvent($sEvent, $fCallback, $iPriority = 100) 
	{
        if (!isset($this->_aSubscriptions[$sEvent])) 
		{
            $this->_aSubscriptions[$sEvent] = array();
        }
        while(isset($this->_aSubscriptions[$sEvent][$iPriority]))	
		{
			$iPriority++;
		}
        $this->_aSubscriptions[$sEvent][$iPriority] = $fCallback;
        ksort($this->_aSubscriptions[$sEvent]);
    }	
	
    /**
     * Broadcasts an event
     *
     * This method will call all subscribers. If one of the subscribers returns false, the process stops.
     *
     * The arguments parameter will be sent to all subscribers
     *
     * @param string $sEvent
     * @param array $aArguments
     * @param mixed $mResult
     * @return boolean
     */
    public function broadcastEvent($sModule, $sEvent, &$aArguments = array(), &$mResult = null, &$bCountinue = true) 
	{
		$bResult = false;
		$aSubscriptions = array();
		if (isset($this->_aSubscriptions[$sEvent])) 
		{
			$aSubscriptions = array_merge(
				$aSubscriptions, 
				$this->_aSubscriptions[$sEvent]
			);
        }
		$sEvent = $sModule . \AApiModule::$Delimiter . $sEvent;
		if (isset($this->_aSubscriptions[$sEvent])) 
		{
			$aSubscriptions = array_merge(
				$aSubscriptions, 
				$this->_aSubscriptions[$sEvent]
			);
		}
		
		foreach($aSubscriptions as $fCallback) 
		{
			if (is_callable($fCallback))
			{
				\CApi::Log('Execute subscription: '. $fCallback[0]->GetName() . \AApiModule::$Delimiter . $fCallback[1]);
				$mCallBackResult = call_user_func_array(
					$fCallback, 
					array(
						&$aArguments,
						&$mResult
					)
				);

				\CApi::GetModuleManager()->AddResult(
					$fCallback[0]->GetName(), 
					$sEvent, 
					$mCallBackResult
				);

				if ($mCallBackResult) 
				{
					$bResult = $mCallBackResult;
					break;
				}
			}
		}

        return $bResult;
    }	
	
	/**
	 * @param string $sParsedTemplateID
	 * @param string $sParsedPlace
	 * @param string $sTemplateFileName
	 * @param string $sModuleName
	 */
	public function includeTemplate($sParsedTemplateID, $sParsedPlace, $sTemplateFileName, $sModuleName = '')
	{
		if (!isset($this->_aTemplates[$sParsedTemplateID]))
		{
			$this->_aTemplates[$sParsedTemplateID] = array();
		}

		$this->_aTemplates[$sParsedTemplateID][] = array(
			$sParsedPlace, 
			$sTemplateFileName, 
			$sModuleName
		);
	}	
	
	/**
	 * 
	 * @param string $sTemplateID
	 * @param string $sTemplateSource
	 * @return string
	 */
	public function ParseTemplate($sTemplateID, $sTemplateSource)
	{
		if (isset($this->_aTemplates[$sTemplateID]) && is_array($this->_aTemplates[$sTemplateID]))
		{
			foreach ($this->_aTemplates[$sTemplateID] as $aItem)
			{
				if (!empty($aItem[0]) && !empty($aItem[1]) && file_exists($aItem[1]))
				{
					$sTemplateHtml = file_get_contents($aItem[1]);
					if (!empty($aItem[2]))
					{
						$sTemplateHtml = str_replace('%ModuleName%', $aItem[2], $sTemplateHtml);
						$sTemplateHtml = str_replace('%MODULENAME%', strtoupper($aItem[2]), $sTemplateHtml);
					}
					$sTemplateSource = str_replace('{%INCLUDE-START/'.$aItem[0].'/INCLUDE-END%}',
						$sTemplateHtml.'{%INCLUDE-START/'.$aItem[0].'/INCLUDE-END%}', $sTemplateSource);
				}
			}
		}

		return $sTemplateSource;
	}	
	
	public function setObjectMap()
	{
	}

	/**
	 * @return string
	 */
	public function GetModulesPath()
	{
		return AURORA_APP_ROOT_PATH.'modules/';
	}

	/**
	 * @return string
	 */
	public function GetTenantModulesPath($sTenant)
	{
		return AURORA_APP_ROOT_PATH.'tenants/' . $sTenant . '/modules/';
	}

	/**
	 * @return array
	 */
	public function GetAllowedModulesName()
	{
		return $this->_aAllowedModulesName;
	}

	/**
	 * @return array
	 */
	public function GetModules()
	{
		return $this->_aModules;
	}
	
	public function GetModuleSettings($sModuleName)
	{
		if (!isset($this->aModulesSettings[strtolower($sModuleName)]))
		{
			$this->aModulesSettings[strtolower($sModuleName)] = new \CApiModuleSettings($sModuleName);
		}
		
		return $this->aModulesSettings[strtolower($sModuleName)];
	}
	
	/**
	 * @param string $sModuleName
	 * @return AApiModule
	 */
	public function GetModule($sModuleName)
	{
		$sModuleName = strtolower($sModuleName);
		return (isset($this->_aModules[$sModuleName]) &&  $this->_aModules[$sModuleName] instanceof \AApiModule) ? $this->_aModules[$sModuleName] : false;
	}
	
	
	/**
	 * @return \AApiModule
	 */
	public function GetModuleFromRequest()
	{
		$sModule = '';
		$oHttp = \MailSo\Base\Http::SingletonInstance();
		if ($oHttp->IsPost()) 
		{
			$sModule = $oHttp->GetPost('Module', null);
		} 
		else 
		{
			$aPath = \System\Service::GetPaths();
			$sModule = (isset($aPath[1])) ? $aPath[1] : '';
		}
		return $this->GetModule($sModule);
	}
	
	/**
	 * 
	 * @param string $sEntryName
	 * @return array
	 */
	public function GetModulesByEntry($sEntryName)
	{
		$aModules = array();
		$oResult = $this->GetModuleFromRequest();
		
		if ($oResult && !$oResult->HasEntry($sEntryName)) 
		{
			$oResult = false;
		}
		if ($oResult === false) 
		{
			foreach ($this->_aModules as $oModule) 
			{
				if ($oModule instanceof AApiModule && $oModule->HasEntry($sEntryName)) 
				{
					$aModules[] = $oModule;
				}
			}
		}
		else
		{
			$aModules = array(
				$oResult
			);
		}
		
		return $aModules;
	}
	
	/**
	 * @param string $sModuleName
	 * @return bool
	 */
	public function ModuleExists($sModuleName)
	{
		return ($this->GetModule($sModuleName)) ? true  : false;
	}
	
	/**
	 * 
	 * @param string $sEntryName
	 * @return mixed
	 */
	public function RunEntry($sEntryName)
	{
		$mResult = false;
		$oModule = $this->GetModuleFromRequest();
		if ($oModule instanceof \AApiModule && $oModule->HasEntry($sEntryName)) 
		{
			$mResult = $oModule->RunEntry($sEntryName);
		}
		
		return $mResult;
	}

	/**
	 * @return string
	 */
	public function Hash()
	{
		$sResult = md5(CApi::Version());
		foreach ($this->_aModules as $oModule) {
			
			$sResult = md5($sResult.$oModule->GetPath().$oModule->GetName().$oModule->GetHash());
		}

		return $sResult;
	}
	
	/**
	 * 
	 * @param string $sModule
	 * @param string $sMethod
	 * @param mixed $mResult
	 */
	public function AddResult($sModule, $sMethod, &$mResult, $iErrorCode = 0)
	{
		$aResult = array(
			'Module' => $sModule,
			'Method' => $sMethod,
			'Result' => $mResult
		);
		
		if ($iErrorCode > 0)
		{
			$aResult['ErrorCode'] = $iErrorCode;
		}
		
		$this->_aResults[] = $aResult;
	}
	
	public function GetResults()
	{
		return $this->_aResults;
	}
	
	public function GetResult($sModule, $sMethod)
	{
		foreach($this->_aResults as $aResult)
		{
			if ($aResult['Module'] === $sModule && $aResult['Method'] === $sMethod)
			{
				return array($aResult);
			}
		}
	}	
}

/**
 * @package Api
 */
class CApiModuleDecorator
{
    /**
	 *
	 * @var \AApiModule
	 */
	protected $oModule;

    /**
	 * 
	 * @param string $sModuleName
	 */
	public function __construct($sModuleName) 
	{
		$this->oModule = \CApi::GetModule($sModuleName);
    }	
	
	/**
	 * 
	 * @param string $sMethodName
	 * @param array $aArguments
	 * @return mixed
	 */
	public function __call($sMethodName, $aArguments) 
	{
		$mResult = false;
		if ($this->oModule instanceof AApiModule)
		{
			$mResult = $this->oModule->CallMethod($sMethodName, $aArguments);
		}
		return $mResult;
	}
}


/**
 * @package Api
 */
abstract class AApiModule
{
	/**
	 * @var string
	 */
	protected $sName;

	/**
	 * @var string
	 */
	protected $sPath;

	/**
	 * @var string
	 */
	protected $sVersion;
	
	/**
	 * @var array
	 */
	protected $aManagersCache = array();	

	/**
	 * @var array
	 */
	protected $aEntries = array();	

	/**
	 * @var array
	 */
	protected $aParameters;

	/**
	 * @var array
	 */
	protected $aObjects = array();	

	/**
	 * @var \CApiCapabilityManager
	 */
	public $oApiCapabilityManager = null;
	
	/**
	 * @var \MailSo\Base\Http
	 */
	public $oHttp;	
	
	/**
	 * @var array
	 */
	protected $aConfig;
	
    /**
     *
     * @var CApiSettings
     */
	protected $oModuleSettings = null;	
	
    /**
     *
     * @var array
     */
	protected $aSettingsMap = array();		
	
    /**
     *
     * @var string
     */
	public static $Delimiter = '::';
	
    /**
     *
     * @var bool
     */
	protected $bInitialized = false;
	
    /**
     *
     * @var array
     */	
	protected $aRequireModules = array();
	
	/**
	 * @param string $sVersion
	 */
	public function __construct($sName, $sPath, $sVersion = '1.0')
	{
		$this->sVersion = (string) $sVersion;

		$this->sName = $sName;
		$this->sPath = $sPath.$sName;
		$this->aParameters = array();
		$this->oApiCapabilityManager = \CApi::GetSystemManager('capability');
		$this->oHttp = \MailSo\Base\Http::SingletonInstance();
		
		$this->aEntries = array(
			'api' => 'EntryApi',
			'download' => 'EntryDownload'
		);
	}
	
	/**
	 * 
	 * @param string $sName
	 * @param string $sPath
	 * @param string $sVersion
	 * @return \AApiModule
	 */
	public static function createInstance($sName, $sPath, $sVersion = '1.0')
	{
		return new static($sName, $sPath, $sVersion);
	}	

	/**
	 * 
	 * @return boolean
	 */
	public function isInitialized()
	{
		return (bool) $this->bInitialized;
	}

	
	/**
	 * 
	 * @return boolean
	 */
	public function initialize()
	{
		$mResult = true;
		if (!$this->isInitialized())
		{
			foreach ($this->aRequireModules as $sModule)
			{
				$mResult = false;
				$oModule = \CApi::GetModule($sModule);
				if ($oModule)
				{
					if (!$oModule->isInitialized())
					{
						$mResult = $oModule->initialize();
					}
					else 
					{
						$mResult = true;
					}
				}
				if (!$mResult)
				{
					break;
				}
			}
			if ($mResult)
			{
				$this->loadModuleSettings();
				$this->bInitialized = true;
				$this->init();
			}
		}
		
		return $mResult;
	}
	
	/**
	 * 
	 */
	public function init() {}
{'fraction_non_alphanumeric': 0.15604938271604937, 'fraction_numerical': 0.002436213991769547, 'mean_word_length': 2.887879175732753, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 180, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1257329', 'n_tokens_mistral': 12143, 'n_tokens_neox': 10337, 'n_words': 2077}