PageRenderTime 76ms CodeModel.GetById 21ms RepoModel.GetById 5ms app.codeStats 0ms

/app/protected/extensions/zurmoinc/framework/modules/Module.php

https://bitbucket.org/zurmo/zurmo/
PHP | 535 lines | 344 code | 38 blank | 153 comment | 30 complexity | 6141a3b283329b653fde7d14521efab9 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, GPL-2.0, LGPL-3.0, LGPL-2.1, BSD-2-Clause
  1. <?php
  2. /*********************************************************************************
  3. * Zurmo is a customer relationship management program developed by
  4. * Zurmo, Inc. Copyright (C) 2012 Zurmo Inc.
  5. *
  6. * Zurmo is free software; you can redistribute it and/or modify it under
  7. * the terms of the GNU General Public License version 3 as published by the
  8. * Free Software Foundation with the addition of the following permission added
  9. * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
  10. * IN WHICH THE COPYRIGHT IS OWNED BY ZURMO, ZURMO DISCLAIMS THE WARRANTY
  11. * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
  12. *
  13. * Zurmo is distributed in the hope that it will be useful, but WITHOUT
  14. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  15. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  16. * details.
  17. *
  18. * You should have received a copy of the GNU General Public License along with
  19. * this program; if not, see http://www.gnu.org/licenses or write to the Free
  20. * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  21. * 02110-1301 USA.
  22. *
  23. * You can contact Zurmo, Inc. with a mailing address at 113 McHenry Road Suite 207,
  24. * Buffalo Grove, IL 60089, USA. or at email address contact@zurmo.com.
  25. ********************************************************************************/
  26. /**
  27. * TODO
  28. */
  29. abstract class Module extends CWebModule
  30. {
  31. private $isEnabled = true;
  32. /**
  33. * Returns an array of module objects, keyed by module id.
  34. * Should be called getModules, but the badly named Yii
  35. * method getModules(), which whould be called something
  36. * like getModuleConfigurations(), is in the way.
  37. * @see getModuleNames()
  38. */
  39. public static function getModuleObjects()
  40. {
  41. $moduleConfig = Yii::app()->getModules();
  42. $modules = array();
  43. foreach ($moduleConfig as $moduleName => $info)
  44. {
  45. $module = Yii::app()->findModule($moduleName);
  46. if (isset($info['modules']) && is_array($info['modules']))
  47. {
  48. foreach ($info['modules'] as $nestedModuleName => $nestedInfo)
  49. {
  50. $modules[$nestedModuleName] = $module->getModule($nestedModuleName);
  51. }
  52. }
  53. $modules[$moduleName] = $module;
  54. }
  55. return $modules;
  56. }
  57. /**
  58. * Returns an array which is the flattened dependencies
  59. * for given module.
  60. */
  61. public static function getDependenciesForModule(Module $module, $dependencies = array())
  62. {
  63. assert('$module !== null');
  64. $dependencies = array_merge(array($module->getName()), $dependencies);
  65. $dependencyNames = $module->getDependencies();
  66. foreach ($dependencyNames as $dependencyName)
  67. {
  68. if (!in_array($dependencyName, $dependencies))
  69. {
  70. $dependentModule = Yii::app()->findModule($dependencyName);
  71. assert('$dependentModule instanceof Module');
  72. $dependencies = array_merge(self::getDependenciesForModule($dependentModule, $dependencies),
  73. $dependencies);
  74. }
  75. }
  76. return array_values(array_unique($dependencies));
  77. }
  78. /**
  79. * @returns the name of this module, which is the
  80. * name of the module's directory under protected/modules.
  81. */
  82. public function getName()
  83. {
  84. $calledClassName = get_called_class();
  85. return $calledClassName::getDirectoryName();
  86. }
  87. /**
  88. * @returns the name of this module, which is the
  89. * name of the module's directory under protected/modules.
  90. * Same method as getName, but getName cannot be static.
  91. * @see Module::getName
  92. */
  93. public static function getDirectoryName()
  94. {
  95. $name = get_called_class();
  96. $name = substr($name, 0, strlen($name) - strlen('Module'));
  97. $name = lcfirst($name);
  98. return $name;
  99. }
  100. /**
  101. * @returns the singular name of the module for example 'Account'
  102. * keeping the uppercase letters. Override if the module's pluralized
  103. * name cannot be changed to singular by simply removing the end 's'.
  104. */
  105. public static function getSingularCamelCasedName()
  106. {
  107. $name = get_called_class();
  108. $name = substr($name, 0, strlen($name) - strlen('Module') - 1);
  109. return $name;
  110. }
  111. /**
  112. * @returns the plural name of the module for example 'Accounts'
  113. * keeping the uppercase letters.
  114. */
  115. public static function getPluralCamelCasedName()
  116. {
  117. $name = get_called_class();
  118. $name = substr($name, 0, strlen($name) - strlen('Module'));
  119. return $name;
  120. }
  121. /**
  122. * @return string - singular module label.
  123. */
  124. protected static function getSingularModuleLabel()
  125. {
  126. $name = static::getPluralModuleLabel();
  127. $name = substr($name, 0, strlen($name) - 1);
  128. return $name;
  129. }
  130. /**
  131. * Gets the modules display label which is its name
  132. * with spaces in title case. Can be overridden
  133. * if the automatically created name is not appropriate.
  134. */
  135. protected static function getPluralModuleLabel()
  136. {
  137. $calledClassName = get_called_class();
  138. $name = $calledClassName::getDirectoryName();
  139. $name = preg_replace('/([A-Z])/', ' \1', $name);
  140. return ucfirst($name);
  141. }
  142. public static function getModuleLabelByTypeAndLanguage($type, $language = null)
  143. {
  144. assert('in_array($type, array("Singular", "SingularLowerCase", "Plural", "PluralLowerCase"))');
  145. assert('$language == null || is_string($language)');
  146. if ($language == null)
  147. {
  148. $language = Yii::app()->language;
  149. }
  150. $label = self::getCustomModuleLabelByTypeAndLanguage($type, $language);
  151. if ($label!= null)
  152. {
  153. return $label;
  154. }
  155. switch ($type)
  156. {
  157. case 'Singular':
  158. return Yii::t('Default', static::getSingularModuleLabel());
  159. case 'SingularLowerCase':
  160. $string = Yii::t('Default', static::getSingularModuleLabel(), array(), null, $language);
  161. return TextUtil::strToLowerWithDefaultEncoding($string);
  162. case 'Plural':
  163. return Yii::t('Default', static::getPluralModuleLabel(), array(), null, $language);
  164. case 'PluralLowerCase':
  165. $string = Yii::t('Default', static::getPluralModuleLabel(), array(), null, $language);
  166. return TextUtil::strToLowerWithDefaultEncoding($string);
  167. }
  168. }
  169. protected static function getCustomModuleLabelByTypeAndLanguage($type, $language)
  170. {
  171. assert('in_array($type, array("Singular", "SingularLowerCase", "Plural", "PluralLowerCase"))');
  172. assert('$language != null');
  173. $metadata = static::getMetadata();
  174. switch ($type)
  175. {
  176. case 'Singular':
  177. if (isset($metadata['global']['singularModuleLabels']) &&
  178. isset($metadata['global']['singularModuleLabels'][$language]))
  179. {
  180. return ucwords($metadata['global']['singularModuleLabels'][$language]);
  181. }
  182. case 'SingularLowerCase':
  183. if ( isset($metadata['global']['singularModuleLabels']) &&
  184. isset($metadata['global']['singularModuleLabels'][$language]))
  185. {
  186. return $metadata['global']['singularModuleLabels'][$language];
  187. }
  188. case 'Plural':
  189. if ( isset($metadata['global']['pluralModuleLabels']) &&
  190. isset($metadata['global']['pluralModuleLabels'][$language]))
  191. {
  192. return ucwords($metadata['global']['pluralModuleLabels'][$language]);
  193. }
  194. case 'PluralLowerCase':
  195. if ( isset($metadata['global']['pluralModuleLabels']) &&
  196. isset($metadata['global']['pluralModuleLabels'][$language]))
  197. {
  198. return $metadata['global']['pluralModuleLabels'][$language];
  199. }
  200. }
  201. }
  202. /**
  203. * Returns whether the module is enabled.
  204. */
  205. public function isEnabled()
  206. {
  207. return $this->isEnabled;
  208. }
  209. /**
  210. * Returns whether the module is can be disabled. Modules that
  211. * must not be disabled must override and return false.
  212. */
  213. public function canDisable()
  214. {
  215. return true;
  216. }
  217. /**
  218. * If setting isEnabled = true then dependent modules which are
  219. * not enabled will be recursively enabled. If setting
  220. * isEnabled = false only this module will be disabled.
  221. */
  222. public function setIsEnabled($isEnabled)
  223. {
  224. assert('is_bool($isEnabled)');
  225. if (!$isEnabled && !$this->canDisable())
  226. {
  227. throw NotSupportedException();
  228. }
  229. if ($isEnabled)
  230. {
  231. $modules = Module::GetModuleObjects();
  232. $dependencies = $this->getDependencies();
  233. foreach ($dependencies as $dependency)
  234. {
  235. assert('array_key_exists($dependency, $modules)');
  236. $modules[$dependency]->setIsEnabled($isEnabled);
  237. }
  238. }
  239. $this->isEnabled = $isEnabled;
  240. assert('!$this->isEnabled || $this->isEnabled && $this->dependenciesAreEnabled()');
  241. }
  242. /**
  243. * Returns an array of the names of the the
  244. * modules the module depends on. ie: if the module
  245. * is enabled then those modules must be too, recursively
  246. * through their dependencies.
  247. */
  248. public abstract function getDependencies();
  249. /**
  250. * Returns an array of the dependency modules that are disabled.
  251. */
  252. public function getEnabledDependencies($temp = null)
  253. {
  254. if ($temp === null) // TODO - remove this $temp junk when the modules metadata is being saved.
  255. {
  256. $temp = self::getModuleObjects();
  257. }
  258. return $this->getEnabledDependenciesInternal($temp);
  259. }
  260. // The public version gets the modules once, then
  261. // the private version can use it recursively.
  262. private function getEnabledDependenciesInternal($modules)
  263. {
  264. $unsatisfiedDependencies = array();
  265. $dependencies = $this->getDependencies();
  266. foreach ($dependencies as $dependency)
  267. {
  268. assert('array_key_exists($dependency, $modules)');
  269. if ($modules[$dependency]->isEnabled())
  270. {
  271. $unsatisfiedDependencies[] = $dependency;
  272. }
  273. array_merge($unsatisfiedDependencies, $modules[$dependency]->getEnabledDependencies($modules));
  274. }
  275. return $unsatisfiedDependencies;
  276. }
  277. public function getRootModelNamesIncludingDependencies()
  278. {
  279. $rootModels = $this->getRootModelNames();
  280. foreach ($this->getEnabledDependencies() as $dependencyName)
  281. {
  282. $module = Yii::app()->getModule($dependencyName);
  283. $dependencyModulesRootModels = $module->getRootModelNamesIncludingDependencies();
  284. $rootModels = array_merge($rootModels, array_diff($dependencyModulesRootModels, $rootModels));
  285. }
  286. return $rootModels;
  287. }
  288. /**
  289. * Implement in all modules that have models. The root models are the
  290. * models that if they are created, then their related models are
  291. * created, and so on recursively, then all of module's models will
  292. * have been created, allowing RedBean to create all of the required
  293. * tables and columns.
  294. */
  295. public function getRootModelNames()
  296. {
  297. return array();
  298. }
  299. /*
  300. * Returns the stronger of the two policy values, being the more
  301. * restrictive given the nature of that specific policy.
  302. */
  303. public static function getStrongerPolicy($policyName, array $values)
  304. {
  305. throw NotSupportedException();
  306. }
  307. /**
  308. * @return array of Policy / PolicyRulesType pairings
  309. */
  310. public static function getPolicyRulesTypes()
  311. {
  312. return array();
  313. }
  314. /**
  315. * TODO
  316. */
  317. public function getConfigurationView()
  318. {
  319. return new ConfigurationView();
  320. }
  321. /**
  322. * TODO
  323. */
  324. public static function getTabMenuItems($user = null)
  325. {
  326. assert('$user == null || $user instanceof User');
  327. $metadata = self::getMetadata();
  328. if (!empty($metadata['global']['tabMenuItems']))
  329. {
  330. return $metadata['global']['tabMenuItems'];
  331. }
  332. return array();
  333. }
  334. /**
  335. * TODO
  336. */
  337. public static function getConfigureMenuItems()
  338. {
  339. $metadata = self::getMetadata();
  340. if (!empty($metadata['global']['configureMenuItems']))
  341. {
  342. return $metadata['global']['configureMenuItems'];
  343. }
  344. return array();
  345. }
  346. public static function getShortCutsMenuItems()
  347. {
  348. $calledClass = get_called_class();
  349. $metadata = $calledClass::getMetadata();
  350. if (!empty($metadata['global']['shortcutsMenuItems']))
  351. {
  352. return $metadata['global']['shortcutsMenuItems'];
  353. }
  354. return array();
  355. }
  356. public function getDesignerMenuItems()
  357. {
  358. $metadata = $this->getMetadata();
  359. if (!empty($metadata['global']['designerMenuItems']))
  360. {
  361. return $metadata['global']['designerMenuItems'];
  362. }
  363. return array();
  364. }
  365. /**
  366. * Get the primary model name for a module.
  367. * Make sure to override in modules that have
  368. * a Primary model otherwise the method is not
  369. * supported.
  370. */
  371. public static function getPrimaryModelName()
  372. {
  373. throw new NotSupportedException();
  374. }
  375. /**
  376. * Override and return a string if the module supports the global search mechanism.
  377. * @return null if not supported otherwise return the appropriate string.
  378. */
  379. public static function getGlobalSearchFormClassName()
  380. {
  381. return null;
  382. }
  383. /**
  384. * Override and return a string of the StatemetadataAdataper class if the module's primary model supports
  385. * states. An example is leads or contacts where the lead is only contacts in a certain state.
  386. */
  387. public static function getStateMetadataAdapterClassName()
  388. {
  389. return null;
  390. }
  391. /**
  392. * Returns metadata for the module.
  393. * Does caching only if the user is not specified. This can potentially be changed to cache when the user is
  394. * specified but must be investigated further before doing this.
  395. * @see getDefaultMetadata()
  396. * @param $user The current user.
  397. * @returns An array of metadata.
  398. */
  399. public static function getMetadata(User $user = null)
  400. {
  401. $className = get_called_class();
  402. if ($user == null)
  403. {
  404. try
  405. {
  406. return GeneralCache::getEntry($className . 'Metadata');
  407. }
  408. catch (NotFoundException $e)
  409. {
  410. }
  411. }
  412. $metadata = MetadataUtil::getMetadata($className, $user);
  413. if (YII_DEBUG)
  414. {
  415. $className::assertMetadataIsValid($metadata);
  416. }
  417. if ($user == null)
  418. {
  419. GeneralCache::cacheEntry($className . 'Metadata', $metadata);
  420. }
  421. return $metadata;
  422. }
  423. /**
  424. * Sets new metadata.
  425. * @param $metadata An array of metadata.
  426. * @param $user The current user.
  427. */
  428. public static function setMetadata(array $metadata, User $user = null)
  429. {
  430. $className = get_called_class();
  431. if (YII_DEBUG)
  432. {
  433. self::assertMetadataIsValid($metadata);
  434. }
  435. MetadataUtil::setMetadata($className, $metadata, $user);
  436. if ($user == null)
  437. {
  438. GeneralCache::forgetEntry($className . 'Metadata');
  439. }
  440. }
  441. /**
  442. * Returns the default meta data for the class.
  443. * It must be appended to the meta data
  444. * from the parent model, if any.
  445. */
  446. public static function getDefaultMetadata()
  447. {
  448. return array();
  449. }
  450. public static function getViewClassNames()
  451. {
  452. return static::getAllClassNamesByPathFolder('views');
  453. }
  454. /**
  455. * For a given module, return an array of models that are in that module.
  456. */
  457. public static function getModelClassNames()
  458. {
  459. return static::getAllClassNamesByPathFolder('models');
  460. }
  461. public static function getAllClassNamesByPathFolder($folder)
  462. {
  463. assert('is_string($folder)');
  464. $classNames = array();
  465. $className = get_called_class();
  466. $pathOfAlias = Yii::getPathOfAlias(
  467. 'application.modules.' . $className::getDirectoryName() . '.' . $folder . '.*');
  468. if (is_dir($pathOfAlias))
  469. {
  470. $directoryFiles = ZurmoFileHelper::findFiles($pathOfAlias);
  471. $classNames = array();
  472. foreach ($directoryFiles as $filePath)
  473. {
  474. $filePathInfo = pathinfo($filePath);
  475. $classNames[] = $filePathInfo['filename'];
  476. }
  477. }
  478. return $classNames;
  479. }
  480. private static function assertMetadataIsValid(array $metadata)
  481. {
  482. //add checks as needed.
  483. }
  484. /**
  485. * Override in modules that create default data during an installation.
  486. */
  487. public static function getDefaultDataMakerClassName()
  488. {
  489. }
  490. /**
  491. * Override in modules that create demo data during an installation.
  492. */
  493. public static function getDemoDataMakerClassName()
  494. {
  495. }
  496. }
  497. ?>