PageRenderTime 32ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/rainloop/v/0.0.0/app/libraries/RainLoop/Plugins/Manager.php

https://gitlab.com/wuhang2003/rainloop-webmail
PHP | 770 lines | 478 code | 91 blank | 201 comment | 71 complexity | ef08659369b60cf7ab84f4a68ecfe71b MD5 | raw file
  1. <?php
  2. namespace RainLoop\Plugins;
  3. class Manager
  4. {
  5. /**
  6. * @var \RainLoop\Actions
  7. */
  8. private $oActions;
  9. /**
  10. * @var array
  11. */
  12. private $aHooks;
  13. /**
  14. * @var array
  15. */
  16. private $aJs;
  17. /**
  18. * @var array
  19. */
  20. private $aAdminJs;
  21. /**
  22. * @var array
  23. */
  24. private $aTemplates;
  25. /**
  26. * @var array
  27. */
  28. private $aAdminTemplates;
  29. /**
  30. * @var array
  31. */
  32. private $aProcessTemplate;
  33. /**
  34. * @var array
  35. */
  36. private $aAdditionalParts;
  37. /**
  38. * @var array
  39. */
  40. private $aAdditionalAjax;
  41. /**
  42. * @var array
  43. */
  44. private $aPlugins;
  45. /**
  46. * @var bool
  47. */
  48. private $bIsEnabled;
  49. /**
  50. * @var \MailSo\Log\Logger
  51. */
  52. private $oLogger;
  53. /**
  54. * @param \RainLoop\Actions $oActions
  55. */
  56. public function __construct(\RainLoop\Actions $oActions)
  57. {
  58. $this->oLogger = null;
  59. $this->oActions = $oActions;
  60. $this->aPlugins = array();
  61. $this->aHooks = array();
  62. $this->aJs = array();
  63. $this->aAdminJs = array();
  64. $this->aTemplates = array();
  65. $this->aAdminTemplates = array();
  66. $this->aAjaxFilters = array();
  67. $this->aAdditionalAjax = array();
  68. $this->aProcessTemplate = array();
  69. $this->bIsEnabled = (bool) $this->oActions->Config()->Get('plugins', 'enable', false);
  70. if ($this->bIsEnabled)
  71. {
  72. $sList = \strtolower($this->oActions->Config()->Get('plugins', 'enabled_list', ''));
  73. if (0 < \strlen($sList))
  74. {
  75. $aList = \explode(',', $sList);
  76. $aList = \array_map('trim', $aList);
  77. foreach ($aList as $sName)
  78. {
  79. if (0 < \strlen($sName))
  80. {
  81. $oPlugin = $this->CreatePluginByName($sName);
  82. if ($oPlugin)
  83. {
  84. $oPlugin->PreInit();
  85. $oPlugin->Init();
  86. $this->aPlugins[] = $oPlugin;
  87. }
  88. }
  89. }
  90. }
  91. $this->RunHook('api.bootstrap.plugins');
  92. }
  93. }
  94. /**
  95. * @param string $sName
  96. *
  97. * @return \RainLoop\Plugins\AbstractPlugin|null
  98. */
  99. public function CreatePluginByName($sName)
  100. {
  101. $oPlugin = null;
  102. if (\preg_match('/^[a-z0-9\-]+$/', $sName) &&
  103. \file_exists(APP_PLUGINS_PATH.$sName.'/index.php'))
  104. {
  105. $sClassName = $this->convertPluginFolderNameToClassName($sName);
  106. if (!\class_exists($sClassName))
  107. {
  108. include APP_PLUGINS_PATH.$sName.'/index.php';
  109. }
  110. if (\class_exists($sClassName))
  111. {
  112. $oPlugin = new $sClassName();
  113. if ($oPlugin instanceof \RainLoop\Plugins\AbstractPlugin)
  114. {
  115. $oPlugin
  116. ->SetName($sName)
  117. ->SetPath(APP_PLUGINS_PATH.$sName)
  118. ->SetVersion(\file_exists(APP_PLUGINS_PATH.$sName.'/VERSION') ?
  119. \file_get_contents(APP_PLUGINS_PATH.$sName.'/VERSION') : '')
  120. ->SetPluginManager($this)
  121. ->SetPluginConfig(new \RainLoop\Config\Plugin($sName, $oPlugin->ConfigMap()))
  122. ;
  123. }
  124. else
  125. {
  126. $oPlugin = null;
  127. }
  128. }
  129. }
  130. return $oPlugin;
  131. }
  132. /**
  133. * @return array
  134. */
  135. public function InstalledPlugins()
  136. {
  137. $aList = array();
  138. $aGlob = @\glob(APP_PLUGINS_PATH.'*', GLOB_ONLYDIR|GLOB_NOSORT);
  139. if (\is_array($aGlob))
  140. {
  141. foreach ($aGlob as $sPathName)
  142. {
  143. $sName = \basename($sPathName);
  144. if (\preg_match('/^[a-z0-9\-]+$/', $sName) &&
  145. \file_exists($sPathName.'/index.php'))
  146. {
  147. $aList[] = array(
  148. $sName,
  149. \file_exists($sPathName.'/VERSION') ?
  150. \file_get_contents($sPathName.'/VERSION') : '0.0'
  151. );
  152. }
  153. }
  154. }
  155. else
  156. {
  157. $this->Actions()->Logger()->Write('Cannot get installed plugins from '.APP_PLUGINS_PATH,
  158. \MailSo\Log\Enumerations\Type::ERROR);
  159. }
  160. return $aList;
  161. }
  162. /**
  163. * @param string $sFolderName
  164. *
  165. * @return string
  166. */
  167. public function convertPluginFolderNameToClassName($sFolderName)
  168. {
  169. $aParts = \array_map('ucfirst', \array_map('strtolower',
  170. \explode(' ', \preg_replace('/[^a-z0-9]+/', ' ', $sFolderName))));
  171. return \implode($aParts).'Plugin';
  172. }
  173. /**
  174. * @return \RainLoop\Actions
  175. */
  176. public function Actions()
  177. {
  178. return $this->oActions;
  179. }
  180. /**
  181. * @return string
  182. */
  183. public function Hash()
  184. {
  185. $sResult = \md5(APP_VERSION);
  186. foreach ($this->aPlugins as $oPlugin)
  187. {
  188. $sResult = \md5($sResult.$oPlugin->Path().$oPlugin->Hash());
  189. }
  190. return $sResult;
  191. }
  192. /**
  193. * @param bool $bAdminScope = false
  194. *
  195. * @return bool
  196. */
  197. public function HaveJs($bAdminScope = false)
  198. {
  199. $bResult = false;
  200. if ($this->bIsEnabled)
  201. {
  202. $bResult = $bAdminScope ? 0 < \count($this->aAdminJs) : 0 < \count($this->aJs);
  203. }
  204. return $bResult;
  205. }
  206. /**
  207. * @param bool $bAdminScope = false
  208. *
  209. * @return string
  210. */
  211. public function CompileJs($bAdminScope = false)
  212. {
  213. $aResult = array();
  214. if ($this->bIsEnabled)
  215. {
  216. $aJs = $bAdminScope ? $this->aAdminJs : $this->aJs;
  217. foreach ($aJs as $sFile)
  218. {
  219. if (\file_exists($sFile))
  220. {
  221. $aResult[] = \file_get_contents($sFile);
  222. }
  223. }
  224. }
  225. return \implode("\n", $aResult);
  226. }
  227. /**
  228. * @todo
  229. * @param bool $bAdminScope = false
  230. *
  231. * @return string
  232. */
  233. public function CompileCss($bAdminScope = false)
  234. {
  235. return '';
  236. }
  237. /**
  238. * @param array $aList
  239. * @param bool $bAdminScope = false
  240. * @return string
  241. */
  242. public function CompileTemplate(&$aList, $bAdminScope = false)
  243. {
  244. if ($this->bIsEnabled)
  245. {
  246. $aTemplates = $bAdminScope ? $this->aAdminTemplates : $this->aTemplates;
  247. foreach ($aTemplates as $sFile)
  248. {
  249. if (\file_exists($sFile))
  250. {
  251. $sTemplateName = \substr(\basename($sFile), 0, -5);
  252. $aList[$sTemplateName] = $sFile;
  253. }
  254. }
  255. }
  256. }
  257. /**
  258. * @param bool $bAdmin
  259. * @param array $aAppData
  260. * @param \RainLoop\Model\Account|null $oAccount = null
  261. *
  262. * @return \RainLoop\Plugins\Manager
  263. */
  264. public function InitAppData($bAdmin, &$aAppData, $oAccount = null)
  265. {
  266. if ($this->bIsEnabled && isset($aAppData['Plugins']) && \is_array($aAppData['Plugins']))
  267. {
  268. $bAuth = isset($aAppData['Auth']) && !!$aAppData['Auth'];
  269. foreach ($this->aPlugins as $oPlugin)
  270. {
  271. if ($oPlugin)
  272. {
  273. $aConfig = array();
  274. $aMap = $oPlugin->ConfigMap();
  275. if (\is_array($aMap))
  276. {
  277. foreach ($aMap as /* @var $oPluginProperty \RainLoop\Plugins\Property */ $oPluginProperty)
  278. {
  279. if ($oPluginProperty && $oPluginProperty->AllowedInJs())
  280. {
  281. $aConfig[$oPluginProperty->Name()] =
  282. $oPlugin->Config()->Get('plugin',
  283. $oPluginProperty->Name(),
  284. $oPluginProperty->DefaultValue());
  285. }
  286. }
  287. }
  288. $oPlugin->FilterAppDataPluginSection($bAdmin, $bAuth, $aConfig);
  289. if (0 < \count($aConfig))
  290. {
  291. $aAppData['Plugins'][$oPlugin->Name()] = $aConfig;
  292. }
  293. }
  294. }
  295. $this->RunHook('filter.app-data', array($bAdmin, &$aAppData));
  296. $this->RunHook('filter.app-data[2]', array(
  297. 'IsAdmin' => $bAdmin,
  298. 'AppData' => &$aAppData,
  299. 'Account' => $oAccount
  300. ));
  301. }
  302. return $this;
  303. }
  304. /**
  305. * @param string $sHookName
  306. * @param mixed $mCallbak
  307. *
  308. * @return \RainLoop\Plugins\Manager
  309. */
  310. public function AddHook($sHookName, $mCallbak)
  311. {
  312. if ($this->bIsEnabled && \is_callable($mCallbak))
  313. {
  314. if (!isset($this->aHooks[$sHookName]))
  315. {
  316. $this->aHooks[$sHookName] = array();
  317. }
  318. $this->aHooks[$sHookName][] = $mCallbak;
  319. }
  320. return $this;
  321. }
  322. /**
  323. * @param string $sFile
  324. * @param bool $bAdminScope = false
  325. *
  326. * @return \RainLoop\Plugins\Manager
  327. */
  328. public function AddJs($sFile, $bAdminScope = false)
  329. {
  330. if ($this->bIsEnabled)
  331. {
  332. if ($bAdminScope)
  333. {
  334. $this->aAdminJs[$sFile] = $sFile;
  335. }
  336. else
  337. {
  338. $this->aJs[$sFile] = $sFile;
  339. }
  340. }
  341. return $this;
  342. }
  343. /**
  344. * @param string $sFile
  345. * @param bool $bAdminScope = false
  346. *
  347. * @return \RainLoop\Plugins\Manager
  348. */
  349. public function AddTemplate($sFile, $bAdminScope = false)
  350. {
  351. if ($this->bIsEnabled)
  352. {
  353. if ($bAdminScope)
  354. {
  355. $this->aAdminTemplates[$sFile] = $sFile;
  356. }
  357. else
  358. {
  359. $this->aTemplates[$sFile] = $sFile;
  360. }
  361. }
  362. return $this;
  363. }
  364. /**
  365. * @param string $sHookName
  366. * @param array $aArg = array()
  367. * @param bool $bLogHook = true
  368. *
  369. * @return \RainLoop\Plugins\Manager
  370. */
  371. public function RunHook($sHookName, $aArg = array(), $bLogHook = true)
  372. {
  373. if ($this->bIsEnabled)
  374. {
  375. if (isset($this->aHooks[$sHookName]))
  376. {
  377. if ($bLogHook)
  378. {
  379. $this->WriteLog('Hook: '.$sHookName, \MailSo\Log\Enumerations\Type::NOTE);
  380. }
  381. foreach ($this->aHooks[$sHookName] as $mCallback)
  382. {
  383. \call_user_func_array($mCallback, $aArg);
  384. }
  385. }
  386. }
  387. return $this;
  388. }
  389. /**
  390. * @param string $sActionName
  391. * @param mixed $mCallbak
  392. *
  393. * @return \RainLoop\Plugins\Manager
  394. */
  395. public function AddAdditionalPartAction($sActionName, $mCallbak)
  396. {
  397. if ($this->bIsEnabled && \is_callable($mCallbak))
  398. {
  399. $sActionName = \strtolower($sActionName);
  400. if (!isset($this->aAdditionalParts[$sActionName]))
  401. {
  402. $this->aAdditionalParts[$sActionName] = array();
  403. }
  404. $this->aAdditionalParts[$sActionName][] = $mCallbak;
  405. }
  406. return $this;
  407. }
  408. /**
  409. * @param string $sActionName
  410. * @param array $aParts = array()
  411. *
  412. * @return \RainLoop\Plugins\Manager
  413. */
  414. public function RunAdditionalPart($sActionName, $aParts = array())
  415. {
  416. $bResult = false;
  417. if ($this->bIsEnabled)
  418. {
  419. $sActionName = \strtolower($sActionName);
  420. if (isset($this->aAdditionalParts[$sActionName]))
  421. {
  422. foreach ($this->aAdditionalParts[$sActionName] as $mCallbak)
  423. {
  424. $bCallResult = \call_user_func_array($mCallbak, $aParts);
  425. if ($bCallResult && !$bResult)
  426. {
  427. $bResult = true;
  428. }
  429. }
  430. }
  431. }
  432. return $bResult;
  433. }
  434. /**
  435. * @param string $sName
  436. * @param string $sPlace
  437. * @param string $sHtml
  438. * @param bool $bPrepend = false
  439. *
  440. * @return \RainLoop\Plugins\Manager
  441. */
  442. public function AddProcessTemplateAction($sName, $sPlace, $sHtml, $bPrepend = false)
  443. {
  444. if ($this->bIsEnabled)
  445. {
  446. if (!isset($this->aProcessTemplate[$sName]))
  447. {
  448. $this->aProcessTemplate[$sName] = array();
  449. }
  450. if (!isset($this->aProcessTemplate[$sName][$sPlace]))
  451. {
  452. $this->aProcessTemplate[$sName][$sPlace] = array();
  453. }
  454. if ($bPrepend)
  455. {
  456. \array_unshift($this->aProcessTemplate[$sName][$sPlace], $sHtml);
  457. }
  458. else
  459. {
  460. \array_push($this->aProcessTemplate[$sName][$sPlace], $sHtml);
  461. }
  462. }
  463. return $this;
  464. }
  465. /**
  466. * @param string $sActionName
  467. * @param mixed $mCallback
  468. *
  469. * @return \RainLoop\Plugins\Manager
  470. */
  471. public function AddAdditionalAjaxAction($sActionName, $mCallback)
  472. {
  473. if ($this->bIsEnabled && \is_callable($mCallback) && 0 < \strlen($sActionName))
  474. {
  475. $sActionName = 'DoPlugin'.$sActionName;
  476. if (!isset($this->aAdditionalAjax[$sActionName]))
  477. {
  478. $this->aAdditionalAjax[$sActionName] = $mCallback;
  479. }
  480. }
  481. return $this;
  482. }
  483. /**
  484. * @param string $sActionName
  485. *
  486. * @return bool
  487. */
  488. public function HasAdditionalAjax($sActionName)
  489. {
  490. return $this->bIsEnabled && isset($this->aAdditionalAjax[$sActionName]);
  491. }
  492. /**
  493. * @param string $sActionName
  494. *
  495. * @return mixed
  496. */
  497. public function RunAdditionalAjax($sActionName)
  498. {
  499. if ($this->bIsEnabled)
  500. {
  501. if (isset($this->aAdditionalAjax[$sActionName]))
  502. {
  503. return \call_user_func($this->aAdditionalAjax[$sActionName]);
  504. }
  505. }
  506. return false;
  507. }
  508. /**
  509. * @param string $sFunctionName
  510. * @param mixed $mData
  511. *
  512. * @return mixed
  513. */
  514. public function AjaxResponseHelper($sFunctionName, $mData)
  515. {
  516. return $this->oActions->DefaultResponse($sFunctionName, $mData);
  517. }
  518. /**
  519. * @param string $sPluginName
  520. *
  521. * @return array
  522. */
  523. public function GetUserPluginSettings($sPluginName)
  524. {
  525. $oAccount = $this->oActions->GetAccount();
  526. if ($oAccount)
  527. {
  528. $oSettings = $this->oActions->SettingsProvider()->Load($oAccount);
  529. if ($oSettings)
  530. {
  531. $aData = $oSettings->GetConf('Plugins', array());
  532. if (isset($aData[$sPluginName]) && \is_array($aData[$sPluginName]))
  533. {
  534. return $aData[$sPluginName];
  535. }
  536. }
  537. }
  538. return array();
  539. }
  540. /**
  541. * @param string $sPluginName
  542. * @param array $aSettings
  543. *
  544. * @return bool
  545. */
  546. public function SaveUserPluginSettings($sPluginName, $aSettings)
  547. {
  548. $oAccount = $this->oActions->GetAccount();
  549. if ($oAccount && \is_array($aSettings))
  550. {
  551. $oSettings = $this->oActions->SettingsProvider()->Load($oAccount);
  552. if ($oSettings)
  553. {
  554. $aData = $oSettings->GetConf('Plugins', array());
  555. if (!\is_array($aData))
  556. {
  557. $aData = array();
  558. }
  559. $aPluginSettings = array();
  560. if (isset($aData[$sPluginName]) && \is_array($aData[$sPluginName]))
  561. {
  562. $aPluginSettings = $aData[$sPluginName];
  563. }
  564. foreach ($aSettings as $sKey => $mValue)
  565. {
  566. $aPluginSettings[$sKey] = $mValue;
  567. }
  568. $aData[$sPluginName] = $aPluginSettings;
  569. $oSettings->SetConf('Plugins',$aData);
  570. return $this->oActions->SettingsProvider()->Save($oAccount, $oSettings);
  571. }
  572. }
  573. return false;
  574. }
  575. /**
  576. * @param string $sLang
  577. * @param array $sActionName
  578. *
  579. * @return \RainLoop\Plugins\Manager
  580. */
  581. public function ReadLang($sLang, &$aLang)
  582. {
  583. if ($this->bIsEnabled)
  584. {
  585. foreach ($this->aPlugins as $oPlugin)
  586. {
  587. if ($oPlugin->UseLangs())
  588. {
  589. $sPath = $oPlugin->Path();
  590. \RainLoop\Utils::ReadAndAddLang($sPath.'/langs/en.ini', $aLang);
  591. if ('en' !== $sLang)
  592. {
  593. \RainLoop\Utils::ReadAndAddLang($sPath.'/langs/'.$sLang.'.ini', $aLang);
  594. }
  595. }
  596. }
  597. }
  598. return $this;
  599. }
  600. /**
  601. * @param string $sName
  602. * @param string $sHtml
  603. *
  604. * @return string
  605. */
  606. public function ProcessTemplate($sName, $sHtml)
  607. {
  608. if (isset($this->aProcessTemplate[$sName]))
  609. {
  610. foreach ($this->aProcessTemplate[$sName] as $sPlace => $aAddHtml)
  611. {
  612. if (\is_array($aAddHtml) && 0 < \count($aAddHtml))
  613. {
  614. foreach ($aAddHtml as $sAddHtml)
  615. {
  616. $sHtml = \str_replace('{{INCLUDE/'.$sPlace.'/PLACE}}', $sAddHtml.'{{INCLUDE/'.$sPlace.'/PLACE}}', $sHtml);
  617. }
  618. }
  619. }
  620. }
  621. return $sHtml;
  622. }
  623. /**
  624. * @return bool
  625. */
  626. public function bIsEnabled()
  627. {
  628. return $this->bIsEnabled;
  629. }
  630. /**
  631. * @return int
  632. */
  633. public function Count()
  634. {
  635. return $this->bIsEnabled ? \count($this->aPlugins) : 0;
  636. }
  637. /**
  638. * @param \MailSo\Log\Logger $oLogger
  639. *
  640. * @return \RainLoop\Plugins\Manager
  641. *
  642. * @throws \MailSo\Base\Exceptions\InvalidArgumentException
  643. */
  644. public function SetLogger($oLogger)
  645. {
  646. if (!($oLogger instanceof \MailSo\Log\Logger))
  647. {
  648. throw new \MailSo\Base\Exceptions\InvalidArgumentException();
  649. }
  650. $this->oLogger = $oLogger;
  651. return $this;
  652. }
  653. /**
  654. * @param string $sDesc
  655. * @param int $iType = \MailSo\Log\Enumerations\Type::INFO
  656. *
  657. * @return void
  658. */
  659. public function WriteLog($sDesc, $iType = \MailSo\Log\Enumerations\Type::INFO)
  660. {
  661. if ($this->oLogger)
  662. {
  663. $this->oLogger->Write($sDesc, $iType, 'PLUGIN');
  664. }
  665. }
  666. /**
  667. * @param string $sDesc
  668. * @param int $iType = \MailSo\Log\Enumerations\Type::INFO
  669. *
  670. * @return void
  671. */
  672. public function WriteException($sDesc, $iType = \MailSo\Log\Enumerations\Type::INFO)
  673. {
  674. if ($this->oLogger)
  675. {
  676. $this->oLogger->WriteException($sDesc, $iType, 'PLUGIN');
  677. }
  678. }
  679. }