/home/ivoiecob/email.hirewise-va.com/system/Module/Manager.php
<?php
/**
* This code is licensed under AGPLv3 license or Afterlogic Software License
* if commercial version of the product was purchased.
* For full statements of the licenses see LICENSE-AFTERLOGIC and LICENSE-AGPL3 files.
*/
namespace Aurora\System\Module;
use Aurora\Modules\Core\Models\User;
use Aurora\System\Exceptions\ApiException;
use Aurora\System\Managers\Response;
/**
* @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
* @license https://afterlogic.com/products/common-licensing Afterlogic Software License
* @copyright Copyright (c) 2019, Afterlogic Corp.
*
* @package Api
*/
class Manager
{
/**
* This array contains a list of modules
*
* @var array
*/
protected $_aModules = array();
/**
* This array contains a list of modules paths
*
* @var array
*/
protected $_aModulesPaths = null;
/**
* This array contains a list of modules
*
* @var array
*/
protected $_aAllowedModulesName = array(
'core' => 'Core'
);
/**
* @var array
*/
private $_aTemplates;
/**
* @var array
*/
private $_aResults;
/**
* @var \Aurora\System\EventEmitter
*/
private $oEventEmitter;
/**
* @var \Aurora\System\ObjectExtender
*/
private $oObjectExtender;
/**
* @var \Aurora\System\Exceptions\Exception
*/
private $oLastException;
/**
* @var array
*/
private $aModulesSettings;
/**
*
*/
public function __construct()
{
$this->oEventEmitter = \Aurora\System\EventEmitter::getInstance();
$this->oObjectExtender = \Aurora\System\ObjectExtender::getInstance();
}
/**
*
* @return self
*/
public static function createInstance()
{
return new self();
}
/**
*
* @return void
*/
public function loadModules()
{
$oUser = \Aurora\System\Api::authorise();
$oCoreModule = $this->loadModule('Core');
if ($oCoreModule instanceof AbstractModule) {
$oTenant = null;
if ($oUser instanceof User && $oUser->Role !== \Aurora\System\Enums\UserRole::SuperAdmin) {
$oTenant = \Aurora\System\Api::getTenantById($oUser->IdTenant);
}
foreach ($this->GetModulesPaths() as $sModuleName => $sModulePath) {
$bIsModuleDisabledForTenant = $this->isModuleDisabledForObject($oTenant, $sModuleName);
$bIsModuleDisabledForUser = $this->isModuleDisabledForObject($oUser, $sModuleName);
$bModuleIsDisabled = $this->getModuleConfigValue($sModuleName, 'Disabled', false);
if (!($bIsModuleDisabledForUser || $bIsModuleDisabledForTenant) && !$bModuleIsDisabled) {
$oLoadedModule = $this->loadModule($sModuleName, $sModulePath);
$bClientModule = $this->isClientModule($sModuleName);
if ($oLoadedModule instanceof AbstractModule || $bClientModule) {
$this->_aAllowedModulesName[\strtolower($sModuleName)] = $sModuleName;
} else {
// \Aurora\System\Api::Log('Module ' . $sModuleName . ' is not allowed. $bModuleLoaded = ' . $oLoadedModule . '. $bClientModule = ' . $bClientModule . '.');
}
} else {
$this->FlushModuleSettings($sModuleName);
// \Aurora\System\Api::Log('Module ' . $sModuleName . ' is not allowed. $bIsModuleDisabledForUser = ' . $bIsModuleDisabledForUser . '. $bModuleIsDisabled = ' . $bModuleIsDisabled . '.');
}
}
} else {
echo "Can't load 'Core' Module";
exit;
}
}
/**
*
*/
protected function isModuleDisabledForObject($oObject, $sModuleName)
{
return ($oObject instanceof \Aurora\System\Classes\Model) ? $oObject->isModuleDisabled($sModuleName) : false;
}
/**
*
* @param string $sModuleName
* @return boolean
*/
protected function isClientModule($sModuleName)
{
$sModulePath = $this->GetModulePath($sModuleName);
return \file_exists($sModulePath . $sModuleName . '/js/manager.js') || \file_exists($sModulePath . $sModuleName . '/vue/manager.js');
}
/**
*
* @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->getModuleSettings($sModuleName);
if ($oModuleConfig) {
$mResult = $oModuleConfig->GetValue($sConfigName, $sDefaultValue);
}
return $mResult;
}
/**
*
* @param string $sModuleName
* @param string $sConfigName
* @param string $sValue
* @return mixed
*/
public function setModuleConfigValue($sModuleName, $sConfigName, $sValue)
{
$oModuleConfig = $this->getModuleSettings($sModuleName);
if ($oModuleConfig) {
$oModuleConfig->SetValue($sConfigName, $sValue);
}
}
/**
*
* @param string $sModuleName
* @return mixed
*/
public function saveModuleConfigValue($sModuleName)
{
$oModuleConfig = $this->getModuleSettings($sModuleName);
if ($oModuleConfig) {
$oModuleConfig->Save();
}
}
/**
*
*/
public function SyncModulesConfigs()
{
$sConfigFilename = 'pre-config.json';
$sConfigPath = AU_APP_ROOT_PATH . $sConfigFilename;
$aModulesPreconfig = [];
//getting modules pre-configuration data
if (file_exists($sConfigPath)) {
$sPreConfig = file_get_contents($sConfigPath);
$aPreConfig = json_decode($sPreConfig, true);
if (is_array($aPreConfig) && isset($aPreConfig['modules'])) {
$aModulesPreconfig = $aPreConfig['modules'];
}
}
foreach ($this->GetModulesPaths() as $sModuleName => $sModulePath) {
if (!empty($sModuleName)) {
$oSettings = $this->getModuleSettings($sModuleName);
if ($oSettings instanceof Settings) {
$oSettings->Load();
//overriding modules default configuration with pre-configuration data
if (isset($aModulesPreconfig[$sModuleName])) {
$aModulePreconfig = $aModulesPreconfig[$sModuleName];
foreach ($aModulePreconfig as $key => $val) {
$oProp = $oSettings->GetSettingsProperty($key);
if ($oProp && $oProp->IsDefault) {
if (!empty($oProp->SpecType)) {
$val = \Aurora\System\Enums\EnumConvert::FromXml($val, $oProp->SpecType);
}
$oSettings->SetValue($key, $val);
}
}
}
$oSettings->Save();
}
}
}
}
/**
*
* @param string $sModuleName
* @return \Aurora\System\Module\AbstractModule
*/
protected function loadModule($sModuleName, $sModulePath = null)
{
$mResult = false;
if (!isset($sModulePath)) {
$sModulePath = $this->GetModulePath($sModuleName);
}
if ($sModulePath) {
if (!$this->isModuleLoaded($sModuleName)) {
$aArgs = array($sModuleName, $sModulePath);
$this->broadcastEvent(
$sModuleName,
'loadModule' . AbstractModule::$Delimiter . 'before',
$aArgs
);
if (@\file_exists($sModulePath . $sModuleName . '/Module.php')) {
$sModuleClassName = '\\Aurora\\Modules\\' . $sModuleName . '\\Module';
$oModule = new $sModuleClassName($sModulePath);
if ($oModule instanceof AbstractModule) {
foreach ($oModule->GetRequireModules() as $sModule) {
if (!$this->loadModule($sModule, $sModulePath)) {
break;
}
}
if ($oModule->initialize() && $oModule->isValid()) {
$this->_aModules[\strtolower($sModuleName)] = $oModule;
$mResult = $oModule;
}
}
}
$this->broadcastEvent(
$sModuleName,
'loadModule' . AbstractModule::$Delimiter . 'after',
$aArgs,
$mResult
);
} else {
$mResult = $this->GetModule($sModuleName);
}
}
return $mResult;
}
/**
* @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;
}
/**
*
* @param string $sModule
* @param string $sType
* @param array $aMap
*/
public function extendObject($sModule, $sType, $aMap)
{
$this->oObjectExtender->extend($sModule, $sType, $aMap);
}
/**
*
* @param string $sType
* @return array
*/
public function getExtendedObject($sType)
{
return $this->oObjectExtender->getObject($sType);
}
/**
*
* @param string $sType
* @return boolean
*/
public function issetObject($sType)
{
return $this->oObjectExtender->issetObject($sType);
}
/**
* @todo return correct path according to curent tenant
*
* @return string
*/
public function GetModulesRootPath()
{
return AU_APP_ROOT_PATH . 'modules/';
}
/**
* @todo return correct path according to curent tenant
*
* @return array
*/
public function GetModulesPaths()
{
if (!isset($this->_aModulesPaths)) {
$sModulesPath = $this->GetModulesRootPath();
$aModulePath = [
$sModulesPath
];
$oCoreModule = $this->loadModule('Core', $sModulesPath);
if ($oCoreModule instanceof \Aurora\Modules\Core\Module) {
$sTenant = \trim($oCoreModule->GetTenantName());
if (!empty($sTenant)) {
$sTenantModulesPath = $this->GetTenantModulesPath($sTenant);
\array_unshift($aModulePath, $sTenantModulesPath);
}
}
$this->_aModulesPaths = [];
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)) {
$this->_aModulesPaths[$sFileItem] = $sModulesPath;
}
}
@\closedir($rDirHandle);
}
}
}
}
return $this->_aModulesPaths;
}
/**
* @todo return correct path according to curent tenant
*
* @return string
*/
public function GetModulePath($sModuleName)
{
$aModulesPaths = $this->GetModulesPaths();
return isset($aModulesPaths[$sModuleName]) ? $aModulesPaths[$sModuleName] : false;
}
/**
* @todo return correct path according to curent tenant
*
* @return string
*/
public function GetModulesSettingsPath()
{
return \Aurora\System\Api::DataPath() . '/settings/modules/';
}
/**
* @return string
*/
public function GetTenantModulesPath($sTenant)
{
return AU_APP_ROOT_PATH . 'tenants/' . $sTenant . '/modules/';
}
/**
* @return array
*/
public function GetAllowedModulesName()
{
$aArgs = [];
$mResult = $this->_aAllowedModulesName;
$this->broadcastEvent('System', 'GetAllowedModulesName', $aArgs, $mResult, true);
return $mResult;
}
/**
* @param string $sModuleName
* @return array
*/
public function IsAllowedModule($sModuleName)
{
$aArgs = [
'ModuleName' => $sModuleName
];
$mResult = array_key_exists(\strtolower($sModuleName), $this->_aAllowedModulesName);
$this->broadcastEvent('System', 'IsAllowedModule', $aArgs, $mResult, true);
return $mResult;
}
/**
* @return array
*/
public function GetModules()
{
return $this->_aModules;
}
/**
* @param string $sModuleName
* @return \Aurora\System\Module\Settings
*/
public function &getModuleSettings($sModuleName)
{
if (!isset($this->aModulesSettings[strtolower($sModuleName)])) {
$sSettingsClassName = '\\Aurora\\Modules\\' . $sModuleName . '\\Settings';
if (class_exists($sSettingsClassName)) {
$this->aModulesSettings[strtolower($sModuleName)] = new $sSettingsClassName($sModuleName);
} else {
$this->aModulesSettings[strtolower($sModuleName)] = new Settings($sModuleName);
}
}
return $this->aModulesSettings[strtolower($sModuleName)];
}
/**
* @param string $sModuleName
* @return void
*/
public function FlushModuleSettings($sModuleName)
{
if (isset($this->aModulesSettings[strtolower($sModuleName)])) {
unset($this->aModulesSettings[strtolower($sModuleName)]);
}
}
/**
* @param string $sModuleName
* @return \Aurora\System\Module\AbstractModule
*/
public function GetModule($sModuleName)
{
$mResult = false;
$sModuleNameLower = strtolower($sModuleName);
if ($this->isModuleLoaded($sModuleName)) {
$mResult = $this->_aModules[$sModuleNameLower];
}
return $mResult;
}
/**
* @return \Aurora\System\Module\AbstractModule
*/
public function GetModuleFromRequest()
{
$sModule = '';
$oHttp = \MailSo\Base\Http::SingletonInstance();
if ($oHttp->IsPost()) {
$sModule = $oHttp->GetPost('Module', null);
}
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 AbstractModule && $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)
{
$oHttp = \MailSo\Base\Http::SingletonInstance();
$aArguments = [
'EntryName' => $sEntryName,
'Module' => $oHttp->GetPost('Module', null),
'Method' => $oHttp->GetPost('Method', null),
'Parameters' => \json_decode($oHttp->GetPost('Parameters', ''), true)
];
$mResult = false;
try {
$bEventResult = $this->broadcastEvent('System', 'RunEntry' . AbstractModule::$Delimiter . 'before', $aArguments, $mResult);
if ($bEventResult !== true) {
if (!\Aurora\System\Router::getInstance()->hasRoute($sEntryName)) {
$sEntryName = 'default';
}
$mResult = \Aurora\System\Router::getInstance()->route(
$sEntryName
);
}
} catch(\Exception $oException) {
$mResult = \Aurora\System\Managers\Response::GetJsonFromObject(
'Json',
\Aurora\System\Managers\Response::ExceptionResponse("System", $oException)
);
\Aurora\System\Api::LogException($oException);
} finally {
$this->broadcastEvent('System', 'RunEntry' . AbstractModule::$Delimiter . 'after', $aArguments, $mResult);
}
return $mResult;
}
/**
* @return string
*/
public function GetModulesHash()
{
$sResult = md5(\Aurora\System\Api::Version());
$aModuleNames = $this->GetAllowedModulesName();
foreach ($aModuleNames as $sModuleName) {
$sResult = md5($sResult . $this->GetModuleHashByName($sModuleName));
}
return $sResult;
}
/**
* @toto need to add module version to information string
* @param string $sModuleName
*
* @return string
*/
public function GetModuleHashByName($sModuleName)
{
$sResult = '';
$sTenantName = \Aurora\System\Api::getTenantName();
$sResult .= $sTenantName !== 'Default' ? $this->GetModulesRootPath() : $this->GetTenantModulesPath($sTenantName);
$sResult .= $sModuleName;
return md5($sResult);
}
/**
* @param string $oExcetpion
*/
public function SetLastException($oExcetpion)
{
$this->oLastException = $oExcetpion;
}
/**
*
*/
public function GetLastException()
{
return $this->oLastException;
}
/**
*
* @param string $sModule
* @param string $sMethod
* @param mixed $mResult
*/
public function AddResult($sModule, $sMethod, $aParameters, $mResult, $iErrorCode = 0)
{
if (is_string($mResult)) {
$mResult = \str_replace(\Aurora\System\Api::$aSecretWords, '*******', $mResult);
}
$aMapParameters = array();
if (is_array($aParameters)) {
foreach ($aParameters as $sKey => $mParameter) {
if (!is_resource($mParameter) && gettype($mParameter) !== 'unknown type') {
$aMapParameters[$sKey] = $mParameter;
}
}
}
$aResult = array(
'Module' => $sModule,
'Method' => $sMethod,
'Parameters' => $aMapParameters,
'Result' => $mResult
);
if ($iErrorCode > 0) {
$aResult['ErrorCode'] = $iErrorCode;
}
$this->_aResults[] = $aResult;
}
/**
* @return array
*/
public function GetResults()
{
return $this->_aResults;
}
/**
* @param string $sModule
* @param string $sMethod
* @return array
*/
public function GetResult($sModule, $sMethod)
{
foreach ($this->_aResults as $aResult) {
if ($aResult['Module'] === $sModule && $aResult['Method'] === $sMethod) {
return [$aResult];
}
}
return [];
}
/**
* 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 = [], &$mResult = null, $bSkipIsAllowedModuleCheck = false)
{
return $this->oEventEmitter->emit(
$sModule,
$sEvent,
$aArguments,
$mResult,
function ($sModule, $aArguments, $mResult) use ($sEvent) {
$this->AddResult($sModule, $sEvent, $aArguments, $mResult);
},
$bSkipIsAllowedModuleCheck
);
}
/**
* 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 callable $fCallback
* @param int $iPriority
* @return void
*/
public function subscribeEvent($sEvent, $fCallback, $iPriority = 100)
{
$this->oEventEmitter->on($sEvent, $fCallback, $iPriority);
}
public function getEvents()
{
return $this->oEventEmitter->getListeners();
}
public function GetSubscriptionsResult()
{
return $this->oEventEmitter->getListenersResult();
}
}