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: | $oCoreModule = $this->loadModule('Core'); |
101: | |
102: | if ($oCoreModule instanceof AbstractModule) { |
103: | $oUser = \Aurora\System\Api::authorise(); |
104: | $oTenant = null; |
105: | if ($oUser instanceof User && $oUser->Role !== \Aurora\System\Enums\UserRole::SuperAdmin) { |
106: | $oTenant = \Aurora\Modules\Core\Module::Decorator()->GetTenantUnchecked($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: | $aModuleDefaultSettings = $oSettings->GetDefaultConfigValues(); |
223: | |
224: | if (isset($aModulesPreconfig[$sModuleName])) { |
225: | $aModulePreconfig = $aModulesPreconfig[$sModuleName]; |
226: | foreach ($aModuleDefaultSettings as $key => $oSetting) { |
227: | if (array_key_exists($key, $aModulePreconfig)) { |
228: | $oSetting->Value = $aModulePreconfig[$key]; |
229: | } |
230: | } |
231: | } |
232: | $aModuleSettings = []; |
233: | if (@\file_exists($oSettings->GetPath())) { |
234: | $aModuleSettings = $oSettings->GetValues(); |
235: | } |
236: | |
237: | $aValues = array_merge( |
238: | $aModuleDefaultSettings, |
239: | $aModuleSettings |
240: | ); |
241: | $oSettings->SetValues($aValues); |
242: | $oSettings->Save(); |
243: | } |
244: | } |
245: | } |
246: | } |
247: | |
248: | |
249: | |
250: | |
251: | |
252: | |
253: | protected function loadModule($sModuleName, $sModulePath = null) |
254: | { |
255: | $mResult = false; |
256: | if (!isset($sModulePath)) { |
257: | $sModulePath = $this->GetModulePath($sModuleName); |
258: | } |
259: | |
260: | if ($sModulePath) { |
261: | if (!$this->isModuleLoaded($sModuleName)) { |
262: | $aArgs = array($sModuleName, $sModulePath); |
263: | |
264: | $this->broadcastEvent( |
265: | $sModuleName, |
266: | 'loadModule' . AbstractModule::$Delimiter . 'before', |
267: | $aArgs |
268: | ); |
269: | |
270: | if (@\file_exists($sModulePath.$sModuleName.'/Module.php')) { |
271: | $sModuleClassName = '\\Aurora\\Modules\\' . $sModuleName . '\\Module'; |
272: | $oModule = new $sModuleClassName($sModulePath); |
273: | if ($oModule instanceof AbstractModule) { |
274: | foreach ($oModule->GetRequireModules() as $sModule) { |
275: | if (!$this->loadModule($sModule, $sModulePath)) { |
276: | break; |
277: | } |
278: | } |
279: | |
280: | if ($oModule->initialize() && $oModule->isValid()) { |
281: | $this->_aModules[\strtolower($sModuleName)] = $oModule; |
282: | $mResult = $oModule; |
283: | } |
284: | } |
285: | } |
286: | |
287: | $this->broadcastEvent( |
288: | $sModuleName, |
289: | 'loadModule' . AbstractModule::$Delimiter . 'after', |
290: | $aArgs, |
291: | $mResult |
292: | ); |
293: | } else { |
294: | $mResult = $this->GetModule($sModuleName); |
295: | } |
296: | } |
297: | return $mResult; |
298: | } |
299: | |
300: | |
301: | |
302: | |
303: | |
304: | |
305: | |
306: | public function includeTemplate($sParsedTemplateID, $sParsedPlace, $sTemplateFileName, $sModuleName = '') |
307: | { |
308: | if (!isset($this->_aTemplates[$sParsedTemplateID])) { |
309: | $this->_aTemplates[$sParsedTemplateID] = array(); |
310: | } |
311: | |
312: | $this->_aTemplates[$sParsedTemplateID][] = array( |
313: | $sParsedPlace, |
314: | $sTemplateFileName, |
315: | $sModuleName |
316: | ); |
317: | } |
318: | |
319: | |
320: | |
321: | |
322: | |
323: | |
324: | |
325: | public function ParseTemplate($sTemplateID, $sTemplateSource) |
326: | { |
327: | if (isset($this->_aTemplates[$sTemplateID]) && \is_array($this->_aTemplates[$sTemplateID])) { |
328: | foreach ($this->_aTemplates[$sTemplateID] as $aItem) { |
329: | if (!empty($aItem[0]) && !empty($aItem[1]) && \file_exists($aItem[1])) { |
330: | $sTemplateHtml = \file_get_contents($aItem[1]); |
331: | if (!empty($aItem[2])) { |
332: | $sTemplateHtml = \str_replace('%ModuleName%', $aItem[2], $sTemplateHtml); |
333: | $sTemplateHtml = \str_replace('%MODULENAME%', \strtoupper($aItem[2]), $sTemplateHtml); |
334: | } |
335: | $sTemplateSource = \str_replace( |
336: | '{%INCLUDE-START/'.$aItem[0].'/INCLUDE-END%}', |
337: | $sTemplateHtml.'{%INCLUDE-START/'.$aItem[0].'/INCLUDE-END%}', |
338: | $sTemplateSource |
339: | ); |
340: | } |
341: | } |
342: | } |
343: | |
344: | return $sTemplateSource; |
345: | } |
346: | |
347: | |
348: | |
349: | |
350: | |
351: | |
352: | |
353: | public function extendObject($sModule, $sType, $aMap) |
354: | { |
355: | $this->oObjectExtender->extend($sModule, $sType, $aMap); |
356: | } |
357: | |
358: | |
359: | |
360: | |
361: | |
362: | |
363: | public function getExtendedObject($sType) |
364: | { |
365: | return $this->oObjectExtender->getObject($sType); |
366: | } |
367: | |
368: | |
369: | |
370: | |
371: | |
372: | |
373: | public function issetObject($sType) |
374: | { |
375: | return $this->oObjectExtender->issetObject($sType); |
376: | } |
377: | |
378: | |
379: | |
380: | |
381: | |
382: | |
383: | public function GetModulesRootPath() |
384: | { |
385: | return AU_APP_ROOT_PATH.'modules/'; |
386: | } |
387: | |
388: | |
389: | |
390: | |
391: | |
392: | |
393: | public function GetModulesPaths() |
394: | { |
395: | if (!isset($this->_aModulesPaths)) { |
396: | $sModulesPath = $this->GetModulesRootPath(); |
397: | $aModulePath = [ |
398: | $sModulesPath |
399: | ]; |
400: | $oCoreModule = $this->loadModule('Core', $sModulesPath); |
401: | if ($oCoreModule instanceof \Aurora\Modules\Core\Module) { |
402: | $sTenant = \trim($oCoreModule->GetTenantName()); |
403: | if (!empty($sTenant)) { |
404: | $sTenantModulesPath = $this->GetTenantModulesPath($sTenant); |
405: | \array_unshift($aModulePath, $sTenantModulesPath); |
406: | } |
407: | } |
408: | $this->_aModulesPaths = []; |
409: | foreach ($aModulePath as $sModulesPath) { |
410: | if (@\is_dir($sModulesPath)) { |
411: | if (false !== ($rDirHandle = @\opendir($sModulesPath))) { |
412: | while (false !== ($sFileItem = @\readdir($rDirHandle))) { |
413: | if (0 < \strlen($sFileItem) && '.' !== $sFileItem[0] && \preg_match('/^[a-zA-Z0-9\-]+$/', $sFileItem)) { |
414: | $this->_aModulesPaths[$sFileItem] = $sModulesPath; |
415: | } |
416: | } |
417: | |
418: | @\closedir($rDirHandle); |
419: | } |
420: | } |
421: | } |
422: | } |
423: | |
424: | return $this->_aModulesPaths; |
425: | } |
426: | |
427: | |
428: | |
429: | |
430: | |
431: | |
432: | public function GetModulePath($sModuleName) |
433: | { |
434: | $aModulesPaths = $this->GetModulesPaths(); |
435: | return isset($aModulesPaths[$sModuleName]) ? $aModulesPaths[$sModuleName] : false; |
436: | } |
437: | |
438: | |
439: | |
440: | |
441: | |
442: | |
443: | public function GetModulesSettingsPath() |
444: | { |
445: | return \Aurora\System\Api::DataPath() . '/settings/modules/'; |
446: | } |
447: | |
448: | |
449: | |
450: | |
451: | public function GetTenantModulesPath($sTenant) |
452: | { |
453: | return AU_APP_ROOT_PATH.'tenants/' . $sTenant . '/modules/'; |
454: | } |
455: | |
456: | |
457: | |
458: | |
459: | public function GetAllowedModulesName() |
460: | { |
461: | $aArgs = []; |
462: | $mResult = $this->_aAllowedModulesName; |
463: | |
464: | $this->broadcastEvent('System', 'GetAllowedModulesName', $aArgs, $mResult, true); |
465: | |
466: | return $mResult; |
467: | } |
468: | |
469: | |
470: | |
471: | |
472: | |
473: | public function IsAllowedModule($sModuleName) |
474: | { |
475: | $aArgs = [ |
476: | 'ModuleName' => $sModuleName |
477: | ]; |
478: | $mResult = array_key_exists(\strtolower($sModuleName), $this->_aAllowedModulesName); |
479: | |
480: | $this->broadcastEvent('System', 'IsAllowedModule', $aArgs, $mResult, true); |
481: | |
482: | return $mResult; |
483: | } |
484: | |
485: | |
486: | |
487: | |
488: | public function GetModules() |
489: | { |
490: | return $this->_aModules; |
491: | } |
492: | |
493: | |
494: | |
495: | |
496: | |
497: | public function &GetModuleSettings($sModuleName) |
498: | { |
499: | if (!isset($this->aModulesSettings[strtolower($sModuleName)])) { |
500: | $this->aModulesSettings[strtolower($sModuleName)] = new Settings($sModuleName); |
501: | } |
502: | |
503: | return $this->aModulesSettings[strtolower($sModuleName)]; |
504: | } |
505: | |
506: | |
507: | |
508: | |
509: | |
510: | public function FlushModuleSettings($sModuleName) |
511: | { |
512: | if (isset($this->aModulesSettings[strtolower($sModuleName)])) { |
513: | unset($this->aModulesSettings[strtolower($sModuleName)]); |
514: | } |
515: | } |
516: | |
517: | |
518: | |
519: | |
520: | |
521: | public function GetModule($sModuleName) |
522: | { |
523: | $mResult = false; |
524: | |
525: | $sModuleNameLower = strtolower($sModuleName); |
526: | if ($this->isModuleLoaded($sModuleName)) { |
527: | $mResult = $this->_aModules[$sModuleNameLower]; |
528: | } |
529: | |
530: | return $mResult; |
531: | } |
532: | |
533: | |
534: | |
535: | |
536: | |
537: | public function GetModuleFromRequest() |
538: | { |
539: | $sModule = ''; |
540: | $oHttp = \MailSo\Base\Http::SingletonInstance(); |
541: | if ($oHttp->IsPost()) { |
542: | $sModule = $oHttp->GetPost('Module', null); |
543: | } |
544: | return $this->GetModule($sModule); |
545: | } |
546: | |
547: | |
548: | |
549: | |
550: | |
551: | |
552: | public function GetModulesByEntry($sEntryName) |
553: | { |
554: | $aModules = array(); |
555: | $oResult = $this->GetModuleFromRequest(); |
556: | |
557: | if ($oResult && !$oResult->HasEntry($sEntryName)) { |
558: | $oResult = false; |
559: | } |
560: | if ($oResult === false) { |
561: | foreach ($this->_aModules as $oModule) { |
562: | if ($oModule instanceof AbstractModule && $oModule->HasEntry($sEntryName)) { |
563: | $aModules[] = $oModule; |
564: | } |
565: | } |
566: | } else { |
567: | $aModules = array( |
568: | $oResult |
569: | ); |
570: | } |
571: | |
572: | return $aModules; |
573: | } |
574: | |
575: | |
576: | |
577: | |
578: | |
579: | public function ModuleExists($sModuleName) |
580: | { |
581: | return ($this->GetModule($sModuleName)) ? true : false; |
582: | } |
583: | |
584: | |
585: | |
586: | |
587: | |
588: | |
589: | public function RunEntry($sEntryName) |
590: | { |
591: | $aArguments = [ |
592: | 'EntryName' => $sEntryName |
593: | ]; |
594: | $mResult = false; |
595: | try { |
596: | $bEventResult = $this->broadcastEvent('System', 'RunEntry' . AbstractModule::$Delimiter . 'before', $aArguments, $mResult); |
597: | |
598: | if ($bEventResult !== true) { |
599: | if (!\Aurora\System\Router::getInstance()->hasRoute($sEntryName)) { |
600: | $sEntryName = 'default'; |
601: | } |
602: | |
603: | $mResult = \Aurora\System\Router::getInstance()->route( |
604: | $sEntryName |
605: | ); |
606: | } |
607: | |
608: | $this->broadcastEvent('System', 'RunEntry' . AbstractModule::$Delimiter . 'after', $aArguments, $mResult); |
609: | } catch(\Exception $oException) { |
610: | $mResult = \Aurora\System\Managers\Response::GetJsonFromObject( |
611: | "Json", |
612: | \Aurora\System\Managers\Response::ExceptionResponse("System", $oException) |
613: | ); |
614: | \Aurora\System\Api::LogException($oException); |
615: | } |
616: | |
617: | return $mResult; |
618: | } |
619: | |
620: | |
621: | |
622: | |
623: | public function GetModulesHash() |
624: | { |
625: | $sResult = md5(\Aurora\System\Api::Version()); |
626: | $aModuleNames = $this->GetAllowedModulesName(); |
627: | foreach ($aModuleNames as $sModuleName) { |
628: | $sResult = md5($sResult.$this->GetModuleHashByName($sModuleName)); |
629: | } |
630: | |
631: | return $sResult; |
632: | } |
633: | |
634: | |
635: | |
636: | |
637: | |
638: | |
639: | |
640: | public function GetModuleHashByName($sModuleName) |
641: | { |
642: | $sResult = ''; |
643: | $sTenantName = \Aurora\System\Api::getTenantName(); |
644: | |
645: | $sResult .= $sTenantName !== 'Default' ? $this->GetModulesRootPath() : $this->GetTenantModulesPath($sTenantName); |
646: | $sResult .= $sModuleName; |
647: | |
648: | return md5($sResult); |
649: | } |
650: | |
651: | |
652: | |
653: | |
654: | public function SetLastException($oExcetpion) |
655: | { |
656: | $this->oLastException = $oExcetpion; |
657: | } |
658: | |
659: | |
660: | |
661: | |
662: | public function GetLastException() |
663: | { |
664: | return $this->oLastException; |
665: | } |
666: | |
667: | |
668: | |
669: | |
670: | |
671: | |
672: | |
673: | public function AddResult($sModule, $sMethod, $aParameters, $mResult, $iErrorCode = 0) |
674: | { |
675: | if (is_string($mResult)) { |
676: | $mResult = \str_replace(\Aurora\System\Api::$aSecretWords, '*******', $mResult); |
677: | } |
678: | |
679: | $aMapParameters = array(); |
680: | if (is_array($aParameters)) { |
681: | foreach ($aParameters as $sKey => $mParameter) { |
682: | if (!is_resource($mParameter) && gettype($mParameter) !== 'unknown type') { |
683: | $aMapParameters[$sKey] = $mParameter; |
684: | } |
685: | } |
686: | } |
687: | |
688: | $aResult = array( |
689: | 'Module' => $sModule, |
690: | 'Method' => $sMethod, |
691: | 'Parameters' => $aMapParameters, |
692: | 'Result' => $mResult |
693: | ); |
694: | |
695: | if ($iErrorCode > 0) { |
696: | $aResult['ErrorCode'] = $iErrorCode; |
697: | } |
698: | |
699: | $this->_aResults[] = $aResult; |
700: | } |
701: | |
702: | |
703: | |
704: | |
705: | public function GetResults() |
706: | { |
707: | return $this->_aResults; |
708: | } |
709: | |
710: | |
711: | |
712: | |
713: | |
714: | |
715: | public function GetResult($sModule, $sMethod) |
716: | { |
717: | foreach ($this->_aResults as $aResult) { |
718: | if ($aResult['Module'] === $sModule && $aResult['Method'] === $sMethod) { |
719: | return [$aResult]; |
720: | } |
721: | } |
722: | |
723: | return []; |
724: | } |
725: | |
726: | |
727: | |
728: | |
729: | |
730: | |
731: | |
732: | |
733: | |
734: | |
735: | |
736: | |
737: | |
738: | public function broadcastEvent($sModule, $sEvent, &$aArguments = [], &$mResult = null, $bSkipIsAllowedModuleCheck = false) |
739: | { |
740: | return $this->oEventEmitter->emit( |
741: | $sModule, |
742: | $sEvent, |
743: | $aArguments, |
744: | $mResult, |
745: | function ($sModule, $aArguments, $mResult) use ($sEvent) { |
746: | $this->AddResult($sModule, $sEvent, $aArguments, $mResult); |
747: | }, |
748: | $bSkipIsAllowedModuleCheck |
749: | ); |
750: | } |
751: | |
752: | |
753: | |
754: | |
755: | |
756: | |
757: | |
758: | |
759: | |
760: | |
761: | |
762: | |
763: | |
764: | |
765: | |
766: | |
767: | |
768: | public function subscribeEvent($sEvent, $fCallback, $iPriority = 100) |
769: | { |
770: | return $this->oEventEmitter->on($sEvent, $fCallback, $iPriority); |
771: | } |
772: | |
773: | public function getEvents() |
774: | { |
775: | return $this->oEventEmitter->getListeners(); |
776: | } |
777: | |
778: | public function GetSubscriptionsResult() |
779: | { |
780: | return $this->oEventEmitter->getListenersResult(); |
781: | } |
782: | } |
783: | |