HookIntegrator.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. <?php
  2. namespace ModulesGarden\ProxmoxAddon\Core\Hook;
  3. use function \ModulesGarden\ProxmoxAddon\Core\Helper\isAdmin;
  4. use \ModulesGarden\ProxmoxAddon\Core\ModuleConstants;
  5. use \ModulesGarden\ProxmoxAddon\Core\DependencyInjection;
  6. use ModulesGarden\ProxmoxAddon\Core\UI\Traits\RequestObjectHandler;
  7. /**
  8. * class HookIntegrator
  9. * Prepares a views basing on /App/Integrations/Admin/ & /App/Integrations/Client controlers
  10. * to be injected on WHMCS subpages
  11. */
  12. class HookIntegrator
  13. {
  14. use RequestObjectHandler;
  15. /**
  16. * @var null
  17. * an array of WHMCS hook params, in which the Integrator was used
  18. */
  19. protected $hookParams = null;
  20. /**
  21. * @var bool
  22. * determines if works on admin or client area side
  23. */
  24. protected $isAdmin = false;
  25. /** @var array
  26. * avalible hook integrations list
  27. */
  28. protected $integrations = [];
  29. /** @var null|string
  30. * HTML data to be returned as a result of the integration process
  31. */
  32. protected $integrationData = [];
  33. public function __construct($hookParams)
  34. {
  35. $this->setHookParams($hookParams);
  36. $this->checkIsAdmin();
  37. $this->integrate();
  38. }
  39. public function setHookParams($hookParams)
  40. {
  41. if (is_array($hookParams))
  42. {
  43. $this->hookParams = $hookParams;
  44. }
  45. return $this;
  46. }
  47. /**
  48. * determines if works on admin or client area side
  49. */
  50. public function checkIsAdmin()
  51. {
  52. $this->isAdmin = isAdmin();
  53. }
  54. /**
  55. * returns integration output
  56. */
  57. public function getHtmlCode()
  58. {
  59. return $this->getWrapperHtml();
  60. }
  61. /**
  62. * starts whole integration process
  63. */
  64. protected function integrate()
  65. {
  66. $this->loadAvailableIntegrations();
  67. $this->loadIntegrationData();
  68. }
  69. /**
  70. * loads available integration instances for current page
  71. */
  72. protected function loadAvailableIntegrations()
  73. {
  74. $hooksPath = ModuleConstants::getModuleRootDir() . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'Integrations'
  75. . DIRECTORY_SEPARATOR . ($this->isAdmin ? 'Admin' : 'Client');
  76. if (!file_exists($hooksPath) || !is_readable($hooksPath))
  77. {
  78. return false;
  79. }
  80. $files = scandir($hooksPath, 1);
  81. if ($files)
  82. {
  83. foreach ($files as $key => $value)
  84. {
  85. if ($value === "." || $value === ".." || !(stripos($value, '.php') > 0))
  86. {
  87. unset($files[$key]);
  88. continue;
  89. }
  90. $this->addIntegration(str_replace('.php', '', $value));
  91. }
  92. }
  93. }
  94. /**
  95. * adds integration instance to the integrations list for current page
  96. * @param null|string $className
  97. * @return bool
  98. */
  99. protected function addIntegration($className = null)
  100. {
  101. //check if integration class exists
  102. $integrationClassName = '\ModulesGarden\ProxmoxAddon\App\Integrations\\' . ($this->isAdmin ? 'Admin' : 'Client') . '\\' . $className;
  103. if (!class_exists($integrationClassName) || !is_subclass_of($integrationClassName, \ModulesGarden\ProxmoxAddon\Core\Hook\AbstractHookIntegrationController::class))
  104. {
  105. return false;
  106. }
  107. //creates an instance of integration class
  108. $integrationInstance = DependencyInjection::create($integrationClassName);
  109. //check if integration should be added to current page
  110. if (!$this->validateIntegrationInstance($integrationInstance))
  111. {
  112. return false;
  113. }
  114. $this->integrations[] = $integrationInstance;
  115. }
  116. /**
  117. * check if the integration should be added to current page
  118. * @param null|AbstractHookIntegrationController $instance
  119. * @return bool
  120. */
  121. public function validateIntegrationInstance($instance = null)
  122. {
  123. if (!is_subclass_of($instance, \ModulesGarden\ProxmoxAddon\Core\Hook\AbstractHookIntegrationController::class))
  124. {
  125. return false;
  126. }
  127. if (
  128. $instance->getFileName() === null ||
  129. $instance->getJqSelector() === null ||
  130. !is_callable($instance->getControllerCallback())
  131. )
  132. {
  133. return false;
  134. }
  135. return true;
  136. }
  137. public function loadIntegrationData()
  138. {
  139. foreach ($this->integrations as $integration)
  140. {
  141. if (!$this->isIntegrationApplicable($integration))
  142. {
  143. continue;
  144. }
  145. /** @var
  146. * $integrationResult \ModulesGarden\ProxmoxAddon\Core\UI\View
  147. */
  148. $integrationResult = call_user_func($integration->getControllerCallback());
  149. $view = new HookIntegratorView($integrationResult, $integration);
  150. $this->updateIntegrationData($integration, $view->getHTML());
  151. }
  152. }
  153. /**
  154. * check if integration params match page/request params
  155. * @param null|AbstractHookIntegrationController $integration
  156. * @return bool
  157. */
  158. public function isIntegrationApplicable($integration = null)
  159. {
  160. //check just in case, in order not to kill whole WHMCS
  161. if (!$integration)
  162. {
  163. return false;
  164. }
  165. //check if filename is correct for the integration
  166. if ($this->hookParams['filename'] !== $integration->getFileName())
  167. {
  168. return false;
  169. }
  170. //check if all provided request params are correct for the integration
  171. foreach ($integration->getRequestParams() as $rKey => $rParam)
  172. {
  173. if (is_array($rParam))
  174. {
  175. $found = false;
  176. foreach ($rParam as $irParam)
  177. {
  178. if ($this->getRequestValue($rKey) === $irParam)
  179. {
  180. $found = true;
  181. break;
  182. }
  183. }
  184. if (!$found)
  185. {
  186. return false;
  187. }
  188. }
  189. elseif ($this->getRequestValue($rKey) !== $rParam)
  190. {
  191. return false;
  192. }
  193. }
  194. //check if integration callback is correct
  195. $integrationCallback = $integration->getControllerCallback();
  196. if ((!is_subclass_of($integrationCallback[0], \ModulesGarden\ProxmoxAddon\Core\Http\AbstractController::class) && !is_subclass_of($integrationCallback[0], \ModulesGarden\ProxmoxAddon\Core\Http\AbstractClientController::class)) || !method_exists($integrationCallback[0], $integrationCallback[1]))
  197. {
  198. return false;
  199. }
  200. return true;
  201. }
  202. protected function updateIntegrationData($integrationDetails, $htmlData)
  203. {
  204. if (!is_string($htmlData) || $htmlData === '' || !$integrationDetails || !is_object($integrationDetails))
  205. {
  206. return false;
  207. }
  208. $this->integrationData[] = [
  209. 'htmlData' => $htmlData,
  210. 'integrationDetails' => $integrationDetails
  211. ];
  212. }
  213. protected function getWrapperHtml()
  214. {
  215. if (!$this->integrationData)
  216. {
  217. return null;
  218. }
  219. $wrapper = new HookIntegrationsWrapper($this->integrationData);
  220. return $wrapper->getHtml();
  221. }
  222. }