PageRenderTime 53ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/src/legacy/util/ModUtil.php

https://github.com/antoniom/core
PHP | 1474 lines | 791 code | 184 blank | 499 comment | 218 complexity | 33d876a710799309416db8fb1e558b37 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-3.0, MIT

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * Copyright Zikula Foundation 2009 - Zikula Application Framework
  4. *
  5. * This work is contributed to the Zikula Foundation under one or more
  6. * Contributor Agreements and licensed to You under the following license:
  7. *
  8. * @license GNU/LGPLv3 (or at your option, any later version).
  9. * @package Util
  10. *
  11. * Please see the NOTICE file distributed with this source code for further
  12. * information regarding copyright and licensing.
  13. */
  14. use Zikula\Core\Event\GenericEvent;
  15. /**
  16. * Module Util.
  17. */
  18. class ModUtil
  19. {
  20. // States
  21. const STATE_UNINITIALISED = 1;
  22. const STATE_INACTIVE = 2;
  23. const STATE_ACTIVE = 3;
  24. const STATE_MISSING = 4;
  25. const STATE_UPGRADED = 5;
  26. const STATE_NOTALLOWED = 6;
  27. const STATE_INVALID = -1;
  28. const CONFIG_MODULE = 'ZConfig';
  29. // Types
  30. const TYPE_MODULE = 2;
  31. const TYPE_SYSTEM = 3;
  32. const TYPE_CORE = 4;
  33. // Module dependency states
  34. const DEPENDENCY_REQUIRED = 1;
  35. const DEPENDENCY_RECOMMENDED = 2;
  36. const DEPENDENCY_CONFLICTS = 3;
  37. /**
  38. * Memory of object oriented modules.
  39. *
  40. * @var array
  41. */
  42. protected static $ooModules = array();
  43. /**
  44. * Module info cache.
  45. *
  46. * @var array
  47. */
  48. protected static $modinfo;
  49. /**
  50. * Module vars.
  51. *
  52. * @var ArrayObject
  53. */
  54. protected static $modvars = array();
  55. /**
  56. * Internal module cache.
  57. *
  58. * @var array
  59. */
  60. protected static $cache = array();
  61. /**
  62. * Module variables getter.
  63. *
  64. * @return ArrayObject
  65. */
  66. public static function getModvars()
  67. {
  68. return self::$modvars;
  69. }
  70. /**
  71. * Flush this static class' cache.
  72. *
  73. * @return void
  74. */
  75. public static function flushCache()
  76. {
  77. self::$cache = array();
  78. }
  79. /**
  80. * The initCoreVars preloads some module vars.
  81. *
  82. * Preloads module vars for a number of key modules to reduce sql statements.
  83. *
  84. * @return void
  85. */
  86. public static function initCoreVars($force=false)
  87. {
  88. // The empty arrays for handlers and settings are required to prevent messages with E_ALL error reporting
  89. self::$modvars = new ArrayObject(array(
  90. EventUtil::HANDLERS => array(),
  91. ServiceUtil::HANDLERS => array(),
  92. 'Settings' => array(),
  93. ));
  94. // don't init vars during the installer or upgrader
  95. if (!$force && System::isInstalling()) {
  96. return;
  97. }
  98. // This loads all module variables into the modvars static class variable.
  99. $em = ServiceUtil::get('doctrine')->getEntityManager();
  100. $modvars = $em->getRepository('Zikula\Core\Doctrine\Entity\ExtensionVar')->findAll();
  101. foreach ($modvars as $var) {
  102. if (!array_key_exists($var['modname'], self::$modvars)) {
  103. self::$modvars[$var['modname']] = array();
  104. }
  105. if (array_key_exists($var['name'], $GLOBALS['ZConfig']['System'])) {
  106. self::$modvars[$var['modname']][$var['name']] = $GLOBALS['ZConfig']['System'][$var['name']];
  107. } else {
  108. self::$modvars[$var['modname']][$var['name']] = $var['value'];
  109. }
  110. }
  111. // Pre-load the module variables array with empty arrays for known modules that
  112. // do not define any module variables to prevent unnecessary SQL queries to
  113. // the module_vars table.
  114. $knownModules = self::getAllMods();
  115. foreach ($knownModules as $key => $mod) {
  116. if (!array_key_exists($mod['name'], self::$modvars)) {
  117. self::$modvars[$mod['name']] = array();
  118. }
  119. }
  120. }
  121. /**
  122. * Checks to see if a module variable is set.
  123. *
  124. * @param string $modname The name of the module.
  125. * @param string $name The name of the variable.
  126. *
  127. * @return boolean True if the variable exists in the database, false if not.
  128. */
  129. public static function hasVar($modname, $name)
  130. {
  131. // define input, all numbers and booleans to strings
  132. if ('ZConfig' !== $modname) {
  133. $modname = preg_match('/\w+Module$/', $modname) || !$modname ? $modname : $modname.'Module';
  134. }
  135. $modname = isset($modname) ? ((string)$modname) : '';
  136. $name = isset($name) ? ((string)$name) : '';
  137. // make sure we have the necessary parameters
  138. if (!System::varValidate($modname, 'mod') || !System::varValidate($name, 'modvar')) {
  139. return false;
  140. }
  141. // The cast to (array) is for the odd instance where self::$modvars[$modname] is set to null--not sure if this is really needed.
  142. $varExists = isset(self::$modvars[$modname]) && array_key_exists($name, (array)self::$modvars[$modname]);
  143. if (!$varExists && System::isUpgrading()) {
  144. // Handle the upgrade edge case--the call to getVar() ensures vars for the module are loaded if newly available.
  145. $modvars = self::getVar($modname);
  146. $varExists = array_key_exists($name, (array)$modvars);
  147. }
  148. return $varExists;
  149. }
  150. /**
  151. * The getVar method gets a module variable.
  152. *
  153. * If the name parameter is included then method returns the
  154. * module variable value.
  155. * if the name parameter is ommitted then method returns a multi
  156. * dimentional array of the keys and values for the module vars.
  157. *
  158. * @param string $modname The name of the module or pseudo-module (e.g., 'Users', 'ZConfig', '/EventHandlers').
  159. * @param string $name The name of the variable.
  160. * @param boolean $default The value to return if the requested modvar is not set.
  161. *
  162. * @return string|array If the name parameter is included then method returns
  163. * string - module variable value
  164. * if the name parameter is ommitted then method returns
  165. * array - multi dimentional array of the keys
  166. * and values for the module vars.
  167. */
  168. public static function getVar($modname, $name = '', $default = false)
  169. {
  170. // if we don't know the modname then lets assume it is the current
  171. // active module
  172. if (!isset($modname)) {
  173. $modname = self::getName();
  174. }
  175. if ('ZConfig' !== $modname) {
  176. $modname = preg_match('/\w+Module$/', $modname) || !$modname ? $modname : $modname.'Module';
  177. }
  178. // if we haven't got vars for this module (or pseudo-module) yet then lets get them
  179. if (!array_key_exists($modname, self::$modvars)) {
  180. // A query out to the database should only be needed if the system is upgrading. Use the installing flag to determine this.
  181. // Prevent a re-query for the same module in the future, where the module does not define any module variables.
  182. self::$modvars[$modname] = array();
  183. }
  184. // if they didn't pass a variable name then return every variable
  185. // for the specified module as an associative array.
  186. // array('var1' => value1, 'var2' => value2)
  187. if (empty($name) && array_key_exists($modname, self::$modvars)) {
  188. return self::$modvars[$modname];
  189. }
  190. // since they passed a variable name then only return the value for
  191. // that variable
  192. if (isset(self::$modvars[$modname]) && array_key_exists($name, self::$modvars[$modname])) {
  193. return self::$modvars[$modname][$name];
  194. }
  195. // we don't know the required module var but we established all known
  196. // module vars for this module so the requested one can't exist.
  197. // we return the default (which itself defaults to false)
  198. return $default;
  199. }
  200. /**
  201. * The setVar method sets a module variable.
  202. *
  203. * @param string $modname The name of the module.
  204. * @param string $name The name of the variable.
  205. * @param string $value The value of the variable.
  206. *
  207. * @return boolean True if successful, false otherwise.
  208. */
  209. public static function setVar($modname, $name, $value = '')
  210. {
  211. // define input, all numbers and booleans to strings
  212. if ('ZConfig' !== $modname) {
  213. $modname = preg_match('/\w+Module$/', $modname) || !$modname ? $modname : $modname.'Module';
  214. }
  215. $modname = isset($modname) ? ((string)$modname) : '';
  216. // validate
  217. if (!System::varValidate($modname, 'mod') || !isset($name)) {
  218. return false;
  219. }
  220. $em = ServiceUtil::get('doctrine')->getEntityManager();
  221. if (self::hasVar($modname, $name)) {
  222. $entity = $em->getRepository('Zikula\Core\Doctrine\Entity\ExtensionVar')->findOneBy(array('modname' => $modname, 'name' => $name));
  223. $entity->setValue($value);
  224. } else {
  225. $entity = new \Zikula\Core\Doctrine\Entity\ExtensionVar();
  226. $entity->setModname($modname);
  227. $entity->setName($name);
  228. $entity->setValue($value);
  229. $em->persist($entity);
  230. }
  231. self::$modvars[$modname][$name] = $value;
  232. $em->flush();
  233. return true;
  234. }
  235. /**
  236. * The setVars method sets multiple module variables.
  237. *
  238. * @param string $modname The name of the module.
  239. * @param array $vars An associative array of varnames/varvalues.
  240. *
  241. * @return boolean True if successful, false otherwise.
  242. */
  243. public static function setVars($modname, array $vars)
  244. {
  245. $ok = true;
  246. foreach ($vars as $var => $value) {
  247. $ok = $ok && self::setVar($modname, $var, $value);
  248. }
  249. return $ok;
  250. }
  251. /**
  252. * The delVar method deletes a module variable.
  253. *
  254. * Delete a module variables. If the optional name parameter is not supplied all variables
  255. * for the module 'modname' are deleted.
  256. *
  257. * @param string $modname The name of the module.
  258. * @param string $name The name of the variable (optional).
  259. *
  260. * @return boolean True if successful, false otherwise.
  261. */
  262. public static function delVar($modname, $name = '')
  263. {
  264. // define input, all numbers and booleans to strings
  265. if ('ZConfig' !== $modname) {
  266. $modname = preg_match('/\w+Module$/', $modname) || !$modname ? $modname : $modname.'Module';
  267. }
  268. $modname = isset($modname) ? ((string)$modname) : '';
  269. // validate
  270. if (!System::varValidate($modname, 'modvar')) {
  271. return false;
  272. }
  273. $val = null;
  274. if (!isset(self::$modvars[$modname])) {
  275. return $val;
  276. }
  277. if (empty($name)) {
  278. if (array_key_exists($modname, self::$modvars)) {
  279. unset(self::$modvars[$modname]);
  280. }
  281. } else {
  282. if (array_key_exists($name, self::$modvars[$modname])) {
  283. $val = self::$modvars[$modname][$name];
  284. // we're dealing with an ArrayObject, so we cannot unset() deep keys.
  285. $array = self::$modvars[$modname];
  286. unset($array[$name]);
  287. self::$modvars[$modname] = $array;
  288. }
  289. }
  290. $em = ServiceUtil::get('doctrine')->getEntityManager();
  291. // if $name is not provided, delete all variables of this module
  292. // else just delete this specific variable
  293. if (empty($name)) {
  294. $dql = "DELETE FROM Zikula\Core\Doctrine\Entity\ExtensionVar v WHERE v.modname = '{$modname}'";
  295. } else {
  296. $dql = "DELETE FROM Zikula\Core\Doctrine\Entity\ExtensionVar v WHERE v.modname = '{$modname}' AND v.name = '{$name}'";
  297. }
  298. $query = $em->createQuery($dql);
  299. $result = $query->getResult();
  300. return (boolean)$result;
  301. }
  302. /**
  303. * Get Module meta info.
  304. *
  305. * @param string $module Module name.
  306. *
  307. * @return array|boolean Module information array or false.
  308. */
  309. public static function getInfoFromName($module)
  310. {
  311. return self::getInfo(self::getIdFromName($module));
  312. }
  313. /**
  314. * The getIdFromName method gets module ID given its name.
  315. *
  316. * @param string $module The name of the module.
  317. *
  318. * @return integer module ID.
  319. */
  320. public static function getIdFromName($module)
  321. {
  322. // define input, all numbers and booleans to strings
  323. $alias = (isset($module) ? strtolower((string)$module) : '');
  324. $module = preg_match('/\w+Module$/i', $module) || !$module ? $module : $module.'Module';
  325. $module = (isset($module) ? strtolower((string)$module) : '');
  326. // validate
  327. if (!System::varValidate($module, 'mod')) {
  328. return false;
  329. }
  330. if (!isset(self::$cache['modid'])) {
  331. self::$cache['modid'] = null;
  332. }
  333. if (!is_array(self::$cache['modid']) || System::isInstalling()) {
  334. $modules = self::getModsTable();
  335. if ($modules === false) {
  336. return false;
  337. }
  338. foreach ($modules as $id => $mod) {
  339. $mName = strtolower($mod['name']);
  340. self::$cache['modid'][$mName] = $mod['id'];
  341. if (!$id == 0) {
  342. $mdName = strtolower($mod['url']);
  343. self::$cache['modid'][$mdName] = $mod['id'];
  344. }
  345. }
  346. if (!isset(self::$cache['modid'][$module]) && !isset(self::$cache['modid'][$alias])) {
  347. self::$cache['modid'][$module] = false;
  348. return false;
  349. }
  350. }
  351. if (isset(self::$cache['modid'][$module])) {
  352. return self::$cache['modid'][$module];
  353. }
  354. if (isset(self::$cache['modid'][$alias])) {
  355. return self::$cache['modid'][$alias];
  356. }
  357. return false;
  358. }
  359. /**
  360. * The getInfo method gets information on module.
  361. *
  362. * Return array of module information or false if core ( id = 0 ).
  363. *
  364. * @param integer $modid The module ID.
  365. *
  366. * @return array|boolean Module information array or false.
  367. */
  368. public static function getInfo($modid = 0)
  369. {
  370. // a $modid of 0 is associated with the core ( blocks.mid, ... ).
  371. if (!is_numeric($modid)) {
  372. return false;
  373. }
  374. if (!is_array(self::$modinfo) || System::isInstalling()) {
  375. self::$modinfo = self::getModsTable();
  376. if (!self::$modinfo) {
  377. return null;
  378. }
  379. if (!isset(self::$modinfo[$modid])) {
  380. self::$modinfo[$modid] = false;
  381. return self::$modinfo[$modid];
  382. }
  383. }
  384. if (isset(self::$modinfo[$modid])) {
  385. return self::$modinfo[$modid];
  386. }
  387. return false;
  388. }
  389. /**
  390. * The getModulesCapableOf method gets a list of modules by module type.
  391. *
  392. * @param string $capability The module type to get (either 'user' or 'admin') (optional) (default='user').
  393. *
  394. * @return array An array of module information arrays.
  395. */
  396. public static function getModulesCapableOf($capability = 'user')
  397. {
  398. if (!isset(self::$cache['modcache'])) {
  399. self::$cache['modcache'] = array();
  400. }
  401. if (!isset(self::$cache['modcache'][$capability]) || !self::$cache['modcache'][$capability]) {
  402. self::$cache['modcache'][$capability] = array();
  403. $mods = self::getAllMods();
  404. foreach ($mods as $key => $mod) {
  405. if (isset($mod['capabilities'][$capability])) {
  406. self::$cache['modcache'][$capability][] = $mods[$key];
  407. }
  408. }
  409. }
  410. return self::$cache['modcache'][$capability];
  411. }
  412. /**
  413. * Indicates whether the specified module has the specified capability.
  414. *
  415. * @param string $module The name of the module.
  416. * @param string $capability The name of the advertised capability.
  417. *
  418. * @return boolean True if the specified module advertises that it has the specified capability, otherwise false.
  419. */
  420. public static function isCapable($module, $capability)
  421. {
  422. $modinfo = self::getInfoFromName($module);
  423. if (!$modinfo) {
  424. return false;
  425. }
  426. return (bool)array_key_exists($capability, $modinfo['capabilities']);
  427. }
  428. /**
  429. * Retrieves the capabilities of the specified module.
  430. *
  431. * @param string $module The module name.
  432. *
  433. * @return array|boolean The capabilities array, false if the module does not advertise any capabilities.
  434. */
  435. public static function getCapabilitiesOf($module)
  436. {
  437. $modules = self::getAllMods();
  438. if (array_key_exists($module, $modules)) {
  439. return $modules[$module]['capabilities'];
  440. }
  441. return false;
  442. }
  443. /**
  444. * The getAllMods method gets a list of all modules.
  445. *
  446. * @return array An array of module information arrays.
  447. */
  448. public static function getAllMods()
  449. {
  450. if (!isset(self::$cache['modsarray'])) {
  451. self::$cache['modsarray'] = array();
  452. }
  453. if (empty(self::$cache['modsarray'])) {
  454. $all = self::getModsTable();
  455. foreach ($all as $mod) {
  456. // "Core" modules should be returned in this list
  457. if (($mod['state'] == self::STATE_ACTIVE)
  458. || (preg_match('/^(extensionsmodule|adminmodule|thememodule|blockmodule|groupsmodule|permissionsmodule|usersmodule)$/i', $mod['name'])
  459. && ($mod['state'] == self::STATE_UPGRADED || $mod['state'] == self::STATE_INACTIVE))) {
  460. self::$cache['modsarray'][$mod['name']] = $mod;
  461. }
  462. }
  463. }
  464. return self::$cache['modsarray'];
  465. }
  466. /**
  467. * Loads a module.
  468. *
  469. * @param string $modname The name of the module.
  470. * @param string $type The type of functions to load.
  471. * @param boolean $force Determines to load Module even if module isn't active.
  472. *
  473. * @return string|boolean Name of module loaded, or false on failure.
  474. */
  475. public static function load($modname, $type = 'user', $force = false)
  476. {
  477. if (strtolower(substr($type, -3)) == 'api') {
  478. return false;
  479. }
  480. return self::loadGeneric($modname, $type, $force);
  481. }
  482. /**
  483. * Load an API module.
  484. *
  485. * @param string $modname The name of the module.
  486. * @param string $type The type of functions to load.
  487. * @param boolean $force Determines to load Module even if module isn't active.
  488. *
  489. * @return string|boolean Name of module loaded, or false on failure.
  490. */
  491. public static function loadApi($modname, $type = 'user', $force = false)
  492. {
  493. return self::loadGeneric($modname, $type, $force, true);
  494. }
  495. /**
  496. * Load a module.
  497. *
  498. * This loads/set's up a module. For classic style modules, it tests to see
  499. * if the module type files exist, admin.php, user.php etc and includes them.
  500. * If they do not exist, it will return false.
  501. *
  502. * Loading a module simply means making the functions/methods available
  503. * by loading the files and other tasks like binding any language domain.
  504. *
  505. * For OO style modules this means registering the main module autoloader,
  506. * and binding any language domain.
  507. *
  508. * @param string $modname The name of the module.
  509. * @param string $type The type of functions to load.
  510. * @param boolean $force Determines to load Module even if module isn't active.
  511. * @param boolean $api Whether or not to load an API (or regular) module.
  512. *
  513. * @return string|boolean Name of module loaded, or false on failure.
  514. */
  515. public static function loadGeneric($modname, $type = 'user', $force = false, $api = false)
  516. {
  517. // define input, all numbers and booleans to strings
  518. $osapi = ($api ? 'api' : '');
  519. $modname = preg_match('/\w+Module$/i', $modname) || !$modname ? $modname : $modname.'Module';
  520. $modname = isset($modname) ? ((string)$modname) : '';
  521. $modtype = strtolower("$modname{$type}{$osapi}");
  522. if (!isset(self::$cache['loaded'])) {
  523. self::$cache['loaded'] = array();
  524. }
  525. if (!empty(self::$cache['loaded'][$modtype])) {
  526. // Already loaded from somewhere else
  527. return self::$cache['loaded'][$modtype];
  528. }
  529. // this is essential to call separately and not in the condition below - drak
  530. $available = self::available($modname, $force);
  531. // check the modules state
  532. if (!$force && !$available) {
  533. return false;
  534. }
  535. // get the module info
  536. $modinfo = self::getInfo(self::getIdFromName($modname));
  537. // check for bad System::varValidate($modname)
  538. if (!$modinfo) {
  539. return false;
  540. }
  541. // if class is loadable or has been loaded exit here.
  542. if (self::isInitialized($modname)) {
  543. self::_loadStyleSheets($modname, $api, $type);
  544. return $modname;
  545. }
  546. self::isOO($modname);
  547. self::initOOModule($modname);
  548. self::$cache['loaded'][$modtype] = $modname;
  549. if ($modinfo['type'] == self::TYPE_MODULE) {
  550. ZLanguage::bindModuleDomain($modname);
  551. }
  552. self::_loadStyleSheets($modname, $api, $type);
  553. $event = new GenericEvent(null, array('modinfo' => $modinfo, 'type' => $type, 'force' => $force, 'api' => $api));
  554. EventUtil::dispatch('module_dispatch.postloadgeneric', $event);
  555. return $modname;
  556. }
  557. /**
  558. * Initialise all modules.
  559. *
  560. * @return void
  561. */
  562. public static function loadAll()
  563. {
  564. $modules = self::getModsTable();
  565. unset($modules[0]);
  566. foreach ($modules as $module) {
  567. if (self::available($module['name'])) {
  568. self::loadGeneric($module['name']);
  569. }
  570. }
  571. }
  572. /**
  573. * Add stylesheet to the page vars.
  574. *
  575. * This makes the modulestylesheet plugin obsolete,
  576. * but only for non-api loads as we would pollute the stylesheets
  577. * not during installation as the Theme engine may not be available yet and not for system themes
  578. * TODO: figure out how to determine if a userapi belongs to a hook module and load the
  579. * corresponding css, perhaps with a new entry in modules table?
  580. *
  581. * @param string $modname Module name.
  582. * @param boolean $api Whether or not it's a api load.
  583. * @param string $type Type.
  584. *
  585. * @return void
  586. */
  587. private static function _loadStyleSheets($modname, $api, $type)
  588. {
  589. if (!System::isInstalling() && !$api) {
  590. PageUtil::addVar('stylesheet', ThemeUtil::getModuleStylesheet($modname));
  591. if (strpos($type, 'admin') === 0) {
  592. // load special admin stylesheets for administrator controllers
  593. PageUtil::addVar('stylesheet', ThemeUtil::getModuleStylesheet('Admin'));
  594. }
  595. }
  596. }
  597. /**
  598. * Get module class.
  599. *
  600. * @param string $modname Module name.
  601. * @param string $type Type.
  602. * @param boolean $api Whether or not to get the api class.
  603. * @param boolean $force Whether or not to force load.
  604. *
  605. * @return boolean|string Class name.
  606. */
  607. public static function getClass($modname, $type, $api = false, $force = false)
  608. {
  609. // do not cache this process - drak
  610. if ($api) {
  611. $result = self::loadApi($modname, $type);
  612. } else {
  613. $result = self::load($modname, $type);
  614. }
  615. if (!$result) {
  616. return false;
  617. }
  618. $modinfo = self::getInfo(self::getIDFromName($modname));
  619. $className = ($api) ? ucwords($modname) . '\\Api\\' . ucwords($type) . 'Api' : ucwords($modname) .
  620. '\\Controller\\' . ucwords($type) . 'Controller';
  621. // allow overriding the OO class (to override existing methods using inheritance).
  622. $event = new GenericEvent(null, array('modname', 'modinfo' => $modinfo, 'type' => $type, 'api' => $api), $className);
  623. EventUtil::dispatch('module_dispatch.custom_classname', $event);
  624. if ($event->isPropagationStopped()) {
  625. $className = $event->getData();
  626. }
  627. // check the modules state
  628. if (!$force && !self::available($modname)) {
  629. return false;
  630. }
  631. if (class_exists($className)) {
  632. return $className;
  633. }
  634. return false;
  635. }
  636. /**
  637. * Checks if module has the given controller.
  638. *
  639. * @param string $modname Module name.
  640. * @param string $type Controller type.
  641. *
  642. * @return boolean
  643. */
  644. public static function hasController($modname, $type)
  645. {
  646. return (bool)self::getClass($modname, $type);
  647. }
  648. /**
  649. * Checks if module has the given API class.
  650. *
  651. * @param string $modname Module name.
  652. * @param string $type API type.
  653. *
  654. * @return boolean
  655. */
  656. public static function hasApi($modname, $type)
  657. {
  658. return (bool)self::getClass($modname, $type, true);
  659. }
  660. /**
  661. * Get class object.
  662. *
  663. * @param string $className Class name.
  664. *
  665. * @throws LogicException If $className is neither a Zikula_AbstractApi nor a Zikula_AbstractController.
  666. * @return object Module object.
  667. */
  668. public static function getObject($className)
  669. {
  670. if (!$className) {
  671. return false;
  672. }
  673. $serviceId = str_replace('\\', '_', strtolower("module.$className"));
  674. $sm = ServiceUtil::getManager();
  675. if ($sm->has($serviceId)) {
  676. $object = $sm->get($serviceId);
  677. } else {
  678. $r = new ReflectionClass($className);
  679. $object = $r->newInstanceArgs(array($sm));
  680. $sm->set($serviceId, $object);
  681. }
  682. return $object;
  683. }
  684. /**
  685. * Get info if callable.
  686. *
  687. * @param string $modname Module name.
  688. * @param string $type Type.
  689. * @param string $func Function.
  690. * @param boolean $api Whether or not this is an api call.
  691. * @param boolean $force Whether or not force load.
  692. *
  693. * @return mixed
  694. */
  695. public static function getCallable($modname, $type, $func, $api = false, $force = false)
  696. {
  697. $className = self::getClass($modname, $type, $api, $force);
  698. if (!$className) {
  699. return false;
  700. }
  701. $object = self::getObject($className);
  702. $func = $api ? $func : $func.'Action';
  703. if (is_callable(array($object, $func))) {
  704. $className = str_replace('\\', '_', $className);
  705. return array('serviceid' => strtolower("module.$className"), 'classname' => $className, 'callable' => array($object, $func));
  706. }
  707. return false;
  708. }
  709. /**
  710. * Run a module function.
  711. *
  712. * @param string $modname The name of the module.
  713. * @param string $type The type of function to run.
  714. * @param string $func The specific function to run.
  715. * @param array $args The arguments to pass to the function.
  716. * @param boolean $api Whether or not to execute an API (or regular) function.
  717. *
  718. * @throws \Zikula\Framework\Exception\NotFoundException If method was not found.
  719. *
  720. * @return mixed.
  721. */
  722. public static function exec($modname, $type = 'user', $func = 'index', $args = array(), $api = false)
  723. {
  724. // define input, all numbers and booleans to strings
  725. $modname = preg_match('/\w+Module$/i', $modname) || !$modname ? $modname : $modname.'Module';
  726. $modname = isset($modname) ? ((string)$modname) : '';
  727. $loadfunc = ($api ? 'ModUtil::loadApi' : 'ModUtil::load');
  728. // validate
  729. if (!System::varValidate($modname, 'mod')) {
  730. return null;
  731. }
  732. $modinfo = self::getInfo(self::getIDFromName($modname));
  733. $controller = null;
  734. $modfunc = null;
  735. $loaded = call_user_func_array($loadfunc, array($modname, $type));
  736. $result = self::getCallable($modname, $type, $func, $api);
  737. if ($result) {
  738. $modfunc = $result['callable'];
  739. $controller = $modfunc[0];
  740. }
  741. $dispatcher = EventUtil::getManager();
  742. if ($loaded) {
  743. $preExecuteEvent = new GenericEvent($controller, array('modname' => $modname, 'modfunc' => $modfunc, 'args' => $args, 'modinfo' => $modinfo, 'type' => $type, 'api' => $api));
  744. $postExecuteEvent = new GenericEvent($controller, array('modname' => $modname, 'modfunc' => $modfunc, 'args' => $args, 'modinfo' => $modinfo, 'type' => $type, 'api' => $api));
  745. if (is_callable($modfunc)) {
  746. $dispatcher->dispatch('module_dispatch.preexecute', $preExecuteEvent);
  747. $modfunc[0]->preDispatch();
  748. $postExecuteEvent->setData(call_user_func($modfunc, $args));
  749. $modfunc[0]->postDispatch();
  750. return $dispatcher->dispatch('module_dispatch.postexecute', $postExecuteEvent)->getData();
  751. }
  752. // try to load plugin
  753. // This kind of eventhandler should
  754. // 1. Check $event['modfunc'] to see if it should run else exit silently.
  755. // 2. Do something like $result = {$event['modfunc']}({$event['args'});
  756. // 3. Save the result $event->setData($result).
  757. // 4. $event->setNotify().
  758. // return void
  759. // This event means that no $type was found
  760. $event = new GenericEvent(null, array('modfunc' => $modfunc, 'args' => $args, 'modinfo' => $modinfo, 'type' => $type, 'api' => $api), false);
  761. $dispatcher->dispatch('module_dispatch.type_not_found', $event);
  762. if ($preExecuteEvent->isPropagationStopped()) {
  763. return $preExecuteEvent->getData();
  764. }
  765. return false;
  766. }
  767. // Issue not found exception for controller requests
  768. if (!$api) {
  769. throw new \Zikula\Framework\Exception\NotFoundException(__f('The requested controller action %s_Controller_%s::%s() could not be found', array($modname, $type, $func)));
  770. }
  771. }
  772. /**
  773. * Run a module function.
  774. *
  775. * @param string $modname The name of the module.
  776. * @param string $type The type of function to run.
  777. * @param string $func The specific function to run.
  778. * @param array $args The arguments to pass to the function.
  779. *
  780. * @return mixed.
  781. */
  782. public static function func($modname, $type = 'user', $func = 'index', $args = array())
  783. {
  784. return self::exec($modname, $type, $func, $args, false);
  785. }
  786. /**
  787. * Run an module API function.
  788. *
  789. * @param string $modname The name of the module.
  790. * @param string $type The type of function to run.
  791. * @param string $func The specific function to run.
  792. * @param array $args The arguments to pass to the function.
  793. *
  794. * @return mixed.
  795. */
  796. public static function apiFunc($modname, $type = 'user', $func = 'index', $args = array())
  797. {
  798. if (empty($type)) {
  799. $type = 'user';
  800. } elseif (!System::varValidate($type, 'api')) {
  801. return null;
  802. }
  803. if (empty($func)) {
  804. $func = 'index';
  805. }
  806. return self::exec($modname, $type, $func, $args, true);
  807. }
  808. /**
  809. * Generate a module function URL.
  810. *
  811. * If the module is non-API compliant (type 1) then
  812. * a) $func is ignored.
  813. * b) $type=admin will generate admin.php?module=... and $type=user will generate index.php?name=...
  814. *
  815. * @param string $modname The name of the module.
  816. * @param string $type The type of function to run.
  817. * @param string $func The specific function to run.
  818. * @param array $args The array of arguments to put on the URL.
  819. * @param boolean|null $ssl Set to constant null,true,false $ssl = true not $ssl = 'true' null - leave the current status untouched,
  820. * true - create a ssl url, false - create a non-ssl url.
  821. * @param string $fragment The framgment to target within the URL.
  822. * @param boolean|null $fqurl Fully Qualified URL. True to get full URL, eg for Redirect, else gets root-relative path unless SSL.
  823. * @param boolean $forcelongurl Force ModUtil::url to not create a short url even if the system is configured to do so.
  824. * @param boolean|string $forcelang Force the inclusion of the $forcelang or default system language in the generated url.
  825. *
  826. * @return string Absolute URL for call.
  827. */
  828. public static function url($modname, $type = null, $func = null, $args = array(), $ssl = null, $fragment = null, $fqurl = null, $forcelongurl = false, $forcelang=false)
  829. {
  830. // define input, all numbers and booleans to strings
  831. $modname = preg_match('/\w+Module$/i', $modname) || !$modname ? $modname : $modname.'Module';
  832. $modname = isset($modname) ? ((string)$modname) : '';
  833. // note - when this legacy is to be removed, change method signature $type = null to $type making it a required argument.
  834. if (!$type) {
  835. throw new UnexpectedValueException('ModUtil::url() - $type is a required argument, you must specify it explicitly.');
  836. }
  837. // note - when this legacy is to be removed, change method signature $func = null to $func making it a required argument.
  838. if (!$func) {
  839. throw new UnexpectedValueException('ModUtil::url() - $func is a required argument, you must specify it explicitly.');
  840. }
  841. // validate
  842. if (!System::varValidate($modname, 'mod')) {
  843. return null;
  844. }
  845. //get the module info
  846. $modinfo = self::getInfo(self::getIDFromName($modname));
  847. // set the module name to the display name if this is present
  848. if (isset($modinfo['url']) && !empty($modinfo['url'])) {
  849. $modname = rawurlencode($modinfo['url']);
  850. }
  851. $entrypoint = System::getVar('entrypoint');
  852. $request = ServiceUtil::getManager()->get('request');
  853. /* @var \Symfony\Component\HttpFoundation\Request $request */
  854. $basePath = $request->getBasePath();
  855. $host = System::serverGetVar('HTTP_HOST');
  856. if (empty($host)) {
  857. return false;
  858. }
  859. $baseuri = System::getBaseUri();
  860. $https = System::serverGetVar('HTTPS');
  861. $shorturls = System::getVar('shorturls');
  862. $shorturlsstripentrypoint = System::getVar('shorturlsstripentrypoint');
  863. $shorturlsdefaultmodule = System::getVar('shorturlsdefaultmodule');
  864. // Don't encode URLs with escaped characters, like return urls.
  865. foreach ($args as $v) {
  866. if (!is_array($v)) {
  867. if (strpos($v, '%') !== false) {
  868. $shorturls = false;
  869. break;
  870. }
  871. } else {
  872. foreach ($v as $vv) {
  873. if (is_array($vv)) {
  874. foreach ($vv as $vvv) {
  875. if (!is_array($vvv) && strpos($vvv, '%') !== false) {
  876. $shorturls = false;
  877. break;
  878. }
  879. }
  880. } elseif (strpos($vv, '%') !== false) {
  881. $shorturls = false;
  882. break;
  883. }
  884. }
  885. break;
  886. }
  887. }
  888. // Setup the language code to use
  889. if (is_array($args) && isset($args['lang'])) {
  890. if (in_array($args['lang'], ZLanguage::getInstalledLanguages())) {
  891. $language = $args['lang'];
  892. }
  893. unset($args['lang']);
  894. }
  895. if (!isset($language)) {
  896. $language = ZLanguage::getLanguageCode();
  897. }
  898. $language = ($forcelang && in_array($forcelang, ZLanguage::getInstalledLanguages()) ? $forcelang : $language);
  899. // Only produce full URL when HTTPS is on or $ssl is set
  900. $siteRoot = '';
  901. if ((isset($https) && $https == 'on') || $ssl != null || $fqurl == true) {
  902. $protocol = 'http' . (($https == 'on' && $ssl !== false) || $ssl === true ? 's' : '');
  903. $secureDomain = System::getVar('secure_domain');
  904. $siteRoot = $protocol . '://' . (($secureDomain != '') ? $secureDomain : ($host . $baseuri)) . '/';
  905. }
  906. // Only convert type=user. Exclude links that append a theme parameter
  907. if ($shorturls && $type == 'user' && $forcelongurl == false) {
  908. if (isset($args['theme'])) {
  909. $theme = $args['theme'];
  910. unset($args['theme']);
  911. }
  912. // Module-specific Short URLs
  913. $url = self::apiFunc($modinfo['name'], 'user', 'encodeurl', array('modname' => $modname, 'type' => $type, 'func' => $func, 'args' => $args));
  914. if (empty($url)) {
  915. // depending on the settings, we have generic directory based short URLs:
  916. // [language]/[module]/[function]/[param1]/[value1]/[param2]/[value2]
  917. // [module]/[function]/[param1]/[value1]/[param2]/[value2]
  918. $vars = '';
  919. foreach ($args as $k => $v) {
  920. if (is_array($v)) {
  921. foreach ($v as $k2 => $w) {
  922. if (is_numeric($w) || !empty($w)) {
  923. // we suppress '', but allow 0 as value (see #193)
  924. $vars .= '/' . $k . '[' . $k2 . ']/' . $w; // &$k[$k2]=$w
  925. }
  926. }
  927. } elseif (is_numeric($v) || !empty($v)) {
  928. // we suppress '', but allow 0 as value (see #193)
  929. $vars .= "/$k/$v"; // &$k=$v
  930. }
  931. }
  932. $url = $modname . ($vars || $func != 'index' ? "/$func$vars" : '');
  933. }
  934. if ($modinfo && $shorturlsdefaultmodule && $shorturlsdefaultmodule == $modinfo['name']) {
  935. $pattern = '/^'.preg_quote($modinfo['url'], '/').'\//';
  936. $url = preg_replace($pattern, '', $url);
  937. }
  938. if (isset($theme)) {
  939. $url = rawurlencode($theme) . '/' . $url;
  940. }
  941. // add language param to short url
  942. if (ZLanguage::isRequiredLangParam() || $forcelang) {
  943. $url = "$language/" . $url;
  944. }
  945. if (!$shorturlsstripentrypoint) {
  946. $url = "$entrypoint/$url" . (!empty($query) ? '?' . $query : '');
  947. } else {
  948. $url = "$url" . (!empty($query) ? '?' . $query : '');
  949. }
  950. } else {
  951. // Regular stuff
  952. // $urlargs = "module=$modname&type=$type&func=$func";
  953. $urlargs = "/$modname/$type/$func?";
  954. // add lang param to URL
  955. if (ZLanguage::isRequiredLangParam() || $forcelang) {
  956. // $urlargs .= "&lang=$language";
  957. $urlargs .= "lang=$language";
  958. }
  959. // $url = "$entrypoint?$urlargs";
  960. $url = "{$basePath}$urlargs";
  961. if (!is_array($args)) {
  962. return false;
  963. } else {
  964. foreach ($args as $key => $value) {
  965. if (is_array($value)) {
  966. foreach ($value as $l => $w) {
  967. if (is_numeric($w) || !empty($w)) {
  968. // we suppress '', but allow 0 as value (see #193)
  969. if (is_array($w)) {
  970. foreach ($w as $m => $n) {
  971. if (is_numeric($n) || !empty($n)) {
  972. $n = strpos($n, '%') !== false ? $n : urlencode($n);
  973. $url .= "&$key" . "[$l][$m]=$n";
  974. }
  975. }
  976. } else {
  977. $w = strpos($w, '%') !== false ? $w : urlencode($w);
  978. $url .= "&$key" . "[$l]=$w";
  979. }
  980. }
  981. }
  982. } elseif (is_numeric($value) || !empty($value)) {
  983. // we suppress '', but allow 0 as value (see #193)
  984. $w = strpos($value, '%') !== false ? $value : urlencode($value);
  985. $url .= "&$key=$value";
  986. }
  987. }
  988. }
  989. }
  990. if (isset($fragment)) {
  991. $url .= '#' . $fragment;
  992. }
  993. return $siteRoot . $url;
  994. }
  995. /**
  996. * Check if a module is available.
  997. *
  998. * @param string $modname The name of the module.
  999. * @param boolean $force Force.
  1000. *
  1001. * @return boolean True if the module is available, false if not.
  1002. */
  1003. public static function available($modname = null, $force = false)
  1004. {
  1005. // define input, all numbers and booleans to strings
  1006. $modname = preg_match('/\w+Module$/i', $modname) || !$modname ? $modname : $modname.'Module';
  1007. $modname = (isset($modname) ? strtolower((string)$modname) : '');
  1008. // validate
  1009. if (!System::varValidate($modname, 'mod')) {
  1010. return false;
  1011. }
  1012. if (!isset(self::$cache['modstate'])) {
  1013. self::$cache['modstate'] = array();
  1014. }
  1015. if (!isset(self::$cache['modstate'][$modname]) || $force == true) {
  1016. $modinfo = self::getInfo(self::getIDFromName($modname));
  1017. if (isset($modinfo['state'])) {
  1018. self::$cache['modstate'][$modname] = $modinfo['state'];
  1019. }
  1020. }
  1021. if ($force == true) {
  1022. self::$cache['modstate'][$modname] = self::STATE_ACTIVE;
  1023. }
  1024. if ((isset(self::$cache['modstate'][$modname]) &&
  1025. self::$cache['modstate'][$modname] == self::STATE_ACTIVE) || (preg_match('/^(extensionsmodule|adminmodule|thememodule|blockmodule|groupsmodule|permissionsmodule|usersmodule)$/i', $modname) &&
  1026. (isset(self::$cache['modstate'][$modname]) && (self::$cache['modstate'][$modname] == self::STATE_UPGRADED || self::$cache['modstate'][$modname] == self::STATE_INACTIVE)))) {
  1027. self::$cache['modstate'][$modname] = self::STATE_ACTIVE;
  1028. return true;
  1029. }
  1030. return false;
  1031. }
  1032. /**
  1033. * Get name of current top-level module.
  1034. *
  1035. * @return string The name of the current top-level module, false if not in a module.
  1036. */
  1037. public static function getName()
  1038. {
  1039. if (!isset(self::$cache['modgetname'])) {
  1040. self::$cache['modgetname'] = FormUtil::getPassedValue('module', null, 'GETPOST', FILTER_SANITIZE_STRING);
  1041. if (empty(self::$cache['modgetname'])) {
  1042. if (!System::getVar('startpage')) {
  1043. self::$cache['modgetname'] = System::getVar('startpage');
  1044. } else {
  1045. $baseUriLenght = strlen(System::getBaseUri());
  1046. $shortUrlPath = substr(System::getCurrentUri(),$baseUriLenght+1);
  1047. if (!empty($shortUrlPath) == 0) {
  1048. self::$cache['modgetname'] = System::getVar('startpage');
  1049. } else {
  1050. $args = explode('/', $shortUrlPath);
  1051. self::$cache['modgetname'] = $args[0];
  1052. }
  1053. }
  1054. }
  1055. // the parameters may provide the module alias so lets get
  1056. // the real name from the db
  1057. $modinfo = self::getInfo(self::getIdFromName(self::$cache['modgetname']));
  1058. if (isset($modinfo['name'])) {
  1059. $type = FormUtil::getPassedValue('type', null, 'GETPOST', FILTER_SANITIZE_STRING);
  1060. self::$cache['modgetname'] = $modinfo['name'];
  1061. if ((!$type == 'init' || !$type == 'initeractiveinstaller') && !self::available(self::$cache['modgetname'])) {
  1062. self::$cache['modgetname'] = System::getVar('startpage');
  1063. }
  1064. }
  1065. }
  1066. return self::$cache['modgetname'];
  1067. }
  1068. /**
  1069. * Get the base directory for a module.
  1070. *
  1071. * Example: If the webroot is located at
  1072. * /var/www/html
  1073. * and the module name is Template and is found
  1074. * in the modules directory then this function
  1075. * would return /var/www/html/modules/Template
  1076. *
  1077. * If the Template module was located in the system
  1078. * directory then this function would return
  1079. * /var/www/html/system/Template
  1080. *
  1081. * This allows you to say:
  1082. * include(ModUtil::getBaseDir() . '/includes/private_functions.php');.
  1083. *
  1084. * @param string $modname Name of module to that you want the base directory of.
  1085. *
  1086. * @return string The path from the root directory to the specified module.
  1087. */
  1088. public static function getBaseDir($modname = '')
  1089. {
  1090. if (empty($modname)) {
  1091. $modname = self::getName();
  1092. }
  1093. $path = System::getBaseUri();
  1094. $directory = ZIKULA_ROOT.'/system/' . $modname;
  1095. if ($path != '') {
  1096. $path .= '/';
  1097. }
  1098. $url = $path . $directory;
  1099. if (!is_dir($url)) {
  1100. $directory = ZIKULA_ROOT.'/modules/' . $modname;
  1101. $url = $path . $directory;
  1102. }
  1103. return $url;
  1104. }
  1105. /**
  1106. * Gets the modules table.
  1107. *
  1108. * Small wrapper function to avoid duplicate sql.
  1109. *
  1110. * @return array An array modules table.
  1111. */
  1112. public static function getModsTable()
  1113. {
  1114. if (!isset(self::$cache['modstable'])) {
  1115. self::$cache['modstable'] = array();
  1116. }
  1117. if (!self::$cache['modstable'] || System::isInstalling()) {
  1118. // get entityManager
  1119. $sm = ServiceUtil::getManager();
  1120. $entityManager = $sm->get('doctrine')->getEntityManager();
  1121. // get all modules
  1122. $modules = $entityManager->getRepository('Zikula\Core\Doctrine\Entity\Extension')->findAll();
  1123. foreach ($modules as $module) {
  1124. $module = $module->toArray();
  1125. if (!isset($module['url']) || empty($module['url'])) {
  1126. $module['url'] = strtolower($module['displayname']);
  1127. }
  1128. self::$cache['modstable'][$module['id']] = $module;
  1129. }
  1130. // add Core module (hack).
  1131. self::$cache['modstable'][0] = array(
  1132. 'id' => 0,
  1133. 'name' => 'zikula',
  1134. 'type' => self::TYPE_CORE,
  1135. 'directory' => '',
  1136. 'displayname' => 'Zikula Core v' . \Zikula\Core\Core::VERSION_NUM,
  1137. 'version' => \Zikula\Core\Core::VERSION_NUM,
  1138. 'state' => self::STATE_ACTIVE);
  1139. }
  1140. return self::$cache['modstable'];
  1141. }
  1142. /**
  1143. * Generic modules select function.
  1144. *
  1145. * Only modules in the module table are returned
  1146. * which means that new/unscanned modules will not be returned.
  1147. *
  1148. * @param string $where The where clause to use for the select.
  1149. * @param string $sort The sort to use.
  1150. *
  1151. * @return array The resulting module object array.
  1152. */
  1153. public static function getModules($where=array(), $sort='displayname')
  1154. {
  1155. // get entityManager
  1156. $sm = ServiceUtil::getManager();
  1157. $entityManager = $sm->get('doctrine')->getEntityManager();
  1158. // get all modules
  1159. $modules = $entityManager->getRepository('Zikula\Core\Doctrine\Entity\Extension')->findBy($where, array($sort => 'ASC'));
  1160. return $modules;
  1161. }
  1162. /**
  1163. * Return an array of modules in the specified state.
  1164. *
  1165. * Only modules in the module table are returned
  1166. * which means that new/unscanned modules will not be returned.
  1167. *
  1168. * @param integer $state The module state (optional) (defaults = active state).
  1169. * @param string $sort The sort to use.
  1170. *
  1171. * @return array The resulting module object array.
  1172. */
  1173. public static function getModulesByState($state=self::STATE_ACTIVE, $sort='displayname')
  1174. {
  1175. $sm = ServiceUtil::getManager();
  1176. $entityManager = $sm->get('doctrine')->getEntityManager();
  1177. $modules = $entityManager->getRepository('Zikula\Core\Doctrine\Entity\Extension')->findBy(array('state' => $state), array($sort => 'ASC'));
  1178. return $modules;
  1179. }
  1180. /**
  1181. * Initialize object oriented module.
  1182. *
  1183. * @param string $moduleName Module name.
  1184. *
  1185. * @return boolean
  1186. */
  1187. public static function initOOModule($moduleName)
  1188. {
  1189. if (self::isInitialized($moduleName)) {
  1190. return true;
  1191. }
  1192. $modinfo = self::getInfo(self::getIdFromName($moduleName));
  1193. if (!$modinfo) {
  1194. return false;
  1195. }
  1196. $modpath = ($modinfo['type'] == self::TYPE_SYSTEM) ? 'system' : 'modules';
  1197. $osdir = DataUtil::formatForOS($modinfo['directory']);
  1198. ZLoader::addModule($moduleName, realpath($modpath));
  1199. // load optional bootstrap
  1200. $bootstrap = ZIKULA_ROOT."/$modpath/$osdir/bootstrap.php";
  1201. if (file_exists($bootstrap)) {
  1202. include_once $bootstrap;
  1203. }
  1204. // register any event handlers.
  1205. // module handlers must be attached from the bootstrap.
  1206. if (is_dir(ZIKULA_CONFIG_PATH."/EventHandlers/$osdir")) {
  1207. EventUtil::

Large files files are truncated, but you can click here to view the full file