PageRenderTime 60ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/web/system/ExtensionsModule/Api/AdminApi.php

https://github.com/antoniom/core
PHP | 1335 lines | 855 code | 192 blank | 288 comment | 263 complexity | 451ed72ef8af801dbe5518aeaf7a4e91 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-3.0, MIT
  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 Zikula
  10. *
  11. * Please see the NOTICE file distributed with this source code for further
  12. * information regarding copyright and licensing.
  13. */
  14. namespace ExtensionsModule\Api;
  15. use LogUtil, SecurityUtil, ModUtil, System, DataUtil, ZLoader, ZLanguage, HookUtil, EventUtil, PluginUtil;
  16. use Zikula\Core\Event\GenericEvent;
  17. use Zikula\Core\Core;
  18. use Zikula\Core\Doctrine\Entity\Extension;
  19. use ExtensionsModule\Util;
  20. /**
  21. * Administrative API functions for the Extensions module.
  22. */
  23. class AdminApi extends \Zikula\Framework\Api\AbstractApi
  24. {
  25. /**
  26. * Update module information.
  27. *
  28. * @param array $args All parameters passed to this function.
  29. * numeric $args['id'] The id number of the module.
  30. *
  31. * @return array An associative array containing the module information for the specified module id.
  32. */
  33. public function modify($args)
  34. {
  35. return $this->entityManager->getRepository('Zikula\Core\Doctrine\Entity\Extension')->findOneBy($args);
  36. }
  37. /**
  38. * Update module information.
  39. *
  40. * @param array $args All parameters passed to this function.
  41. * numeric $args['id'] The id number of the module to update.
  42. * string $args['displayname'] The new display name of the module.
  43. * string $args['description'] The new description of the module.
  44. *
  45. * @return boolean True on success, false on failure.
  46. */
  47. public function update($args)
  48. {
  49. // Argument check
  50. if (!isset($args['id']) || !is_numeric($args['id']) ||
  51. !isset($args['displayname']) ||
  52. !isset($args['description']) ||
  53. !isset($args['url'])) {
  54. throw new \InvalidArgumentException('Missing or invalid arguments');
  55. }
  56. // Security check
  57. if (!SecurityUtil::checkPermission('Extensions::', "::$args[id]", ACCESS_ADMIN)) {
  58. throw new \Zikula\Framework\Exception\ForbiddenException();
  59. }
  60. // check for duplicate display names
  61. // get the module info for the module being updated
  62. $moduleinforeal = ModUtil::getInfo($args['id']);
  63. // validate URL
  64. $moduleinfourl = ModUtil::getInfoFromName($args['url']);
  65. // If the two real module name don't match then the new display name can't be used
  66. if ($moduleinfourl && $moduleinfourl['name'] != $moduleinforeal['name']) {
  67. return LogUtil::registerError($this->__('Error! Could not save the module URL information. A duplicate module URL was detected.'));
  68. }
  69. if (empty($args['url'])) {
  70. return LogUtil::registerError($this->__('Error! Module URL is a required field, please enter a unique name.'));
  71. }
  72. if (empty($args['displayname'])) {
  73. return LogUtil::registerError($this->__('Error! Module URL is a required field, please enter a unique name.'));
  74. }
  75. // Rename operation
  76. /* @var Extension $entity */
  77. $entity = $this->entityManager->getRepository('Zikula\Core\Doctrine\Entity\Extension')->findOneBy(array('id' => $args['id']));
  78. $entity->setDisplayname($args['displayname']);
  79. $entity->setDescription($args['description']);
  80. $entity->setUrl($args['url']);
  81. $this->entityManager->persist($entity);
  82. $this->entityManager->flush();
  83. // write changes to db
  84. $this->entityManager->flush();
  85. return true;
  86. }
  87. /**
  88. * Obtain a list of modules.
  89. *
  90. * @param array $args All parameters passed to this function.
  91. * integer $args['startnum'] The number of the module at which to start the list (for paging); optional,
  92. * defaults to 1.
  93. * integer $args['numitems'] The number of the modules to return in the list (for paging); optional, defaults to
  94. * -1, which returns modules starting at the specified number without limit.
  95. * integer $args['state'] Filter the list by this state; optional.
  96. * integer $args['type'] Filter the list by this type; optional.
  97. * string $args['letter'] Filter the list by module names beginning with this letter; optional.
  98. *
  99. * @return array An associative array of known modules.
  100. */
  101. public function listmodules($args)
  102. {
  103. // Security check
  104. if (!System::isInstalling()) {
  105. if (!SecurityUtil::checkPermission('Extensions::', '::', ACCESS_ADMIN)) {
  106. throw new \Zikula\Framework\Exception\ForbiddenException();
  107. }
  108. }
  109. // create a QueryBuilder instance
  110. $qb = $this->entityManager->createQueryBuilder();
  111. // add select and from params
  112. $qb->select('e')
  113. ->from('Zikula\Core\Doctrine\Entity\Extension', 'e');
  114. // filter by first letter of module
  115. if (isset($args['letter']) && !empty($args['letter'])) {
  116. $clause1 = $qb->expr()->like('e.name', $qb->expr()->literal($args['letter'] . '%'));
  117. $clause2 = $qb->expr()->like('e.name', $qb->expr()->literal(strtolower($args['letter']) . '%'));
  118. $qb->andWhere($clause1 . ' OR ' . $clause2);
  119. }
  120. // filter by type
  121. $type = (empty($args['type']) || $args['type'] < 0 || $args['type'] > ModUtil::TYPE_SYSTEM) ? 0 : (int)$args['type'];
  122. if ($type != 0) {
  123. $qb->andWhere($qb->expr()->eq('e.type', $qb->expr()->literal($type)));
  124. }
  125. // filter by module state
  126. if ($this->container['multisites.enabled'] == 1) {
  127. $state = (empty($args['state']) || $args['state'] < -1 || $args['state'] > ModUtil::STATE_NOTALLOWED) ? 0 : (int)$args['state'];
  128. } else {
  129. $state = (empty($args['state']) || $args['state'] < -1 || $args['state'] > ModUtil::STATE_UPGRADED) ? 0 : (int)$args['state'];
  130. }
  131. switch ($state) {
  132. case ModUtil::STATE_UNINITIALISED:
  133. case ModUtil::STATE_INACTIVE:
  134. case ModUtil::STATE_ACTIVE:
  135. case ModUtil::STATE_MISSING:
  136. case ModUtil::STATE_UPGRADED:
  137. case ModUtil::STATE_NOTALLOWED:
  138. case ModUtil::STATE_INVALID:
  139. $qb->andWhere($qb->expr()->eq('e.state', $qb->expr()->literal($state)));
  140. break;
  141. case 10:
  142. $qb->andWhere($qb->expr()->gt('e.state', 10));
  143. break;
  144. }
  145. // add clause for ordering
  146. $sort = isset($args['sort']) ? (string)$args['sort'] : 'name';
  147. $sortdir = isset($args['sortdir']) && $args['sortdir'] ? $args['sortdir'] : 'ASC';
  148. $qb->orderBy('e.' . $sort, $sortdir);
  149. // add limit and offset
  150. $startnum = (!isset($args['startnum']) || empty($args['startnum']) || $args['startnum'] < 0) ? 0 : (int)$args['startnum'];
  151. $numitems = (!isset($args['numitems']) || empty($args['numitems']) || $args['numitems'] < 0) ? 0 : (int)$args['numitems'];
  152. if ($numitems > 0) {
  153. $qb->setFirstResult($startnum)
  154. ->setMaxResults($numitems);
  155. }
  156. // convert querybuilder instance into a Query object
  157. $query = $qb->getQuery();
  158. //echo $query->getSQL();
  159. // execute query
  160. $result = $query->getResult();
  161. return $result;
  162. }
  163. /**
  164. * Set the state of a module.
  165. *
  166. * @param array $args All parameters passed to this function.
  167. * numeric $args['id'] The module id.
  168. * integer $args['state'] The new state.
  169. *
  170. * @return boolean True if successful, false otherwise.
  171. */
  172. public function setState($args)
  173. {
  174. // Argument check
  175. if (!isset($args['id']) || !is_numeric($args['id']) || !isset($args['state'])) {
  176. throw new \InvalidArgumentException('Missing or invalid arguments');
  177. }
  178. // Security check
  179. if (!System::isInstalling()) {
  180. if (!SecurityUtil::checkPermission('Extensions::', '::', ACCESS_EDIT)) {
  181. throw new \Zikula\Framework\Exception\ForbiddenException();
  182. }
  183. }
  184. // get module
  185. $module = $this->entityManager->getRepository('\Zikula\Core\Doctrine\Entity\Extension')->find($args['id']);
  186. if (empty($module)) {
  187. return false;
  188. }
  189. $modinfo = ModUtil::getInfo($args['id']);
  190. // Check valid state transition
  191. switch ($args['state']) {
  192. case ModUtil::STATE_UNINITIALISED:
  193. if ($this->container['multisites.enabled'] == 1) {
  194. if (!SecurityUtil::checkPermission('Extensions::', '::', ACCESS_ADMIN)) {
  195. return LogUtil::registerError($this->__('Error! Invalid module state transition.'));
  196. }
  197. }
  198. break;
  199. case ModUtil::STATE_INACTIVE:
  200. break;
  201. case ModUtil::STATE_ACTIVE:
  202. break;
  203. case ModUtil::STATE_MISSING:
  204. break;
  205. case ModUtil::STATE_UPGRADED:
  206. $oldstate = $module['state'];
  207. if ($oldstate == ModUtil::STATE_UNINITIALISED) {
  208. return LogUtil::registerError($this->__('Error! Invalid module state transition.'));
  209. }
  210. break;
  211. }
  212. // change state
  213. $module['state'] = $args['state'];
  214. $this->entityManager->flush();
  215. // state changed, so update the ModUtil::available-info for this module.
  216. $modinfo = ModUtil::getInfo($args['id']);
  217. ModUtil::available($modinfo['name'], true);
  218. return true;
  219. }
  220. /**
  221. * Remove a module.
  222. *
  223. * @param array $args All parameters sent to this function.
  224. * numeric $args['id'] The id of the module.
  225. * boolean $args['removedependents'] Remove any modules dependent on this module (default: false).
  226. * boolean $args['interactive_remove'] Whether to operat in interactive mode or not.
  227. *
  228. * @return boolean True on success, false on failure.
  229. */
  230. public function remove($args)
  231. {
  232. // Argument check
  233. if (!isset($args['id']) || !is_numeric($args['id'])) {
  234. throw new \InvalidArgumentException('Missing or invalid arguments');
  235. }
  236. if (!isset($args['removedependents']) || !is_bool($args['removedependents'])) {
  237. $removedependents = false;
  238. } else {
  239. $removedependents = true;
  240. }
  241. // Security check
  242. if (!SecurityUtil::checkPermission('Extensions::', '::', ACCESS_ADMIN)) {
  243. throw new \Zikula\Framework\Exception\ForbiddenException();
  244. }
  245. // Get module information
  246. $modinfo = ModUtil::getInfo($args['id']);
  247. if (empty($modinfo)) {
  248. return LogUtil::registerError($this->__('Error! No such module ID exists.'));
  249. }
  250. switch ($modinfo['state']) {
  251. case ModUtil::STATE_NOTALLOWED:
  252. return LogUtil::registerError($this->__f('Error! No permission to upgrade %s.', $modinfo['name']));
  253. break;
  254. }
  255. $osdir = DataUtil::formatForOS($modinfo['directory']);
  256. $modpath = ($modinfo['type'] == ModUtil::TYPE_SYSTEM) ? 'system' : 'modules';
  257. ZLoader::addModule($osdir, $modpath);
  258. $version = Util::getVersionMeta($osdir, $modpath);
  259. $bootstrap = "$modpath/$osdir/bootstrap.php";
  260. if (file_exists($bootstrap)) {
  261. include_once $bootstrap;
  262. }
  263. if ($modinfo['type'] == ModUtil::TYPE_MODULE) {
  264. if (is_dir("modules/$osdir/Resources/locale")) {
  265. ZLanguage::bindModuleDomain($modinfo['name']);
  266. }
  267. }
  268. // Get module database info
  269. ModUtil::dbInfoLoad($modinfo['name'], $osdir);
  270. // Module deletion function. Only execute if the module is initialised.
  271. if ($modinfo['state'] != ModUtil::STATE_UNINITIALISED) {
  272. $className = ucwords($modinfo['name']) . '\Installer';
  273. $reflectionInstaller = new \ReflectionClass($className);
  274. if (!$reflectionInstaller->isSubclassOf('Zikula\Framework\AbstractInstaller')) {
  275. LogUtil::registerError($this->__f("%s must be an instance of Zikula\Framework\AbstractInstaller", $className));
  276. }
  277. $installer = $reflectionInstaller->newInstanceArgs(array($this->container));
  278. $interactiveClass = ucwords($modinfo['name']) . '_Controller_Interactiveinstaller';
  279. $interactiveController = null;
  280. if (class_exists($interactiveClass)) {
  281. $reflectionInteractive = new \ReflectionClass($interactiveClass);
  282. if (!$reflectionInteractive->isSubclassOf('Zikula\Framework\Controller\AbstractInteractiveInstaller')
  283. ) {
  284. LogUtil::registerError($this->__f("%s must be an instance of
  285. Zikula\Framework\Controller\AbstractInteractiveInstaller", $className));
  286. }
  287. $interactiveController = $reflectionInteractive->newInstance($this->container);
  288. }
  289. // perform the actual deletion of the module
  290. $func = array($installer, 'uninstall');
  291. $interactive_func = array($interactiveController, 'uninstall');
  292. // allow bypass of interactive removal during a new installation only.
  293. if (System::isInstalling() && is_callable($interactive_func) && !is_callable($func)) {
  294. return; // return void here
  295. }
  296. if ((isset($args['interactive_remove']) && $args['interactive_remove'] == false) && is_callable($interactive_func)) {
  297. // Because interactive installers extend the AbstractController, is_callable will always return true because of the __call()
  298. // so we must check if the method actually exists by reflection - drak
  299. if ($reflectionInteractive->hasMethod('upgrade')) {
  300. $this->request->getSession()->set('interactive_remove', true);
  301. return call_user_func($interactive_func);
  302. }
  303. }
  304. // non-interactive
  305. if (is_callable($func)) {
  306. if (call_user_func($func) != true) {
  307. return false;
  308. }
  309. }
  310. }
  311. // Delete any module variables that the module cleanup function might have missed
  312. $dql = "DELETE FROM Zikula\Core\Doctrine\Entity\ExtensionVar v WHERE v.modname = '{$modinfo['name']}'";
  313. $query = $this->entityManager->createQuery($dql);
  314. $query->getResult();
  315. HookUtil::unregisterProviderBundles($version->getHookProviderBundles());
  316. HookUtil::unregisterSubscriberBundles($version->getHookSubscriberBundles());
  317. EventUtil::unregisterPersistentModuleHandlers($modinfo['name']);
  318. // remove the entry from the modules table
  319. if ($this->container['multisites.enabled'] == 1) {
  320. // who can access to the mainSite can delete the modules in any other site
  321. $canDelete = (($this->container['multisites.mainsiteurl'] == $this->request->query->get('sitedns', null) && $this->container['multisites.based_on_domains'] == 0) || ($this->container['multisites.mainsiteurl'] == $_SERVER['HTTP_HOST'] && $this->container['multisites.based_on_domains'] == 1)) ? 1 : 0;
  322. //delete the module infomation only if it is not allowed, missign or invalid
  323. if ($canDelete == 1 || $modinfo['state'] == ModUtil::STATE_NOTALLOWED || $modinfo['state'] == ModUtil::STATE_MISSING || $modinfo['state'] == ModUtil::STATE_INVALID) {
  324. // remove the entry from the modules table
  325. $dql = "DELETE FROM Zikula\Core\Doctrine\Entity\Extension e WHERE e.id = {$args['id']}";
  326. $query = $this->entityManager->createQuery($dql);
  327. $query->getResult();
  328. } else {
  329. //set state as uninnitialised
  330. ModUtil::apiFunc('modulesModule', 'admin', 'setstate', array('id' => $args['id'], 'state' => ModUtil::STATE_UNINITIALISED));
  331. }
  332. } else {
  333. $dql = "DELETE FROM Zikula\Core\Doctrine\Entity\Extension e WHERE e.id = {$args['id']}";
  334. $query = $this->entityManager->createQuery($dql);
  335. $query->getResult();
  336. }
  337. $event = new GenericEvent(null, $modinfo);
  338. $this->dispatcher->dispatch('installer.module.uninstalled', $event);
  339. return true;
  340. }
  341. /**
  342. * Scan the file system for modules.
  343. *
  344. * This function scans the file system for modules and returns an array with all (potential) modules found.
  345. * This information is used to regenerate the module list.
  346. *
  347. * @return array An array of modules found in the file system.
  348. */
  349. public function getfilemodules()
  350. {
  351. // Security check
  352. if (!System::isInstalling()) {
  353. if (!SecurityUtil::checkPermission('Extensions::', '::', ACCESS_ADMIN)) {
  354. throw new \Zikula\Framework\Exception\ForbiddenException();
  355. }
  356. }
  357. // Get all modules on filesystem
  358. $filemodules = array();
  359. // set the paths to search
  360. $rootdirs = array('system' => ModUtil::TYPE_SYSTEM, 'modules' => ModUtil::TYPE_MODULE);
  361. foreach ($rootdirs as $rootdir => $moduletype) {
  362. if (is_dir($rootdir)) {
  363. $dirs = \FileUtil::getFiles($rootdir, false, true, null, 'd');
  364. foreach ($dirs as $dir) {
  365. ZLoader::addModule($dir, $rootdir);
  366. // loads the gettext domain for 3rd party modules
  367. if ($rootdir == 'modules' && (is_dir("modules/$dir/Resources/locale"))) {
  368. ZLanguage::bindModuleDomain($dir);
  369. }
  370. try {
  371. $modversion = Util::getVersionMeta($dir, $rootdir);
  372. } catch (\Exception $e) {
  373. LogUtil::registerError($e->getMessage());
  374. continue;
  375. }
  376. if (!isset($modversion['capabilities'])) {
  377. $modversion['capabilities'] = array();
  378. }
  379. $name = $dir;
  380. // Work out if admin-capable
  381. if (class_exists("{$dir}\Controller\AdminController")) {
  382. $caps = $modversion['capabilities'];
  383. $caps['admin'] = array('version' => '1.0');
  384. $modversion['capabilities'] = $caps;
  385. }
  386. // Work out if user-capable
  387. if (class_exists("{$dir}\Controller\UserController")) {
  388. $caps = $modversion['capabilities'];
  389. $caps['user'] = array('version' => '1.0');
  390. $modversion['capabilities'] = $caps;
  391. }
  392. $version = $modversion['version'];
  393. $description = $modversion['description'];
  394. if (isset($modversion['displayname']) && !empty($modversion['displayname'])) {
  395. $displayname = $modversion['displayname'];
  396. } else {
  397. $displayname = $modversion['name'];
  398. }
  399. $capabilities = serialize($modversion['capabilities']);
  400. // bc for urls
  401. if (isset($modversion['url']) && !empty($modversion['url'])) {
  402. $url = $modversion['url'];
  403. } else {
  404. $url = $displayname;
  405. }
  406. if (isset($modversion['securityschema']) && is_array($modversion['securityschema'])) {
  407. $securityschema = serialize($modversion['securityschema']);
  408. } else {
  409. $securityschema = serialize(array());
  410. }
  411. $core_min = isset($modversion['core_min']) ? $modversion['core_min'] : '';
  412. $core_max = isset($modversion['core_max']) ? $modversion['core_max'] : '';
  413. $oldnames = isset($modversion['oldnames']) ? $modversion['oldnames'] : '';
  414. if (isset($modversion['dependencies']) && is_array($modversion['dependencies'])) {
  415. $moddependencies = serialize($modversion['dependencies']);
  416. } else {
  417. $moddependencies = serialize(array());
  418. }
  419. $filemodules[$name] = array(
  420. 'directory' => $dir,
  421. 'name' => $name,
  422. 'type' => $moduletype,
  423. 'displayname' => $displayname,
  424. 'url' => $url,
  425. 'oldnames' => $oldnames,
  426. 'version' => $version,
  427. 'capabilities' => $capabilities,
  428. 'description' => $description,
  429. 'securityschema' => $securityschema,
  430. 'dependencies' => $moddependencies,
  431. 'core_min' => $core_min,
  432. 'core_max' => $core_max,
  433. );
  434. // important: unset modversion and modtype, otherwise the
  435. // following modules will have some values not defined in
  436. // the next version files to be read
  437. unset($modversion);
  438. unset($modtype);
  439. }
  440. }
  441. }
  442. return $filemodules;
  443. }
  444. /**
  445. * Regenerate modules list.
  446. *
  447. * @param array $args All parameters passed to this function.
  448. * array $args['filemodules'] An array of modules in the filesystem, as would be returned by
  449. * {@link getfilemodules()}; optional, defaults to the results of
  450. * $this->getfilemodules().
  451. *
  452. * @return boolean True on success, false on failure.
  453. */
  454. public function regenerate($args)
  455. {
  456. // Security check
  457. if (!System::isInstalling()) {
  458. if (!SecurityUtil::checkPermission('Extensions::', '::', ACCESS_ADMIN)) {
  459. throw new \Zikula\Framework\Exception\ForbiddenException();
  460. }
  461. }
  462. // Argument check
  463. if (!isset($args['filemodules']) || !is_array($args['filemodules'])) {
  464. throw new \InvalidArgumentException('Missing or invalid arguments');
  465. }
  466. $entity = 'Zikula\Core\Doctrine\Entity\Extension';
  467. // default action
  468. $filemodules = $args['filemodules'];
  469. $defaults = (isset($args['defaults']) ? $args['defaults'] : false);
  470. // Get all modules in DB
  471. $allmodules = $this->entityManager->getRepository('Zikula\Core\Doctrine\Entity\Extension')->findAll();
  472. if (!$allmodules) {
  473. return LogUtil::registerError($this->__('Error! Could not load data.'));
  474. }
  475. // index modules by name
  476. $dbmodules = array();
  477. /* @var Extension $module */
  478. foreach ($allmodules as $module) {
  479. $dbmodules[$module['name']] = $module->toArray();
  480. }
  481. // build a list of found modules and dependencies
  482. $module_names = array();
  483. $moddependencies = array();
  484. foreach ($filemodules as $modinfo) {
  485. $module_names[] = $modinfo['name'];
  486. if (isset($modinfo['dependencies']) && !empty($modinfo['dependencies'])) {
  487. $moddependencies[$modinfo['name']] = unserialize($modinfo['dependencies']);
  488. }
  489. }
  490. // see if any modules have changed name since last generation
  491. foreach ($filemodules as $name => $modinfo) {
  492. if (isset($modinfo['oldnames']) && !empty($modinfo['oldnames'])) {
  493. foreach ($dbmodules as $dbname => $dbmodinfo) {
  494. if (isset($dbmodinfo['name']) && in_array($dbmodinfo['name'], (array)$modinfo['oldnames'])) {
  495. // migrate its modvars
  496. $dql = "
  497. UPDATE Zikula\Core\DoctrineEntity\ExtensionVar v
  498. SET v.modname = '{$modinfo['name']}'
  499. WHERE v.modname = '{$dbname}'";
  500. $query = $this->entityManager->createQuery($dql);
  501. $query->getResult();
  502. // rename the module register
  503. $dql = "
  504. UPDATE Zikula\Core\Doctrine\Entity\Extension e
  505. SET e.name = '{$modinfo['name']}'
  506. WHERE e.id = {$dbmodules[$dbname]['id']}";
  507. $query = $this->entityManager->createQuery($dql);
  508. $query->getResult();
  509. // replace the old module with the new one in the dbmodules array
  510. $newmodule = $dbmodules[$dbname];
  511. $newmodule['name'] = $modinfo['name'];
  512. unset($dbmodules[$dbname]);
  513. $dbname = $modinfo['name'];
  514. $dbmodules[$dbname] = $newmodule;
  515. }
  516. }
  517. }
  518. if (isset($dbmodules[$name]) && $dbmodules[$name]['state'] > 10) {
  519. $dbmodules[$name]['state'] = $dbmodules[$name]['state'] - 20;
  520. $this->setState(array('id' => $dbmodules[$name]['id'], 'state' => $dbmodules[$name]['state']));
  521. }
  522. if (isset($dbmodules[$name]['id'])) {
  523. $modinfo['id'] = $dbmodules[$name]['id'];
  524. if ($dbmodules[$name]['state'] != ModUtil::STATE_UNINITIALISED && $dbmodules[$name]['state'] != ModUtil::STATE_INVALID) {
  525. unset($modinfo['version']);
  526. }
  527. if (!$defaults) {
  528. unset($modinfo['displayname']);
  529. unset($modinfo['description']);
  530. unset($modinfo['url']);
  531. }
  532. unset($modinfo['oldnames']);
  533. unset($modinfo['dependencies']);
  534. $modinfo['capabilities'] = unserialize($modinfo['capabilities']);
  535. $modinfo['securityschema'] = unserialize($modinfo['securityschema']);
  536. $module = $this->entityManager->getRepository($entity)->find($modinfo['id']);
  537. $module->merge($modinfo);
  538. $this->entityManager->flush();
  539. }
  540. // check core version is compatible with current
  541. $minok = 0;
  542. $maxok = 0;
  543. // strip any -dev, -rcN etc from version number
  544. $coreVersion = preg_replace('#(\d+\.\d+\.\d+).*#', '$1', Core::VERSION_NUM);
  545. if (!empty($filemodules[$name]['core_min'])) {
  546. $minok = version_compare($coreVersion, $filemodules[$name]['core_min']);
  547. }
  548. if (!empty($filemodules[$name]['core_max'])) {
  549. $maxok = version_compare($filemodules[$name]['core_max'], $coreVersion);
  550. }
  551. if (isset($dbmodules[$name])) {
  552. if ($minok == -1 || $maxok == -1) {
  553. $dbmodules[$name]['state'] = $dbmodules[$name]['state'] + 20;
  554. $this->setState(array('id' => $dbmodules[$name]['id'], 'state' => $dbmodules[$name]['state']));
  555. }
  556. if (isset($dbmodules[$name]['state'])) {
  557. $filemodules[$name]['state'] = $dbmodules[$name]['state'];
  558. }
  559. }
  560. }
  561. // See if we have lost any modules since last generation
  562. foreach ($dbmodules as $name => $modinfo) {
  563. if (!in_array($name, $module_names)) {
  564. $lostmodule = $this->entityManager->getRepository($entity)->findOneBy(array('name' => $name));
  565. if (!$lostmodule) {
  566. return LogUtil::registerError($this->__f('Error! Could not load data for module %s.', array($name)));
  567. }
  568. if ($dbmodules[$name]['state'] == ModUtil::STATE_INVALID) {
  569. // module was invalid and now it was removed, delete it
  570. $this->remove(array('id' => $dbmodules[$name]['id']));
  571. } elseif ($dbmodules[$name]['state'] == ModUtil::STATE_UNINITIALISED) {
  572. // module was uninitialised and subsequently removed, delete it
  573. $this->remove(array('id' => $dbmodules[$name]['id']));
  574. } else {
  575. // Set state of module to 'missing'
  576. $this->setState(array('id' => $dbmodules[$name]['id'], 'state' => ModUtil::STATE_MISSING));
  577. }
  578. unset($dbmodules[$name]);
  579. }
  580. }
  581. // See if we have gained any modules since last generation,
  582. // or if any current modules have been upgraded
  583. foreach ($filemodules as $name => $modinfo) {
  584. if (empty($dbmodules[$name])) {
  585. // set state to invalid if we can't determine an ID
  586. $modinfo['state'] = ModUtil::STATE_UNINITIALISED;
  587. if (!$modinfo['version']) {
  588. $modinfo['state'] = ModUtil::STATE_INVALID;
  589. }
  590. // unset some vars
  591. unset($modinfo['oldnames']);
  592. unset($modinfo['dependencies']);
  593. // unserialze some vars
  594. $modinfo['capabilities'] = unserialize($modinfo['capabilities']);
  595. $modinfo['securityschema'] = unserialize($modinfo['securityschema']);
  596. // insert new module to db
  597. if ($this->container['multisites.enabled'] == 1) {
  598. // only the main site can regenerate the modules list
  599. if (($this->container['multisites.mainsiteurl'] == $this->request->query->get('sitedns', null) && $this->container['multisites.based_on_domains'] == 0) || ($this->container['multisites.mainsiteurl'] == $_SERVER['HTTP_HOST'] && $this->container['multisites.based_on_domains'] == 1)) {
  600. $item = new $entity;
  601. $item->merge($modinfo);
  602. $this->entityManager->persist($item);
  603. }
  604. } else {
  605. $item = new $entity;
  606. $item->merge($modinfo);
  607. $this->entityManager->persist($item);
  608. }
  609. $this->entityManager->flush();
  610. } else {
  611. // module is in the db already
  612. if ($dbmodules[$name]['state'] == ModUtil::STATE_MISSING) {
  613. // module was lost, now it is here again
  614. $this->setState(array('id' => $dbmodules[$name]['id'], 'state' => ModUtil::STATE_INACTIVE));
  615. } elseif ($dbmodules[$name]['state'] == ModUtil::STATE_INVALID && $modinfo['version']) {
  616. // module was invalid, now it is valid
  617. $item = $this->entityManager->getRepository($entity)->find($dbmodules[$name]['id']);
  618. $item['state'] = ModUtil::STATE_UNINITIALISED;
  619. $this->entityManager->flush();
  620. }
  621. if ($dbmodules[$name]['version'] != $modinfo['version']) {
  622. if ($dbmodules[$name]['state'] != ModUtil::STATE_UNINITIALISED &&
  623. $dbmodules[$name]['state'] != ModUtil::STATE_INVALID) {
  624. $this->setState(array('id' => $dbmodules[$name]['id'], 'state' => ModUtil::STATE_UPGRADED));
  625. }
  626. }
  627. }
  628. }
  629. // now clear re-load the dependencies table with all current dependencies
  630. $connection = $this->entityManager->getConnection();
  631. $platform = $connection->getDatabasePlatform();
  632. $connection->executeUpdate($platform->getTruncateTableSQL('module_deps', true));
  633. // loop round dependences adding the module id - we do this now rather than
  634. // earlier since we won't have the id's for new modules at that stage
  635. ModUtil::flushCache();
  636. foreach ($moddependencies as $modname => $moddependency) {
  637. $modid = ModUtil::getIdFromName($modname);
  638. // each module may have multiple dependencies
  639. foreach ($moddependency as $dependency) {
  640. $dependency['modid'] = $modid;
  641. $item = new \Zikula\Core\Doctrine\Entity\ExtensionDependency();
  642. $item->merge($dependency);
  643. $this->entityManager->persist($item);
  644. }
  645. }
  646. $this->entityManager->flush();
  647. return true;
  648. }
  649. /**
  650. * Initialise a module.
  651. *
  652. * @param array $args All parameters passed to this function.
  653. * numeric $args['id'] The module ID.
  654. * boolean $args['interactive_mode'] Perform the initialization in interactive mode or not.
  655. *
  656. * @return boolean|void True on success, false on failure, or null when we bypassed the installation;
  657. */
  658. public function initialise($args)
  659. {
  660. // Argument check
  661. if (!isset($args['id']) || !is_numeric($args['id'])) {
  662. throw new \InvalidArgumentException('Missing or invalid arguments');
  663. }
  664. // Get module information
  665. $modinfo = ModUtil::getInfo($args['id']);
  666. if (empty($modinfo)) {
  667. return LogUtil::registerError($this->__('Error! No such module ID exists.'));
  668. }
  669. switch ($modinfo['state']) {
  670. case ModUtil::STATE_NOTALLOWED:
  671. return LogUtil::registerError($this->__f('Error! No permission to install %s.', $modinfo['name']));
  672. break;
  673. default:
  674. if ($modinfo['state'] > 10) {
  675. return LogUtil::registerError($this->__f('Error! %s is not compatible with this version of Zikula.', $modinfo['name']));
  676. }
  677. }
  678. // Get module database info
  679. $osdir = DataUtil::formatForOS($modinfo['directory']);
  680. ModUtil::dbInfoLoad($modinfo['name'], $osdir);
  681. $modpath = ($modinfo['type'] == ModUtil::TYPE_SYSTEM) ? 'system' : 'modules';
  682. // load module maintainence functions
  683. ZLoader::addModule($osdir, $modpath);
  684. $bootstrap = "$modpath/$osdir/bootstrap.php";
  685. if (file_exists($bootstrap)) {
  686. include_once $bootstrap;
  687. }
  688. if ($modinfo['type'] == ModUtil::TYPE_MODULE) {
  689. if (is_dir("modules/$osdir/Resources/locale")) {
  690. ZLanguage::bindModuleDomain($modinfo['name']);
  691. }
  692. }
  693. $className = ucwords($modinfo['name']) . '\Installer';
  694. $reflectionInstaller = new \ReflectionClass($className);
  695. if (!$reflectionInstaller->isSubclassOf('Zikula\Framework\AbstractInstaller')) {
  696. LogUtil::registerError($this->__f("%s must be an instance of Zikula\Framework\AbstractInstaller",
  697. $className));
  698. }
  699. $installer = $reflectionInstaller->newInstance($this->container);
  700. $interactiveClass = ucwords($modinfo['name']) . '\Controller\Interactiveinstaller';
  701. $interactiveController = null;
  702. if (class_exists($interactiveClass)) {
  703. $reflectionInteractive = new \ReflectionClass($interactiveClass);
  704. if (!$reflectionInteractive->isSubclassOf('Zikula\Framework\Controller\AbstractInteractiveInstaller')) {
  705. LogUtil::registerError($this->__f("%s must be an instance of
  706. Zikula\Controller\AbstractInteractiveInstaller", $className));
  707. }
  708. $interactiveController = $reflectionInteractive->newInstance($this->container);
  709. }
  710. // perform the actual install of the module
  711. // system or module
  712. $func = array($installer, 'install');
  713. $interactive_func = array($interactiveController, 'install');
  714. // allow bypass of interactive install during a new installation only.
  715. if (System::isInstalling() && is_callable($interactive_func) && !is_callable($func)) {
  716. return; // return void here
  717. }
  718. if (!System::isInstalling() && isset($args['interactive_init']) && ($args['interactive_init'] == false)
  719. && is_callable($interactive_func)
  720. ) {
  721. // so we must check if the method actually exists by reflection - drak
  722. if ($reflectionInteractive->hasMethod('install')) {
  723. $this->request->getSession()->set('interactive_init', true);
  724. return call_user_func($interactive_func);
  725. }
  726. }
  727. // non-interactive
  728. if (is_callable($func)) {
  729. if (call_user_func($func) != true) {
  730. return false;
  731. }
  732. }
  733. // Update state of module
  734. if (!$this->setState(array('id' => $args['id'], 'state' => ModUtil::STATE_ACTIVE))) {
  735. return LogUtil::registerError($this->__('Error! Could not change module state.'));
  736. }
  737. if (!System::isInstalling()) {
  738. // This should become an event handler - drak
  739. $category = ModUtil::getVar('Admin', 'defaultcategory');
  740. ModUtil::apiFunc('AdminModule', 'admin', 'addmodtocategory', array('module' => $modinfo['name'], 'category' => $category));
  741. }
  742. // All went ok so issue installed event
  743. $event = new GenericEvent(null, $modinfo);
  744. $this->dispatcher->dispatch('installer.module.installed', $event);
  745. // Success
  746. return true;
  747. }
  748. /**
  749. * Upgrade a module.
  750. *
  751. * @param array $args All parameters passed to this function.
  752. * numeric $args['id'] The module ID.
  753. * boolean $args['interactive_upgrade'] Whether or not to upgrade in interactive mode.
  754. *
  755. * @return boolean True on success, false on failure.
  756. */
  757. public function upgrade($args)
  758. {
  759. // Argument check
  760. if (!isset($args['id']) || !is_numeric($args['id'])) {
  761. throw new \InvalidArgumentException('Missing or invalid arguments');
  762. }
  763. $entity = 'Zikula\Core\Doctrine\Entity\Extension';
  764. // Get module information
  765. $modinfo = ModUtil::getInfo($args['id']);
  766. if (empty($modinfo)) {
  767. return LogUtil::registerError($this->__('Error! No such module ID exists.'));
  768. }
  769. switch ($modinfo['state']) {
  770. case ModUtil::STATE_NOTALLOWED:
  771. return LogUtil::registerError($this->__f('Error! No permission to upgrade %s.', $modinfo['name']));
  772. break;
  773. default:
  774. if ($modinfo['state'] > 10) {
  775. return LogUtil::registerError($this->__f('Error! %s is not compatible with this version of Zikula.', $modinfo['name']));
  776. }
  777. }
  778. $osdir = DataUtil::formatForOS($modinfo['directory']);
  779. ModUtil::dbInfoLoad($modinfo['name'], $osdir);
  780. $modpath = ($modinfo['type'] == ModUtil::TYPE_SYSTEM) ? 'system' : 'modules';
  781. // load module maintainence functions
  782. ZLoader::addModule($osdir, $modpath);
  783. $bootstrap = "$modpath/$osdir/bootstrap.php";
  784. if (file_exists($bootstrap)) {
  785. include_once $bootstrap;
  786. }
  787. if ($modinfo['type'] == ModUtil::TYPE_MODULE) {
  788. if (is_dir("modules/$osdir/Resources/locale")) {
  789. ZLanguage::bindModuleDomain($modinfo['name']);
  790. }
  791. }
  792. $className = ucwords($modinfo['name']) . '\Installer';
  793. $reflectionInstaller = new \ReflectionClass($className);
  794. if (!$reflectionInstaller->isSubclassOf('Zikula\Framework\AbstractInstaller')) {
  795. LogUtil::registerError($this->__f("%s must be an instance of Zikula\Framework\AbstractInstaller",
  796. $className));
  797. }
  798. $installer = $reflectionInstaller->newInstanceArgs(array($this->container));
  799. $interactiveClass = ucwords($modinfo['name']) . '\Controller\Interactiveinstaller';
  800. $interactiveController = null;
  801. if (class_exists($interactiveClass)) {
  802. $reflectionInteractive = new \ReflectionClass($interactiveClass);
  803. if (!$reflectionInteractive->isSubclassOf('Zikula\Framework\Controller\AbstractInteractiveInstaller')) {
  804. LogUtil::registerError($this->__f("%s must be an instance of
  805. Zikula\Framework\Controller\AbstractInteractiveInstaller", $className));
  806. }
  807. $interactiveController = $reflectionInteractive->newInstance($this->container);
  808. }
  809. // perform the actual upgrade of the module
  810. $func = array($installer, 'upgrade');
  811. $interactive_func = array($interactiveController, 'upgrade');
  812. // allow bypass of interactive upgrade during a new installation only.
  813. if (System::isInstalling() && is_callable($interactive_func) && !is_callable($func)) {
  814. return; // return void here
  815. }
  816. if (isset($args['interactive_upgrade']) && $args['interactive_upgrade'] == false && is_callable($interactive_func)) {
  817. // Because interactive installers extend the AbstractController, is_callable will always return true because of the __call()
  818. // so we must check if the method actually exists by reflection - drak
  819. if ($reflectionInteractive->hasMethod('upgrade')) {
  820. $this->request->getSession()->set('interactive_upgrade', true);
  821. return call_user_func($interactive_func, array('oldversion' => $modinfo['version']));
  822. }
  823. }
  824. // non-interactive
  825. if (is_callable($func)) {
  826. $result = call_user_func($func, $modinfo['version']);
  827. if (is_string($result)) {
  828. if ($result != $modinfo['version']) {
  829. // update the last successful updated version
  830. $item = $this->entityManager->getRepository($entity)->find($modinfo['id']);
  831. $item['version'] = $result;
  832. $this->entityManager->flush();
  833. }
  834. return false;
  835. } elseif ($result != true) {
  836. return false;
  837. }
  838. }
  839. $modversion['version'] = '0';
  840. $modversion = Util::getVersionMeta($osdir, $modpath);
  841. $version = $modversion['version'];
  842. // Update state of module
  843. $result = $this->setState(array('id' => $args['id'], 'state' => ModUtil::STATE_ACTIVE));
  844. if ($result) {
  845. LogUtil::registerStatus($this->__("Done! Module has been upgraded. Its status is now 'Active'."));
  846. } else {
  847. return false;
  848. }
  849. // update the module with the new version
  850. $item = $this->entityManager->getRepository($entity)->find($args['id']);
  851. $item['version'] = $version;
  852. $this->entityManager->flush();
  853. // Upgrade succeeded, issue event.
  854. $event = new GenericEvent(null, $modinfo);
  855. $this->dispatcher->dispatch('installer.module.upgraded', $event);
  856. // Success
  857. return true;
  858. }
  859. /**
  860. * Upgrade all modules.
  861. *
  862. * @return array An array of upgrade results, indexed by module name.
  863. */
  864. public function upgradeall()
  865. {
  866. $upgradeResults = array();
  867. // regenerate modules list
  868. $filemodules = $this->getfilemodules();
  869. $this->regenerate(array('filemodules' => $filemodules));
  870. // get a list of modules needing upgrading
  871. if ($this->listmodules(array('state' => ModUtil::STATE_UPGRADED))) {
  872. $newmods = $this->listmodules(array('state' => ModUtil::STATE_UPGRADED));
  873. // Sort upgrade order according to this list.
  874. $priorities = array('Extensions', 'Users' , 'Groups', 'Permissions', 'Admin', 'Blocks', 'Theme', 'Settings', 'Categories', 'SecurityCenter', 'Errors');
  875. $sortedList = array();
  876. foreach ($priorities as $priority) {
  877. foreach ($newmods as $key => $modinfo) {
  878. if ($modinfo['name'] == $priority) {
  879. $sortedList[] = $modinfo;
  880. unset($newmods[$key]);
  881. }
  882. }
  883. }
  884. $newmods = array_merge($sortedList, $newmods);
  885. foreach ($newmods as $mod) {
  886. $upgradeResults[$mod['name']] = $this->upgrade(array('id' => $mod['id']));
  887. }
  888. System::setVar('Version_Num', Core::VERSION_NUM);
  889. }
  890. return $upgradeResults;
  891. }
  892. /**
  893. * Utility function to count the number of items held by this module.
  894. *
  895. * @param array $args All parameters passed to this function.
  896. * string $args['letter'] Filter the count by the first letter of the module name; optional.
  897. * integer $args['state'] Filter the count by the module state; optional.
  898. *
  899. * @return integer The number of items held by this module.
  900. */
  901. public function countitems($args)
  902. {
  903. // create a QueryBuilder instance
  904. $qb = $this->entityManager->createQueryBuilder();
  905. // add select and from params
  906. $qb->select('COUNT(e.id)')
  907. ->from('Zikula\Core\Doctrine\Entity\Extension', 'e');
  908. // filter by first letter of module
  909. if (isset($args['letter']) && !empty($args['letter'])) {
  910. $clause1 = $qb->expr()->like('e.name', $qb->expr()->literal($args['letter'] . '%'));
  911. $clause2 = $qb->expr()->like('e.name', $qb->expr()->literal(strtolower($args['letter']) . '%'));
  912. $qb->andWhere($clause1 . ' OR ' . $clause2);
  913. }
  914. // filter by type
  915. $type = (empty($args['type']) || $args['type'] < 0 || $args['type'] > ModUtil::TYPE_SYSTEM) ? 0 : (int)$args['type'];
  916. if ($type != 0) {
  917. $qb->andWhere($qb->expr()->eq('e.type', $qb->expr()->literal($type)));
  918. }
  919. if ($this->container['multisites.enabled'] == 1) {
  920. $state = (empty($args['state']) || $args['state'] < -1 || $args['state'] > ModUtil::STATE_NOTALLOWED) ? 0 : (int)$args['state'];
  921. } else {
  922. $state = (empty($args['state']) || $args['state'] < -1 || $args['state'] > ModUtil::STATE_UPGRADED) ? 0 : (int)$args['state'];
  923. }
  924. // filter by module state
  925. if ($this->container['multisites.enabled'] == 1) {
  926. $state = (empty($args['state']) || $args['state'] < -1 || $args['state'] > ModUtil::STATE_NOTALLOWED) ? 0 : (int)$args['state'];
  927. } else {
  928. $state = (empty($args['state']) || $args['state'] < -1 || $args['state'] > ModUtil::STATE_UPGRADED) ? 0 : (int)$args['state'];
  929. }
  930. switch ($state) {
  931. case ModUtil::STATE_UNINITIALISED:
  932. case ModUtil::STATE_INACTIVE:
  933. case ModUtil::STATE_ACTIVE:
  934. case ModUtil::STATE_MISSING:
  935. case ModUtil::STATE_UPGRADED:
  936. case ModUtil::STATE_NOTALLOWED:
  937. case ModUtil::STATE_INVALID:
  938. $qb->andWhere($qb->expr()->eq('e.state', $qb->expr()->literal($state)));
  939. break;
  940. case 10:
  941. $qb->andWhere($qb->expr()->gt('e.state', 10));
  942. break;
  943. }
  944. $query = $qb->getQuery();
  945. $count = $query->getSingleScalarResult();
  946. return (int)$count;
  947. }
  948. /**
  949. * Get available admin panel links.
  950. *
  951. * @return array An array of admin links.
  952. */
  953. public function getlinks()
  954. {
  955. $links = array();
  956. if (SecurityUtil::checkPermission('Extensions::', '::', ACCESS_ADMIN)) {
  957. $links[] = array('url' => ModUtil::url('Extensions', 'admin', 'view'),
  958. 'text' => $this->__('Modules list'),
  959. 'class' => 'z-icon-es-view',
  960. 'links' => array(
  961. array('url' => ModUtil::url('Extensions', 'admin', 'view'),
  962. 'text' => $this->__('All')),
  963. array('url' => ModUtil::url('Extensions', 'admin', 'view', array('state'=>ModUtil::STATE_UNINITIALISED)),
  964. 'text' => $this->__('Not installed')),
  965. array('url' => ModUtil::url('Extensions', 'admin', 'view', array('state'=>ModUtil::STATE_INACTIVE)),
  966. 'text' => $this->__('Inactive')),
  967. array('url' => ModUtil::url('Extensions', 'admin', 'view', array('state'=>ModUtil::STATE_ACTIVE)),
  968. 'text' => $this->__('Active')),
  969. array('url' => ModUtil::url('Extensions', 'admin', 'view', array('state'=>ModUtil::STATE_MISSING)),
  970. 'text' => $this->__('Files missing')),
  971. array('url' => ModUtil::url('Extensions', 'admin', 'view', array('state'=>ModUtil::STATE_UPGRADED)),
  972. 'text' => $this->__('New version uploaded')),
  973. array('url' => ModUtil::url('Extensions', 'admin', 'view', array('state'=>ModUtil::STATE_INVALID)),
  974. 'text' => $this->__('Invalid structure'))
  975. ));
  976. $links[] = array('url' => ModUtil::url('Extensions', 'admin', 'viewPlugins'),
  977. 'text' => $this->__('Plugins list'),
  978. 'class' => 'z-icon-es-gears',
  979. 'links' => array(
  980. array('url' => ModUtil::url('Extensions', 'admin', 'viewPlugins'),
  981. 'text' => $this->__('All')),
  982. array('url' => ModUtil::url('Extensions', 'admin', 'viewPlugins', array('state'=>PluginUtil::NOTINSTALLED)),
  983. 'text' => $this->__('Not installed')),
  984. array('url' => ModUtil::url('Extensions', 'admin', 'viewPlugins', array('state'=>PluginUtil::DISABLED)),
  985. 'text' => $this->__('Inactive')),
  986. array('url' => ModUtil::url('Extensions', 'admin', 'viewPlugins', array('state'=>PluginUtil::ENABLED)),
  987. 'text' => $this->__('Active'))
  988. ));
  989. $links[] = array('url' => ModUtil::url('Extensions', 'admin', 'viewPlugins', array('systemplugins' => true)),
  990. 'text' => $this->__('System Plugins'),
  991. 'class' => 'z-icon-es-gears',
  992. 'links' => array(
  993. array('url' => ModUtil::url('Extensions', 'admin', 'viewPlugins', array('systemplugins' => true)),
  994. 'text' => $this->__('All')),
  995. array('url' => ModUtil::url('Extensions', 'admin', 'viewPlugins', array('systemplugins' => true, 'state'=>PluginUtil::NOTINSTALLED)),
  996. 'text' => $this->__('Not installed')),
  997. array('url' => ModUtil::url('Extensions', 'admin', 'viewPlugins', array('systemplugins' => true, 'state'=>PluginUtil::DISABLED)),
  998. 'text' => $this->__('Inactive')),
  999. array('url' => ModUtil::url('Extensions', 'admin', 'viewPlugins', array('systemplugins' => true, 'state'=>PluginUtil::ENABLED)),
  1000. 'text' => $this->__('Active'))
  1001. ));
  1002. $links[] = array('url' => ModUtil::url('Extensions', 'admin', 'modifyconfig'), 'text' => $this->__('Settings'), 'class' => 'z-icon-es-config');
  1003. //$filemodules = ModUtil::apiFunc('ExtensionsModule', 'admin', 'getfilemodules');
  1004. //ModUtil::apiFunc('ExtensionsModule', 'admin', 'regenerate', array('filemodules' => $filemodules));
  1005. // get a list of modules needing upgrading
  1006. $newmods = ModUtil::apiFunc('ExtensionsModule', 'admin', 'listmodules', array('state' => ModUtil::STATE_UPGRADED));
  1007. if ($newmods) {
  1008. $links[] = array('url' => ModUtil::url('Extensions', 'admin', 'upgradeall'), 'text' => $this->__('Upgrade All'), 'class' => 'z-icon-es-config');
  1009. }
  1010. }
  1011. return $links;
  1012. }
  1013. /**
  1014. * Get all module dependencies.
  1015. *
  1016. * @param array $args All parameters sent to this function (not currently used).
  1017. *
  1018. * @return array Array of dependencies.
  1019. */
  1020. public function getdallependencies()
  1021. {
  1022. $dependencies = $this->entityManager->getRepository('Zikula\Core\Doctrine\Entity\ExtensionDependency')->findBy(array(), array('modid' => 'ASC'));
  1023. return $dependencies;
  1024. }
  1025. /**
  1026. * Get dependencies for a module.
  1027. *
  1028. * @param array $args All parameters sent to this function.
  1029. * numeric $args['modid'] Id of module to get dependencies for.
  1030. *
  1031. * @return array|boolean Array of dependencies; false otherwise.
  1032. */
  1033. public function getdependencies($args)
  1034. {
  1035. // Argument check
  1036. if (!isset($args['modid']) || empty($args['modid']) || !is_numeric($args['modid'])) {
  1037. throw new \InvalidArgumentException('Missing or invalid arguments');
  1038. }
  1039. $dependencies = $this->entityManager->getRepository('Zikula\Core\Doctrine\Entity\ExtensionDependency')->findBy(array('modid' => $args['modid']));
  1040. return $dependencies;
  1041. }
  1042. /**
  1043. * Get dependents of a module.
  1044. *
  1045. * @param array $args All parameters passed to this function.
  1046. * numeric $args['modid'] Id of module to get dependents for.
  1047. *
  1048. * @return array|boolean Array of dependents; false otherwise.
  1049. */
  1050. public function getdependents($args)
  1051. {
  1052. // Argument check
  1053. if (!isset($args['modid']) || empty($args['modid']) || !is_numeric($args['modid'])) {
  1054. throw new \InvalidArgumentException('Missing or invalid arguments');
  1055. }
  1056. $modinfo = ModUtil::getInfo($args['modid']);
  1057. $dependents = $this->entityManager->getRepository('Zikula\Core\Doctrine\Entity\ExtensionDependency')->findBy(array('modname' => $modinfo['name']));
  1058. return $dependents;
  1059. }
  1060. /**
  1061. * Check modules for consistency.
  1062. *
  1063. * @param array $args All parameters passed to this function.
  1064. * array $args['filemodules'] Array of modules in the filesystem, as returned by {@link getfilemodules()}.
  1065. *
  1066. * @see getfilemodules()
  1067. *
  1068. * @return array An array of arrays with links to inconsistencies.
  1069. */
  1070. public function checkconsistency($args)
  1071. {
  1072. // Security check
  1073. if (!System::isInstalling()) {
  1074. if (!SecurityUtil::checkPermission('Extensions::', '::', ACCESS_ADMIN)) {
  1075. throw new \Zikula\Framework\Exception\ForbiddenException();
  1076. }
  1077. }
  1078. // Argument check
  1079. if (!isset($args['filemodules']) || !is_array($args['filemodules'])) {
  1080. throw new \InvalidArgumentException('Missing or invalid arguments');
  1081. }
  1082. $filemodules = $args['filemodules'];
  1083. $modulenames = array();
  1084. $displaynames = array();
  1085. $errors_modulenames = array();
  1086. $errors_displaynames = array();
  1087. // check for duplicate names or display names
  1088. foreach ($filemodules as $dir => $modinfo) {
  1089. if (isset($modulenames[strtolower($modinfo['name'])])) {
  1090. $errors_modulenames[] = array('name' => $modinfo['name'],
  1091. 'dir1' => $modulenames[strtolower($modinfo['name'])],
  1092. 'dir2' => $dir);
  1093. }
  1094. if (isset($displaynames[strtolower($modinfo['displayname'])])) {
  1095. $errors_displaynames[] = array('name' => $modinfo['displayname'],
  1096. 'dir1' => $displaynames[strtolower($modinfo['displayname'])],
  1097. 'dir2' => $dir);
  1098. }
  1099. if (isset($displaynames[strtolower($modinfo['url'])])) {
  1100. $errors_displaynames[] = array('name' => $modinfo['url'],
  1101. 'dir1' => $displaynames[strtolower($modinfo['url'])],
  1102. 'dir2' => $dir);
  1103. }
  1104. $modulenames[strtolower($modinfo['name'])] = $dir;
  1105. $displaynames[strtolower($modinfo['displayname'])] = $dir;
  1106. }
  1107. // do we need to check for duplicate oldnames as well?
  1108. return array('errors_modulenames' => $errors_modulenames,
  1109. 'errors_displaynames' => $errors_displaynames);
  1110. }
  1111. /**
  1112. * Check if a module comes from the core.
  1113. *
  1114. * @param array $args All parameters sent to this function.
  1115. * string $args['modulename'] The name of the module to check.
  1116. *
  1117. * @return boolean True if it's a core module; otherwise false.
  1118. */
  1119. public function iscoremodule($args)
  1120. {
  1121. static $coreModules;
  1122. if (!isset($coreModules)) {
  1123. $coreModules = array(
  1124. 'Admin',
  1125. 'Blocks',
  1126. 'Categories',
  1127. 'Errors',
  1128. 'Groups',
  1129. 'Mailer',
  1130. 'Extensions',
  1131. 'Permissions',
  1132. 'SecurityCenter',
  1133. 'Settings',
  1134. 'Theme',
  1135. 'Users',
  1136. );
  1137. }
  1138. if (in_array($args['modulename'], $coreModules)) {
  1139. return true;
  1140. }
  1141. return false;
  1142. }
  1143. }