1: | <?php |
2: | |
3: | |
4: | |
5: | |
6: | |
7: | |
8: | namespace Aurora\System\Module; |
9: | |
10: | use Aurora\Modules\Core\Models\User; |
11: | use Aurora\System\Exceptions\ApiException; |
12: | use Aurora\System\Managers\Response; |
13: | |
14: | |
15: | |
16: | |
17: | |
18: | |
19: | |
20: | |
21: | class Manager |
22: | { |
23: | |
24: | |
25: | |
26: | |
27: | |
28: | protected $_aModules = array(); |
29: | |
30: | |
31: | |
32: | |
33: | |
34: | |
35: | protected $_aModulesPaths = null; |
36: | |
37: | |
38: | |
39: | |
40: | |
41: | |
42: | protected $_aAllowedModulesName = array( |
43: | 'core' => 'Core' |
44: | ); |
45: | |
46: | |
47: | |
48: | |
49: | private $_aTemplates; |
50: | |
51: | |
52: | |
53: | |
54: | private $_aResults; |
55: | |
56: | |
57: | |
58: | |
59: | private $oEventEmitter; |
60: | |
61: | |
62: | |
63: | |
64: | private $oObjectExtender; |
65: | |
66: | |
67: | |
68: | |
69: | private $oLastException; |
70: | |
71: | |
72: | |
73: | |
74: | private $aModulesSettings; |
75: | |
76: | |
77: | |
78: | |
79: | public function __construct() |
80: | { |
81: | $this->oEventEmitter = \Aurora\System\EventEmitter::getInstance(); |
82: | $this->oObjectExtender = \Aurora\System\ObjectExtender::getInstance(); |
83: | } |
84: | |
85: | |
86: | |
87: | |
88: | |
89: | public static function createInstance() |
90: | { |
91: | return new self(); |
92: | } |
93: | |
94: | |
95: | |
96: | |
97: | |
98: | public function loadModules() |
99: | { |
100: | $oUser = \Aurora\System\Api::authorise(); |
101: | $oCoreModule = $this->loadModule('Core'); |
102: | |
103: | if ($oCoreModule instanceof AbstractModule) { |
104: | $oTenant = null; |
105: | if ($oUser instanceof User && $oUser->Role !== \Aurora\System\Enums\UserRole::SuperAdmin) { |
106: | $oTenant = \Aurora\Modules\Core\Module::Decorator()->GetTenantWithoutRoleCheck($oUser->IdTenant); |
107: | } |
108: | foreach ($this->GetModulesPaths() as $sModuleName => $sModulePath) { |
109: | $bIsModuleDisabledForTenant = \Aurora\Modules\Core\Module::Decorator()->IsModuleDisabledForObject($oTenant, $sModuleName); |
110: | $bIsModuleDisabledForUser = \Aurora\Modules\Core\Module::Decorator()->IsModuleDisabledForObject($oUser, $sModuleName); |
111: | $bModuleIsDisabled = $this->getModuleConfigValue($sModuleName, 'Disabled', false); |
112: | if (!($bIsModuleDisabledForUser || $bIsModuleDisabledForTenant) && !$bModuleIsDisabled) { |
113: | $oLoadedModule = $this->loadModule($sModuleName, $sModulePath); |
114: | $bClientModule = $this->isClientModule($sModuleName); |
115: | if ($oLoadedModule instanceof AbstractModule || $bClientModule) { |
116: | $this->_aAllowedModulesName[\strtolower($sModuleName)] = $sModuleName; |
117: | } else { |
118: | |
119: | } |
120: | } else { |
121: | $this->FlushModuleSettings($sModuleName); |
122: | |
123: | } |
124: | } |
125: | } else { |
126: | echo "Can't load 'Core' Module"; |
127: | exit; |
128: | } |
129: | } |
130: | |
131: | |
132: | |
133: | |
134: | |
135: | |
136: | protected function isClientModule($sModuleName) |
137: | { |
138: | $sModulePath = $this->GetModulePath($sModuleName); |
139: | return \file_exists($sModulePath . $sModuleName . '/js/manager.js') || \file_exists($sModulePath . $sModuleName . '/vue/manager.js'); |
140: | } |
141: | |
142: | |
143: | |
144: | |
145: | |
146: | |
147: | public function isModuleLoaded($sModuleName) |
148: | { |
149: | return \array_key_exists(\strtolower($sModuleName), $this->_aModules); |
150: | } |
151: | |
152: | |
153: | |
154: | |
155: | |
156: | |
157: | |
158: | |
159: | public function getModuleConfigValue($sModuleName, $sConfigName, $sDefaultValue = null) |
160: | { |
161: | $mResult = $sDefaultValue; |
162: | $oModuleConfig = $this->getModuleSettings($sModuleName); |
163: | |
164: | if ($oModuleConfig) { |
165: | $mResult = $oModuleConfig->GetValue($sConfigName, $sDefaultValue); |
166: | } |
167: | |
168: | return $mResult; |
169: | } |
170: | |
171: | |
172: | |
173: | |
174: | |
175: | |
176: | |
177: | |
178: | public function setModuleConfigValue($sModuleName, $sConfigName, $sValue) |
179: | { |
180: | $oModuleConfig = $this->getModuleSettings($sModuleName); |
181: | if ($oModuleConfig) { |
182: | $oModuleConfig->SetValue($sConfigName, $sValue); |
183: | } |
184: | } |
185: | |
186: | |
187: | |
188: | |
189: | |
190: | |
191: | public function saveModuleConfigValue($sModuleName) |
192: | { |
193: | $oModuleConfig = $this->getModuleSettings($sModuleName); |
194: | if ($oModuleConfig) { |
195: | $oModuleConfig->Save(); |
196: | } |
197: | } |
198: | |
199: | |
200: | |
201: | |
202: | public function SyncModulesConfigs() |
203: | { |
204: | $sConfigFilename = 'pre-config.json'; |
205: | $sConfigPath = AU_APP_ROOT_PATH . $sConfigFilename; |
206: | $aModulesPreconfig = []; |
207: | |
208: | if (file_exists($sConfigPath)) { |
209: | $sPreConfig = file_get_contents($sConfigPath); |
210: | |
211: | $aPreConfig = json_decode($sPreConfig, true); |
212: | |
213: | if (is_array($aPreConfig) && isset($aPreConfig['modules'])) { |
214: | $aModulesPreconfig = $aPreConfig['modules']; |
215: | } |
216: | } |
217: | |
218: | foreach ($this->GetModulesPaths() as $sModuleName => $sModulePath) { |
219: | if (!empty($sModuleName)) { |
220: | $oSettings = $this->getModuleSettings($sModuleName); |
221: | if ($oSettings instanceof Settings) { |
222: | $oSettings->Load(); |
223: | |
224: | if (isset($aModulesPreconfig[$sModuleName])) { |
225: | $aModulePreconfig = $aModulesPreconfig[$sModuleName]; |
226: | foreach ($aModulePreconfig as $key => $val) { |
227: | $oProp = $oSettings->GetSettingsProperty($key); |
228: | if ($oProp && $oProp->IsDefault) { |
229: | if (!empty($oProp->SpecType)) { |
230: | $val = \Aurora\System\Enums\EnumConvert::FromXml($val, $oProp->SpecType); |
231: | } |
232: | $oSettings->SetValue($key, $val); |
233: | } |
234: | } |
235: | } |
236: | $oSettings->Save(); |
237: | } |
238: | } |
239: | } |
240: | } |
241: | |
242: | |
243: | |
244: | |
245: | |
246: | |
247: | protected function loadModule($sModuleName, $sModulePath = null) |
248: | { |
249: | $mResult = false; |
250: | if (!isset($sModulePath)) { |
251: | $sModulePath = $this->GetModulePath($sModuleName); |
252: | } |
253: | |
254: | if ($sModulePath) { |
255: | if (!$this->isModuleLoaded($sModuleName)) { |
256: | $aArgs = array($sModuleName, $sModulePath); |
257: | |
258: | $this->broadcastEvent( |
259: | $sModuleName, |
260: | 'loadModule' . AbstractModule::$Delimiter . 'before', |
261: | $aArgs |
262: | ); |
263: | |
264: | if (@\file_exists($sModulePath . $sModuleName . '/Module.php')) { |
265: | $sModuleClassName = '\\Aurora\\Modules\\' . $sModuleName . '\\Module'; |
266: | $oModule = new $sModuleClassName($sModulePath); |
267: | if ($oModule instanceof AbstractModule) { |
268: | foreach ($oModule->GetRequireModules() as $sModule) { |
269: | if (!$this->loadModule($sModule, $sModulePath)) { |
270: | break; |
271: | } |
272: | } |
273: | |
274: | if ($oModule->initialize() && $oModule->isValid()) { |
275: | $this->_aModules[\strtolower($sModuleName)] = $oModule; |
276: | $mResult = $oModule; |
277: | } |
278: | } |
279: | } |
280: | |
281: | $this->broadcastEvent( |
282: | $sModuleName, |
283: | 'loadModule' . AbstractModule::$Delimiter . 'after', |
284: | $aArgs, |
285: | $mResult |
286: | ); |
287: | } else { |
288: | $mResult = $this->GetModule($sModuleName); |
289: | } |
290: | } |
291: | return $mResult; |
292: | } |
293: | |
294: | |
295: | |
296: | |
297: | |
298: | |
299: | |
300: | public function includeTemplate($sParsedTemplateID, $sParsedPlace, $sTemplateFileName, $sModuleName = '') |
301: | { |
302: | if (!isset($this->_aTemplates[$sParsedTemplateID])) { |
303: | $this->_aTemplates[$sParsedTemplateID] = array(); |
304: | } |
305: | |
306: | $this->_aTemplates[$sParsedTemplateID][] = array( |
307: | $sParsedPlace, |
308: | $sTemplateFileName, |
309: | $sModuleName |
310: | ); |
311: | } |
312: | |
313: | |
314: | |
315: | |
316: | |
317: | |
318: | |
319: | public function ParseTemplate($sTemplateID, $sTemplateSource) |
320: | { |
321: | if (isset($this->_aTemplates[$sTemplateID]) && \is_array($this->_aTemplates[$sTemplateID])) { |
322: | foreach ($this->_aTemplates[$sTemplateID] as $aItem) { |
323: | if (!empty($aItem[0]) && !empty($aItem[1]) && \file_exists($aItem[1])) { |
324: | $sTemplateHtml = \file_get_contents($aItem[1]); |
325: | if (!empty($aItem[2])) { |
326: | $sTemplateHtml = \str_replace('%ModuleName%', $aItem[2], $sTemplateHtml); |
327: | $sTemplateHtml = \str_replace('%MODULENAME%', \strtoupper($aItem[2]), $sTemplateHtml); |
328: | } |
329: | $sTemplateSource = \str_replace( |
330: | '{%INCLUDE-START/' . $aItem[0] . '/INCLUDE-END%}', |
331: | $sTemplateHtml . '{%INCLUDE-START/' . $aItem[0] . '/INCLUDE-END%}', |
332: | $sTemplateSource |
333: | ); |
334: | } |
335: | } |
336: | } |
337: | |
338: | return $sTemplateSource; |
339: | } |
340: | |
341: | |
342: | |
343: | |
344: | |
345: | |
346: | |
347: | public function extendObject($sModule, $sType, $aMap) |
348: | { |
349: | $this->oObjectExtender->extend($sModule, $sType, $aMap); |
350: | } |
351: | |
352: | |
353: | |
354: | |
355: | |
356: | |
357: | public function getExtendedObject($sType) |
358: | { |
359: | return $this->oObjectExtender->getObject($sType); |
360: | } |
361: | |
362: | |
363: | |
364: | |
365: | |
366: | |
367: | public function issetObject($sType) |
368: | { |
369: | return $this->oObjectExtender->issetObject($sType); |
370: | } |
371: | |
372: | |
373: | |
374: | |
375: | |
376: | |
377: | public function GetModulesRootPath() |
378: | { |
379: | return AU_APP_ROOT_PATH . 'modules/'; |
380: | } |
381: | |
382: | |
383: | |
384: | |
385: | |
386: | |
387: | public function GetModulesPaths() |
388: | { |
389: | if (!isset($this->_aModulesPaths)) { |
390: | $sModulesPath = $this->GetModulesRootPath(); |
391: | $aModulePath = [ |
392: | $sModulesPath |
393: | ]; |
394: | $oCoreModule = $this->loadModule('Core', $sModulesPath); |
395: | if ($oCoreModule instanceof \Aurora\Modules\Core\Module) { |
396: | $sTenant = \trim($oCoreModule->GetTenantName()); |
397: | if (!empty($sTenant)) { |
398: | $sTenantModulesPath = $this->GetTenantModulesPath($sTenant); |
399: | \array_unshift($aModulePath, $sTenantModulesPath); |
400: | } |
401: | } |
402: | $this->_aModulesPaths = []; |
403: | foreach ($aModulePath as $sModulesPath) { |
404: | if (@\is_dir($sModulesPath)) { |
405: | if (false !== ($rDirHandle = @\opendir($sModulesPath))) { |
406: | while (false !== ($sFileItem = @\readdir($rDirHandle))) { |
407: | if (0 < \strlen($sFileItem) && '.' !== $sFileItem[0] && \preg_match('/^[a-zA-Z0-9\-]+$/', $sFileItem)) { |
408: | $this->_aModulesPaths[$sFileItem] = $sModulesPath; |
409: | } |
410: | } |
411: | |
412: | @\closedir($rDirHandle); |
413: | } |
414: | } |
415: | } |
416: | } |
417: | |
418: | return $this->_aModulesPaths; |
419: | } |
420: | |
421: | |
422: | |
423: | |
424: | |
425: | |
426: | public function GetModulePath($sModuleName) |
427: | { |
428: | $aModulesPaths = $this->GetModulesPaths(); |
429: | return isset($aModulesPaths[$sModuleName]) ? $aModulesPaths[$sModuleName] : false; |
430: | } |
431: | |
432: | |
433: | |
434: | |
435: | |
436: | |
437: | public function GetModulesSettingsPath() |
438: | { |
439: | return \Aurora\System\Api::DataPath() . '/settings/modules/'; |
440: | } |
441: | |
442: | |
443: | |
444: | |
445: | public function GetTenantModulesPath($sTenant) |
446: | { |
447: | return AU_APP_ROOT_PATH . 'tenants/' . $sTenant . '/modules/'; |
448: | } |
449: | |
450: | |
451: | |
452: | |
453: | public function GetAllowedModulesName() |
454: | { |
455: | $aArgs = []; |
456: | $mResult = $this->_aAllowedModulesName; |
457: | |
458: | $this->broadcastEvent('System', 'GetAllowedModulesName', $aArgs, $mResult, true); |
459: | |
460: | return $mResult; |
461: | } |
462: | |
463: | |
464: | |
465: | |
466: | |
467: | public function IsAllowedModule($sModuleName) |
468: | { |
469: | $aArgs = [ |
470: | 'ModuleName' => $sModuleName |
471: | ]; |
472: | $mResult = array_key_exists(\strtolower($sModuleName), $this->_aAllowedModulesName); |
473: | |
474: | $this->broadcastEvent('System', 'IsAllowedModule', $aArgs, $mResult, true); |
475: | |
476: | return $mResult; |
477: | } |
478: | |
479: | |
480: | |
481: | |
482: | public function GetModules() |
483: | { |
484: | return $this->_aModules; |
485: | } |
486: | |
487: | |
488: | |
489: | |
490: | |
491: | public function &getModuleSettings($sModuleName) |
492: | { |
493: | if (!isset($this->aModulesSettings[strtolower($sModuleName)])) { |
494: | $sSettingsClassName = '\\Aurora\\Modules\\' . $sModuleName . '\\Settings'; |
495: | if (class_exists($sSettingsClassName)) { |
496: | $this->aModulesSettings[strtolower($sModuleName)] = new $sSettingsClassName($sModuleName); |
497: | } else { |
498: | $this->aModulesSettings[strtolower($sModuleName)] = new Settings($sModuleName); |
499: | } |
500: | } |
501: | |
502: | return $this->aModulesSettings[strtolower($sModuleName)]; |
503: | } |
504: | |
505: | |
506: | |
507: | |
508: | |
509: | public function FlushModuleSettings($sModuleName) |
510: | { |
511: | if (isset($this->aModulesSettings[strtolower($sModuleName)])) { |
512: | unset($this->aModulesSettings[strtolower($sModuleName)]); |
513: | } |
514: | } |
515: | |
516: | |
517: | |
518: | |
519: | |
520: | public function GetModule($sModuleName) |
521: | { |
522: | $mResult = false; |
523: | |
524: | $sModuleNameLower = strtolower($sModuleName); |
525: | if ($this->isModuleLoaded($sModuleName)) { |
526: | $mResult = $this->_aModules[$sModuleNameLower]; |
527: | } |
528: | |
529: | return $mResult; |
530: | } |
531: | |
532: | |
533: | |
534: | |
535: | |
536: | public function GetModuleFromRequest() |
537: | { |
538: | $sModule = ''; |
539: | $oHttp = \MailSo\Base\Http::SingletonInstance(); |
540: | if ($oHttp->IsPost()) { |
541: | $sModule = $oHttp->GetPost('Module', null); |
542: | } |
543: | return $this->GetModule($sModule); |
544: | } |
545: | |
546: | |
547: | |
548: | |
549: | |
550: | |
551: | public function GetModulesByEntry($sEntryName) |
552: | { |
553: | $aModules = array(); |
554: | $oResult = $this->GetModuleFromRequest(); |
555: | |
556: | if ($oResult && !$oResult->HasEntry($sEntryName)) { |
557: | $oResult = false; |
558: | } |
559: | if ($oResult === false) { |
560: | foreach ($this->_aModules as $oModule) { |
561: | if ($oModule instanceof AbstractModule && $oModule->HasEntry($sEntryName)) { |
562: | $aModules[] = $oModule; |
563: | } |
564: | } |
565: | } else { |
566: | $aModules = array( |
567: | $oResult |
568: | ); |
569: | } |
570: | |
571: | return $aModules; |
572: | } |
573: | |
574: | |
575: | |
576: | |
577: | |
578: | public function ModuleExists($sModuleName) |
579: | { |
580: | return ($this->GetModule($sModuleName)) ? true : false; |
581: | } |
582: | |
583: | |
584: | |
585: | |
586: | |
587: | |
588: | public function RunEntry($sEntryName) |
589: | { |
590: | $oHttp = \MailSo\Base\Http::SingletonInstance(); |
591: | |
592: | $aArguments = [ |
593: | 'EntryName' => $sEntryName, |
594: | 'Module' => $oHttp->GetPost('Module', null), |
595: | 'Method' => $oHttp->GetPost('Method', null), |
596: | 'Parameters' => \json_decode($oHttp->GetPost('Parameters', ''), true) |
597: | ]; |
598: | $mResult = false; |
599: | |
600: | try { |
601: | $bEventResult = $this->broadcastEvent('System', 'RunEntry' . AbstractModule::$Delimiter . 'before', $aArguments, $mResult); |
602: | |
603: | if ($bEventResult !== true) { |
604: | if (!\Aurora\System\Router::getInstance()->hasRoute($sEntryName)) { |
605: | $sEntryName = 'default'; |
606: | } |
607: | |
608: | $mResult = \Aurora\System\Router::getInstance()->route( |
609: | $sEntryName |
610: | ); |
611: | } |
612: | } catch(\Exception $oException) { |
613: | $mResult = \Aurora\System\Managers\Response::GetJsonFromObject( |
614: | 'Json', |
615: | \Aurora\System\Managers\Response::ExceptionResponse("System", $oException) |
616: | ); |
617: | \Aurora\System\Api::LogException($oException); |
618: | } finally { |
619: | $this->broadcastEvent('System', 'RunEntry' . AbstractModule::$Delimiter . 'after', $aArguments, $mResult); |
620: | } |
621: | |
622: | return $mResult; |
623: | } |
624: | |
625: | |
626: | |
627: | |
628: | public function GetModulesHash() |
629: | { |
630: | $sResult = md5(\Aurora\System\Api::Version()); |
631: | $aModuleNames = $this->GetAllowedModulesName(); |
632: | foreach ($aModuleNames as $sModuleName) { |
633: | $sResult = md5($sResult . $this->GetModuleHashByName($sModuleName)); |
634: | } |
635: | |
636: | return $sResult; |
637: | } |
638: | |
639: | |
640: | |
641: | |
642: | |
643: | |
644: | |
645: | public function GetModuleHashByName($sModuleName) |
646: | { |
647: | $sResult = ''; |
648: | $sTenantName = \Aurora\System\Api::getTenantName(); |
649: | |
650: | $sResult .= $sTenantName !== 'Default' ? $this->GetModulesRootPath() : $this->GetTenantModulesPath($sTenantName); |
651: | $sResult .= $sModuleName; |
652: | |
653: | return md5($sResult); |
654: | } |
655: | |
656: | |
657: | |
658: | |
659: | public function SetLastException($oExcetpion) |
660: | { |
661: | $this->oLastException = $oExcetpion; |
662: | } |
663: | |
664: | |
665: | |
666: | |
667: | public function GetLastException() |
668: | { |
669: | return $this->oLastException; |
670: | } |
671: | |
672: | |
673: | |
674: | |
675: | |
676: | |
677: | |
678: | public function AddResult($sModule, $sMethod, $aParameters, $mResult, $iErrorCode = 0) |
679: | { |
680: | if (is_string($mResult)) { |
681: | $mResult = \str_replace(\Aurora\System\Api::$aSecretWords, '*******', $mResult); |
682: | } |
683: | |
684: | $aMapParameters = array(); |
685: | if (is_array($aParameters)) { |
686: | foreach ($aParameters as $sKey => $mParameter) { |
687: | if (!is_resource($mParameter) && gettype($mParameter) !== 'unknown type') { |
688: | $aMapParameters[$sKey] = $mParameter; |
689: | } |
690: | } |
691: | } |
692: | |
693: | $aResult = array( |
694: | 'Module' => $sModule, |
695: | 'Method' => $sMethod, |
696: | 'Parameters' => $aMapParameters, |
697: | 'Result' => $mResult |
698: | ); |
699: | |
700: | if ($iErrorCode > 0) { |
701: | $aResult['ErrorCode'] = $iErrorCode; |
702: | } |
703: | |
704: | $this->_aResults[] = $aResult; |
705: | } |
706: | |
707: | |
708: | |
709: | |
710: | public function GetResults() |
711: | { |
712: | return $this->_aResults; |
713: | } |
714: | |
715: | |
716: | |
717: | |
718: | |
719: | |
720: | public function GetResult($sModule, $sMethod) |
721: | { |
722: | foreach ($this->_aResults as $aResult) { |
723: | if ($aResult['Module'] === $sModule && $aResult['Method'] === $sMethod) { |
724: | return [$aResult]; |
725: | } |
726: | } |
727: | |
728: | return []; |
729: | } |
730: | |
731: | |
732: | |
733: | |
734: | |
735: | |
736: | |
737: | |
738: | |
739: | |
740: | |
741: | |
742: | |
743: | public function broadcastEvent($sModule, $sEvent, &$aArguments = [], &$mResult = null, $bSkipIsAllowedModuleCheck = false) |
744: | { |
745: | return $this->oEventEmitter->emit( |
746: | $sModule, |
747: | $sEvent, |
748: | $aArguments, |
749: | $mResult, |
750: | function ($sModule, $aArguments, $mResult) use ($sEvent) { |
751: | $this->AddResult($sModule, $sEvent, $aArguments, $mResult); |
752: | }, |
753: | $bSkipIsAllowedModuleCheck |
754: | ); |
755: | } |
756: | |
757: | |
758: | |
759: | |
760: | |
761: | |
762: | |
763: | |
764: | |
765: | |
766: | |
767: | |
768: | |
769: | |
770: | |
771: | |
772: | |
773: | public function subscribeEvent($sEvent, $fCallback, $iPriority = 100) |
774: | { |
775: | $this->oEventEmitter->on($sEvent, $fCallback, $iPriority); |
776: | } |
777: | |
778: | public function getEvents() |
779: | { |
780: | return $this->oEventEmitter->getListeners(); |
781: | } |
782: | |
783: | public function GetSubscriptionsResult() |
784: | { |
785: | return $this->oEventEmitter->getListenersResult(); |
786: | } |
787: | } |
788: | |