Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
...
  • 7 commits
  • 43 files changed
  • 0 commit comments
  • 2 contributors
Commits on Apr 15, 2012
@planetenkiller planetenkiller added simple implementation of a module system
Modules == Symfony bundles
Custom kernel loads active modules from DB. This custom kernel add bundles to the bundle list *before* symfony's bundle loading starts.
A custom routing loader automatically add modules routes (controllers of modules have to use annotations).
7754293
@planetenkiller planetenkiller stop module loading in command line mode 78420be
@planetenkiller planetenkiller added missing file ed715ca
@planetenkiller planetenkiller improved module system, added themes support 2f9d7d4
@planetenkiller planetenkiller switch to new bundle loading approach
Zikula kernel loads all bundles (modules and themes) every time.
A custom event dispatcher skipps service listeners of inactive modules.
A custom controller resolver skipps controllers of inactive modules.
A container builder sandbox forbidds taggs other than 'kernel.event_listener'.
848aff7
Drak Relocate files b2a650c
Drak Apply naming conventions. f72c85d
Showing with 1,819 additions and 0 deletions.
  1. +19 −0 src/Zikula/Bundle/ModuleBundle/Controller/DefaultController.php
  2. +154 −0 src/Zikula/Bundle/ModuleBundle/Controller/ModuleController.php
  3. +124 −0 src/Zikula/Bundle/ModuleBundle/DefaultModuleService.php
  4. +17 −0 src/Zikula/Bundle/ModuleBundle/DependencyInjection/Compiler/ControllerResolverCompilerPass.php
  5. +29 −0 src/Zikula/Bundle/ModuleBundle/DependencyInjection/Configuration.php
  6. +32 −0 src/Zikula/Bundle/ModuleBundle/DependencyInjection/ReadOnlyDefinition.php
  7. +303 −0 src/Zikula/Bundle/ModuleBundle/DependencyInjection/SandboxContainerBuilder.php
  8. +45 −0 src/Zikula/Bundle/ModuleBundle/DependencyInjection/SandboxContainerExtension.php
  9. +28 −0 src/Zikula/Bundle/ModuleBundle/DependencyInjection/ZikulaModulesExtension.php
  10. +121 −0 src/Zikula/Bundle/ModuleBundle/Entity/Module.php
  11. +46 −0 src/Zikula/Bundle/ModuleBundle/Entity/Module.php~
  12. +10 −0 src/Zikula/Bundle/ModuleBundle/Exception/InvalidModuleStructureException.php
  13. +10 −0 src/Zikula/Bundle/ModuleBundle/Exception/InvalidThemeStructureException.php
  14. +21 −0 src/Zikula/Bundle/ModuleBundle/Form/ModuleType.php
  15. +43 −0 src/Zikula/Bundle/ModuleBundle/ModuleAwareControllerResolver.php
  16. +39 −0 src/Zikula/Bundle/ModuleBundle/ModuleAwareEventDispatcher.php
  17. +38 −0 src/Zikula/Bundle/ModuleBundle/ModuleAwareTraceableEventDispatcher.php
  18. +17 −0 src/Zikula/Bundle/ModuleBundle/ModuleInstallerInterface.php
  19. +43 −0 src/Zikula/Bundle/ModuleBundle/ModuleService/DoctrineStorage.php
  20. +24 −0 src/Zikula/Bundle/ModuleBundle/ModuleService/StorageInterface.php
  21. +14 −0 src/Zikula/Bundle/ModuleBundle/ModuleServiceInterface.php
  22. +32 −0 src/Zikula/Bundle/ModuleBundle/Resources/config/services.xml
  23. 0  src/Zikula/Bundle/ModuleBundle/Resources/public/js/test.js
  24. +1 −0  src/Zikula/Bundle/ModuleBundle/Resources/views/Default/index.html.twig
  25. +22 −0 src/Zikula/Bundle/ModuleBundle/Resources/views/Module/edit.html.twig
  26. +33 −0 src/Zikula/Bundle/ModuleBundle/Resources/views/Module/index.html.twig
  27. +16 −0 src/Zikula/Bundle/ModuleBundle/Resources/views/Module/new.html.twig
  28. +41 −0 src/Zikula/Bundle/ModuleBundle/Resources/views/Module/show.html.twig
  29. +17 −0 src/Zikula/Bundle/ModuleBundle/Tests/Controller/DefaultControllerTest.php
  30. +54 −0 src/Zikula/Bundle/ModuleBundle/Tests/Controller/ModuleControllerTest.php
  31. +157 −0 src/Zikula/Bundle/ModuleBundle/ZikulaKernel.php
  32. +46 −0 src/Zikula/Bundle/ModuleBundle/ZikulaModule.php
  33. +14 −0 src/Zikula/Bundle/ModuleBundle/ZikulaModuleBundle.php
  34. +31 −0 src/Zikula/Bundle/ModuleBundle/ZikulaModuleRoutesLoader.php
  35. +15 −0 src/Zikula/Bundle/ThemeBundle/Controller/DefaultController.php
  36. +29 −0 src/Zikula/Bundle/ThemeBundle/DependencyInjection/Configuration.php
  37. +28 −0 src/Zikula/Bundle/ThemeBundle/DependencyInjection/ZikulaThemesExtension.php
  38. +38 −0 src/Zikula/Bundle/ThemeBundle/EventListener/ThemeRenderer.php
  39. +13 −0 src/Zikula/Bundle/ThemeBundle/Resources/config/services.xml
  40. +5 −0 src/Zikula/Bundle/ThemeBundle/Resources/views/Default/welcome.html.twig
  41. +17 −0 src/Zikula/Bundle/ThemeBundle/Tests/Controller/DefaultControllerTest.php
  42. +24 −0 src/Zikula/Bundle/ThemeBundle/ZikulaTheme.php
  43. +9 −0 src/Zikula/Bundle/ThemeBundle/ZikulaThemeBundle.php
View
19 src/Zikula/Bundle/ModuleBundle/Controller/DefaultController.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Zikula\ModuleBundle\Controller;
+
+use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
+
+class DefaultController extends Controller
+{
+ /**
+ * @Route("/zhello/{name}")
+ * @Template()
+ */
+ public function indexAction($name)
+ {
+ return array('name' => $name);
+ }
+}
View
154 src/Zikula/Bundle/ModuleBundle/Controller/ModuleController.php
@@ -0,0 +1,154 @@
+<?php
+
+namespace Zikula\ModuleBundle\Controller;
+
+use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
+use Zikula\ModuleBundle\Entity\Module;
+use Zikula\ModuleBundle\Form\ModuleType;
+
+/**
+ * Module controller.
+ *
+ * @Route("/module")
+ */
+class ModuleController extends Controller
+{
+ /**
+ * Lists all Module entities.
+ *
+ * @Route("/", name="module")
+ * @Template()
+ */
+ public function indexAction()
+ {
+ $this->get('zikula.modules')->regenerateModuleList();
+ $entities = $this->get('zikula.modules')->getAllModules();
+
+ return array('entities' => $entities);
+ }
+
+ /**
+ * Finds and displays a Module entity.
+ *
+ * @Route("/{id}/show", name="module_show")
+ * @Template()
+ */
+ public function showAction($id)
+ {
+ $em = $this->getDoctrine()->getEntityManager();
+
+ $entity = $em->getRepository('ZikulaModulesBundle:Module')->find($id);
+
+ if (!$entity) {
+ throw $this->createNotFoundException('Unable to find Module entity.');
+ }
+
+ $deleteForm = $this->createDeleteForm($id);
+
+ return array(
+ 'entity' => $entity,
+ 'delete_form' => $deleteForm->createView(), );
+ }
+
+ /**
+ * Displays a form to edit an existing Module entity.
+ *
+ * @Route("/{id}/edit", name="module_edit")
+ * @Template()
+ */
+ public function editAction($id)
+ {
+ $em = $this->getDoctrine()->getEntityManager();
+
+ $entity = $em->getRepository('ZikulaModulesBundle:Module')->find($id);
+
+ if (!$entity) {
+ throw $this->createNotFoundException('Unable to find Module entity.');
+ }
+
+ $editForm = $this->createForm(new ModuleType(), $entity);
+ $deleteForm = $this->createDeleteForm($id);
+
+ return array(
+ 'entity' => $entity,
+ 'edit_form' => $editForm->createView(),
+ 'delete_form' => $deleteForm->createView(),
+ );
+ }
+
+ /**
+ * Edits an existing Module entity.
+ *
+ * @Route("/{id}/update", name="module_update")
+ * @Method("post")
+ * @Template("ZikulaModulesBundle:Module:edit.html.twig")
+ */
+ public function updateAction($id)
+ {
+ $em = $this->getDoctrine()->getEntityManager();
+
+ $entity = $em->getRepository('ZikulaModulesBundle:Module')->find($id);
+
+ if (!$entity) {
+ throw $this->createNotFoundException('Unable to find Module entity.');
+ }
+
+ $editForm = $this->createForm(new ModuleType(), $entity);
+ $deleteForm = $this->createDeleteForm($id);
+
+ $request = $this->getRequest();
+
+ $editForm->bindRequest($request);
+
+ if ($editForm->isValid()) {
+ $this->get('zikula.modules')->update($entity);
+
+ return $this->redirect($this->generateUrl('module_edit', array('id' => $id)));
+ }
+
+ return array(
+ 'entity' => $entity,
+ 'edit_form' => $editForm->createView(),
+ 'delete_form' => $deleteForm->createView(),
+ );
+ }
+
+ /**
+ * Deletes a Module entity.
+ *
+ * @Route("/{id}/delete", name="module_delete")
+ * @Method("post")
+ */
+ public function deleteAction($id)
+ {
+ $form = $this->createDeleteForm($id);
+ $request = $this->getRequest();
+
+ $form->bindRequest($request);
+
+ if ($form->isValid()) {
+ $em = $this->getDoctrine()->getEntityManager();
+ $entity = $em->getRepository('ZikulaModulesBundle:Module')->find($id);
+
+ if (!$entity) {
+ throw $this->createNotFoundException('Unable to find Module entity.');
+ }
+
+ $em->remove($entity);
+ $em->flush();
+ }
+
+ return $this->redirect($this->generateUrl('module'));
+ }
+
+ private function createDeleteForm($id)
+ {
+ return $this->createFormBuilder(array('id' => $id))
+ ->add('id', 'hidden')
+ ->getForm()
+ ;
+ }
+}
View
124 src/Zikula/Bundle/ModuleBundle/DefaultModuleService.php
@@ -0,0 +1,124 @@
+<?php
+
+namespace Zikula\ModuleBundle;
+
+use Doctrine\ORM\EntityManager;
+
+/**
+ * Default module service implementation.
+ */
+class DefaultModuleService implements ModuleServiceInterface
+{
+ /**
+ * @var ModuleService\StorageInterface
+ */
+ private $storage;
+
+ /**
+ * @var String
+ */
+ private $dirWithModules;
+
+ /**
+ * @var EntityManager
+ */
+ private $em;
+
+ public function __construct(ModuleService\StorageInterface $storage, EntityManager $em, $dirWithModules=null)
+ {
+ $this->em = $em;
+ $this->storage = $storage;
+ $this->dirWithModules = __DIR__ . '/../../../modules/'; // default
+
+ if(!empty($dirWithModules)) {
+ $this->dirWithModules = $dirWithModules;
+ }
+ }
+
+ public function regenerateModuleList()
+ {
+ $dir = new \DirectoryIterator($this->dirWithModules);
+
+ $modules = array();
+ foreach($dir as $fileInfo) {
+ /* @var $fileInfo \SplFileInfo */
+
+ if($fileInfo->isDir() && !$fileInfo->isDot()) {
+ $class = $fileInfo->getFilename() . '\\' . $fileInfo->getFilename() . 'Module';
+ $module = new $class();
+
+ if(!$module instanceof ZikulaModule) {
+ throw new Exception\InvalidModuleStructureException($fileInfo->getFilename());
+ }
+
+ $modules[] = $module;
+ }
+ }
+
+ $modulesInDb = $this->storage->getAll();
+
+ foreach($modules as $module) {
+ $dbmodule = null;
+ foreach($modulesInDb as $moduleInDb) {
+ if($moduleInDb->getName() == $module->getName()) {
+ $dbmodule = $moduleInDb;
+ }
+ }
+
+ if($dbmodule) {
+ if($dbmodule->getVersion() != $module->getVersion()) {
+ $dbmodule->setState(Entity\Module::STATE_NEED_UPGRADE);
+ }
+ $dbmodule->setVersion($module->getVersion());
+ $this->storage->update($dbmodule);
+ } else {
+ $dbmodule = new Entity\Module();
+ $dbmodule->setName($module->getName());
+ $dbmodule->setVersion($module->getVersion());
+ $dbmodule->setState(Entity\Module::STATE_NEW);
+ $this->storage->insert($dbmodule);
+ }
+ }
+ }
+
+ public function getAllModules()
+ {
+ return $this->storage->getAll();
+ }
+
+ public function getModule($id)
+ {
+ return $this->storage->get($id);
+ }
+
+ public function installModule($id)
+ {
+ $module = $this->getModule($id);
+
+ if(!$module) {
+ throw new \InvalidArgumentException(sprintf('No module with $id %s', $id));
+ }
+
+ $class = $module->getName() . '\\' . $module->getName() . 'Module';
+
+ $moduleObject = new $class();
+ $installer = $moduleObject->createInstaller();
+
+ $entities = $installer->entitiesToInstall();
+ foreach($entities as $key => $class) {
+ //FIXME: can we use symfony DIC to get a metadatafactory instance?
+ $entities[$key] = $this->em->getMetadataFactory()->getMetadataFor($class);
+ }
+
+ if(count($entities) > 0) {
+ //TODO: move to own class or class attribute to remove hard coded dependency to SchemaTool
+ $schemaTool = new \Doctrine\ORM\Tools\SchemaTool($this->em);
+ $schemaTool->createSchema($entities);
+ }
+
+ $installer->install();
+
+ $module->setState(Entity\Module::STATE_ACTIVE);
+ $this->storage->update($module);
+ }
+}
View
17 ...ikula/Bundle/ModuleBundle/DependencyInjection/Compiler/ControllerResolverCompilerPass.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Zikula\ModuleBundle\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+/**
+ *
+ */
+class ControllerResolverCompilerPass implements \Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface
+{
+
+ public function process(ContainerBuilder $container)
+ {
+ $container->getDefinition('http_kernel')->replaceArgument(2, new \Symfony\Component\DependencyInjection\Reference('zikula.controller.resolver'));
+ }
+}
View
29 src/Zikula/Bundle/ModuleBundle/DependencyInjection/Configuration.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Zikula\ModuleBundle\DependencyInjection;
+
+use Symfony\Component\Config\Definition\Builder\TreeBuilder;
+use Symfony\Component\Config\Definition\ConfigurationInterface;
+
+/**
+ * This is the class that validates and merges configuration from your app/config files
+ *
+ * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
+ */
+class Configuration implements ConfigurationInterface
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function getConfigTreeBuilder()
+ {
+ $treeBuilder = new TreeBuilder();
+ $rootNode = $treeBuilder->root('zikula_modules');
+
+ // Here you should define the parameters that are allowed to
+ // configure your bundle. See the documentation linked above for
+ // more information on that topic.
+
+ return $treeBuilder;
+ }
+}
View
32 src/Zikula/Bundle/ModuleBundle/DependencyInjection/ReadOnlyDefinition.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Zikula\ModuleBundle\DependencyInjection;
+
+/**
+ *
+ */
+class ReadOnlyDefinition
+{
+ /**
+ * @var \Symfony\Component\DependencyInjection\Definition
+ */
+ private $delegate;
+
+ public function __construct($delegate)
+ {
+ $this->delegate = $delegate;
+ }
+
+ public function __call($name, $arguments)
+ {
+ if(substr($name, 0, 3) == 'set'
+ || substr($name, 0, 3) == 'add'
+ || substr($name, 0, 6) == 'remove'
+ || substr($name, 0, 4) == 'clear'
+ || substr($name, 0, 7) == 'replace') {
+ throw new \LogicException('forbidden');
+ }
+
+ return call_user_func_array(array($this->delegate, $name), $arguments);
+ }
+}
View
303 src/Zikula/Bundle/ModuleBundle/DependencyInjection/SandboxContainerBuilder.php
@@ -0,0 +1,303 @@
+<?php
+
+namespace Zikula\ModuleBundle\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\DependencyInjection\Compiler\PassConfig;
+
+/**
+ *
+ */
+class SandboxContainerBuilder extends ContainerBuilder
+{
+ /**
+ * @var ContainerBuilder
+ */
+ private $delegate;
+
+ private $ownServiceIds = array();
+
+ public function __construct($delegate, &$ownServiceIds)
+ {
+ $this->delegate = $delegate;
+ $this->ownServiceIds =& $ownServiceIds;
+ }
+
+ public function registerExtension(\Symfony\Component\DependencyInjection\Extension\ExtensionInterface $extension)
+ {
+ throw new \LogicException('forbidden');
+ }
+
+ public function addCompilerPass(\Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION)
+ {
+ throw new \LogicException('forbidden');
+ }
+
+ public function merge(ContainerBuilder $container)
+ {
+ throw new \LogicException('forbidden');
+ }
+
+ public function set($id, $service, $scope = self::SCOPE_CONTAINER)
+ {
+ if(!in_array($id, $this->ownServiceIds)) {
+ if($this->delegate->has($id)) {
+ throw new \LogicException('forbidden');
+ } else {
+ $this->ownServiceIds[] = $id;
+ }
+ }
+
+ $this->delegate->set($id, $service, $scope);
+ }
+
+ public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE)
+ {
+ if(!in_array($id, $this->ownServiceIds)) {
+ throw new \LogicException('forbidden');
+ }
+
+ return $this->delegate->get($id, $invalidBehavior);
+ }
+
+ public function setDefinition($id, \Symfony\Component\DependencyInjection\Definition $definition)
+ {
+ if(!in_array($id, $this->ownServiceIds)) {
+ if($this->delegate->hasDefinition($id)) {
+ throw new \LogicException('forbidden');
+ } else {
+ $this->ownServiceIds[] = $id;
+ }
+ }
+
+ foreach($definition->getTags() as $tag => $attributes) {
+ if($tag != 'kernel.event_listener') {
+ throw new \LogicException('forbidden');
+ }
+ }
+
+ $this->delegate->setDefinition($id, $definition);
+ }
+
+ public function getDefinition($id)
+ {
+ $def = $this->delegate->getDefinition($id);
+
+ if(!in_array($id, $this->ownServiceIds)) {
+ throw new \LogicException('forbidden');
+ }
+
+ return $def;
+ }
+
+ public function addAliases(array $aliases)
+ {
+ return $this->delegate->addAliases($aliases);
+ }
+
+ public function addDefinitions(array $definitions)
+ {
+ return $this->delegate->addDefinitions($definitions);
+ }
+
+ public function addObjectResource($object)
+ {
+ return $this->delegate->addObjectResource($object);
+ }
+
+ public function addResource(\Symfony\Component\Config\Resource\ResourceInterface $resource)
+ {
+ return $this->delegate->addResource($resource);
+ }
+
+ public function addScope(\Symfony\Component\DependencyInjection\ScopeInterface $scope)
+ {
+ return $this->delegate->addScope($scope);
+ }
+
+ public function compile()
+ {
+ return $this->delegate->compile();
+ }
+
+ public function enterScope($name)
+ {
+ return $this->delegate->enterScope($name);
+ }
+
+ public function findDefinition($id)
+ {
+ return $this->delegate->findDefinition($id);
+ }
+
+ public function findTaggedServiceIds($name)
+ {
+ return $this->delegate->findTaggedServiceIds($name);
+ }
+
+ public function getAlias($id)
+ {
+ return $this->delegate->getAlias($id);
+ }
+
+ public function getAliases()
+ {
+ return $this->delegate->getAliases();
+ }
+
+ public function getCompiler()
+ {
+ return $this->delegate->getCompiler();
+ }
+
+ public function getCompilerPassConfig()
+ {
+ return $this->delegate->getCompilerPassConfig();
+ }
+
+ public function getDefinitions()
+ {
+ return $this->delegate->getDefinitions();
+ }
+
+ public function getExtension($name)
+ {
+ return $this->delegate->getExtension($name);
+ }
+
+ public function getExtensionConfig($name)
+ {
+ return $this->delegate->getExtensionConfig($name);
+ }
+
+ public function getExtensions()
+ {
+ return $this->delegate->getExtensions();
+ }
+
+ public function getParameter($name)
+ {
+ return $this->delegate->getParameter($name);
+ }
+
+ public function getParameterBag()
+ {
+ return $this->delegate->getParameterBag();
+ }
+
+ public function getResources()
+ {
+ return $this->delegate->getResources();
+ }
+
+ public function getScopeChildren()
+ {
+ return $this->delegate->getScopeChildren();
+ }
+
+ public function getScopes()
+ {
+ return $this->delegate->getScopes();
+ }
+
+ public static function getServiceConditionals($value)
+ {
+ return $this->delegate->getServiceConditionals($value);
+ }
+
+ public function getServiceIds()
+ {
+ return $this->delegate->getServiceIds();
+ }
+
+ public function has($id)
+ {
+ return $this->delegate->has($id);
+ }
+
+ public function hasAlias($id)
+ {
+ return $this->delegate->hasAlias($id);
+ }
+
+ public function hasDefinition($id)
+ {
+ return $this->delegate->hasDefinition($id);
+ }
+
+ public function hasExtension($name)
+ {
+ return $this->delegate->hasExtension($name);
+ }
+
+ public function hasParameter($name)
+ {
+ return $this->delegate->hasParameter($name);
+ }
+
+ public function hasScope($name)
+ {
+ return $this->delegate->hasScope($name);
+ }
+
+ public function isFrozen()
+ {
+ return $this->delegate->isFrozen();
+ }
+
+ public function isScopeActive($name)
+ {
+ return $this->delegate->isScopeActive($name);
+ }
+
+ public function leaveScope($name)
+ {
+ return $this->delegate->leaveScope($name);
+ }
+
+ public function loadFromExtension($extension, array $values = array())
+ {
+ return $this->delegate->loadFromExtension($extension, $values);
+ }
+
+ public function register($id, $class = null)
+ {
+ return $this->delegate->register($id, $class);
+ }
+
+ public function removeAlias($alias)
+ {
+ return $this->delegate->removeAlias($alias);
+ }
+
+ public function removeDefinition($id)
+ {
+ return $this->delegate->removeDefinition($id);
+ }
+
+ public function resolveServices($value)
+ {
+ return $this->delegate->resolveServices($value);
+ }
+
+ public function setAlias($alias, $id)
+ {
+ return $this->delegate->setAlias($alias, $id);
+ }
+
+ public function setAliases(array $aliases)
+ {
+ return $this->delegate->setAliases($aliases);
+ }
+
+ public function setDefinitions(array $definitions)
+ {
+ return $this->delegate->setDefinitions($definitions);
+ }
+
+ public function setParameter($name, $value)
+ {
+ return $this->delegate->setParameter($name, $value);
+ }
+}
View
45 src/Zikula/Bundle/ModuleBundle/DependencyInjection/SandboxContainerExtension.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Zikula\ModuleBundle\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+/**
+ *
+ */
+class SandboxContainerExtension implements ExtensionInterface
+{
+ /**
+ * @var ExtensionInterface
+ */
+ private $delegate;
+ private $serviceIds = array();
+
+ public function __construct($delegate, &$serviceIds)
+ {
+ $this->delegate = $delegate;
+ $this->serviceIds =& $serviceIds;
+ }
+
+ public function getAlias()
+ {
+ return $this->delegate->getAlias();
+ }
+
+ public function getNamespace()
+ {
+ return $this->delegate->getNamespace();
+ }
+
+ public function getXsdValidationBasePath()
+ {
+ return $this->delegate->getXsdValidationBasePath();
+ }
+
+ public function load(array $config, ContainerBuilder $container)
+ {
+ $this->delegate->load($config, new SandboxContainerBuilder($container, $this->serviceIds));
+ }
+
+}
View
28 src/Zikula/Bundle/ModuleBundle/DependencyInjection/ZikulaModulesExtension.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Zikula\ModuleBundle\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\Config\FileLocator;
+use Symfony\Component\HttpKernel\DependencyInjection\Extension;
+use Symfony\Component\DependencyInjection\Loader;
+
+/**
+ * This is the class that loads and manages your bundle configuration
+ *
+ * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
+ */
+class ZikulaModulesExtension extends Extension
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function load(array $configs, ContainerBuilder $container)
+ {
+ $configuration = new Configuration();
+ $config = $this->processConfiguration($configuration, $configs);
+
+ $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
+ $loader->load('services.xml');
+ }
+}
View
121 src/Zikula/Bundle/ModuleBundle/Entity/Module.php
@@ -0,0 +1,121 @@
+<?php
+
+namespace Zikula\ModuleBundle\Entity;
+
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * Zikula\ModuleBundle\Entity\Module
+ *
+ * @ORM\Table()
+ * @ORM\Entity
+ */
+class Module
+{
+ const STATE_NEW = 0;
+ const STATE_ACTIVE = 1;
+ const STATE_INACTIVE = 2;
+ const STATE_NEED_UPGRADE = 3;
+
+ /**
+ * @var integer $id
+ *
+ * @ORM\Column(name="id", type="integer")
+ * @ORM\Id
+ * @ORM\GeneratedValue(strategy="AUTO")
+ */
+ private $id;
+
+ /**
+ * @var string $class
+ *
+ * @ORM\Column(name="name", type="string", length=255)
+ */
+ private $name;
+
+ /**
+ * @var smallint $state
+ *
+ * @ORM\Column(name="state", type="smallint")
+ */
+ private $state;
+
+ /**
+ * @var string $class
+ *
+ * @ORM\Column(name="version", type="string", length=10)
+ */
+ private $version;
+
+
+
+ /**
+ * Get id
+ *
+ * @return integer
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * Set name
+ *
+ * @param string $name
+ */
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * Get name
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Set state
+ *
+ * @param smallint $state
+ */
+ public function setState($state)
+ {
+ $this->state = $state;
+ }
+
+ /**
+ * Get state
+ *
+ * @return smallint
+ */
+ public function getState()
+ {
+ return $this->state;
+ }
+
+ /**
+ * Set version
+ *
+ * @param string $version
+ */
+ public function setVersion($version)
+ {
+ $this->version = $version;
+ }
+
+ /**
+ * Get version
+ *
+ * @return string
+ */
+ public function getVersion()
+ {
+ return $this->version;
+ }
+}
View
46 src/Zikula/Bundle/ModuleBundle/Entity/Module.php~
@@ -0,0 +1,46 @@
+<?php
+
+namespace Zikula\ModulesBundle\Entity;
+
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * Zikula\ModulesBundle\Entity\Module
+ *
+ * @ORM\Table()
+ * @ORM\Entity
+ */
+class Module
+{
+ /**
+ * @var integer $id
+ *
+ * @ORM\Column(name="id", type="integer")
+ * @ORM\Id
+ * @ORM\GeneratedValue(strategy="AUTO")
+ */
+ private $id;
+
+ /**
+ * @var string $class
+ *
+ * @ORM\Column(name="name", type="string", length=255)
+ */
+ private $name;
+
+ /**
+ * @var smallint $state
+ *
+ * @ORM\Column(name="state", type="smallint")
+ */
+ private $state;
+
+ /**
+ * @var string $class
+ *
+ * @ORM\Column(name="version", type="string", length=10)
+ */
+ private $version;
+
+
+}
View
10 src/Zikula/Bundle/ModuleBundle/Exception/InvalidModuleStructureException.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace Zikula\ModuleBundle\Exception;
+
+/**
+ *
+ */
+class InvalidModuleStructureException extends \Exception {
+
+}
View
10 src/Zikula/Bundle/ModuleBundle/Exception/InvalidThemeStructureException.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace Zikula\ModuleBundle\Exception;
+
+/**
+ *
+ */
+class InvalidThemeStructureException extends \Exception {
+
+}
View
21 src/Zikula/Bundle/ModuleBundle/Form/ModuleType.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace Zikula\ModuleBundle\Form;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\FormBuilder;
+
+class ModuleType extends AbstractType
+{
+ public function buildForm(FormBuilder $builder, array $options)
+ {
+ $builder
+ ->add('name')
+ ;
+ }
+
+ public function getName()
+ {
+ return 'zikula_modulesbundle_moduletype';
+ }
+}
View
43 src/Zikula/Bundle/ModuleBundle/ModuleAwareControllerResolver.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace Zikula\ModuleBundle;
+
+use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ *
+ */
+class ModuleAwareControllerResolver implements ControllerResolverInterface
+{
+ /**
+ * @var ControllerResolverInterface
+ */
+ private $delegate;
+ private $kernel;
+
+ public function __construct($delegate, $kernel)
+ {
+ $this->kernel = $kernel;
+ $this->delegate = $delegate;
+ }
+
+ public function getArguments(Request $request, $controller)
+ {
+ return $this->delegate->getArguments($request, $controller);
+ }
+
+ public function getController(Request $request)
+ {
+ $controller = $this->delegate->getController($request);
+
+ if(is_array($controller) && is_object($controller[0])) {
+ if($this->kernel->isClassInModule(get_class($controller[0]))
+ && !$this->kernel->isClassInActiveModule(get_class($controller[0]))) {
+ $controller = false;
+ }
+ }
+
+ return $controller;
+ }
+}
View
39 src/Zikula/Bundle/ModuleBundle/ModuleAwareEventDispatcher.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Zikula\ModuleBundle;
+
+use Symfony\Bundle\FrameworkBundle\ContainerAwareEventDispatcher;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+
+/**
+ *
+ */
+class ModuleAwareEventDispatcher extends ContainerAwareEventDispatcher
+{
+ private $kernel;
+
+ public function __construct(ContainerInterface $container)
+ {
+ parent::__construct($container);
+
+ $this->kernel = $container->get('kernel');
+ }
+
+
+ public function addListenerService($eventName, $callback, $priority = 0)
+ {
+ if (!is_array($callback) || 2 !== count($callback)) {
+ throw new \InvalidArgumentException('Expected an array("service", "method") argument');
+ }
+
+ $bundle = $this->kernel->getBundleByServiceId($eventName[0]);
+
+ // skip inactive module bundles
+ if(!$bundle
+ || !$bundle instanceof ZikulaModule
+ || ($bundle instanceof ZikulaModule && $this->kernel->isModuleBundleActive($bundle))) {
+ parent::addListenerService($eventName, $callback, $priority);
+ }
+ }
+}
View
38 src/Zikula/Bundle/ModuleBundle/ModuleAwareTraceableEventDispatcher.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Zikula\ModuleBundle;
+
+use Symfony\Bundle\FrameworkBundle\Debug\TraceableEventDispatcher;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpKernel\Log\LoggerInterface;
+
+/**
+ *
+ */
+class ModuleAwareTraceableEventDispatcher extends TraceableEventDispatcher
+{
+ private $kernel;
+
+ public function __construct(ContainerInterface $container, LoggerInterface $logger = null)
+ {
+ parent::__construct($container, $logger);
+
+ $this->kernel = $container->get('kernel');
+ }
+
+ public function addListenerService($eventName, $callback, $priority = 0)
+ {
+ if (!is_array($callback) || 2 !== count($callback)) {
+ throw new \InvalidArgumentException('Expected an array("service", "method") argument');
+ }
+
+ $bundle = $this->kernel->getBundleByServiceId($callback[0]);
+
+ // skip inactive module bundles
+ if(!$bundle
+ || !$bundle instanceof ZikulaModule
+ || ($bundle instanceof ZikulaModule && $this->kernel->isModuleBundleActive($bundle))) {
+ parent::addListenerService($eventName, $callback, $priority);
+ }
+ }
+}
View
17 src/Zikula/Bundle/ModuleBundle/ModuleInstallerInterface.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Zikula\ModuleBundle;
+
+/**
+ *
+ */
+interface ModuleInstallerInterface
+{
+ public function entitiesToInstall();
+
+ public function install();
+
+ public function upgrade();
+
+ public function uninstall();
+}
View
43 src/Zikula/Bundle/ModuleBundle/ModuleService/DoctrineStorage.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace Zikula\ModuleBundle\ModuleService;
+
+use Doctrine\ORM\EntityManager;
+
+/**
+ * Doctrine based storage.
+ */
+class DoctrineStorage implements StorageInterface
+{
+ /**
+ * @var EntityManager
+ */
+ private $em;
+
+ public function __construct(EntityManager $em)
+ {
+ $this->em = $em;
+ }
+
+ public function getAll()
+ {
+ return $this->em->getRepository('ZikulaModulesBundle:Module')->findAll();
+ }
+
+ public function get($id)
+ {
+ return $this->em->find('ZikulaModulesBundle', $id);
+ }
+
+ public function insert(\Zikula\ModuleBundle\Entity\Module $module)
+ {
+ $this->em->persist($module);
+ $this->em->flush();
+ }
+
+ public function update(\Zikula\ModuleBundle\Entity\Module $module)
+ {
+ $this->em->persist($module);
+ $this->em->flush();
+ }
+}
View
24 src/Zikula/Bundle/ModuleBundle/ModuleService/StorageInterface.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Zikula\ModuleBundle\ModuleService;
+
+/**
+ * Interface of module metadata storages.
+ */
+interface StorageInterface
+{
+ /**
+ * @return \Zikula\ModuleBundle\Entity\Module[]
+ */
+ public function getAll();
+
+ /**
+ * @return \Zikula\ModuleBundle\Entity\Module
+ */
+ public function get($id);
+
+ public function insert(\Zikula\ModuleBundle\Entity\Module $module);
+
+ public function update(\Zikula\ModuleBundle\Entity\Module $module);
+}
+
View
14 src/Zikula/Bundle/ModuleBundle/ModuleServiceInterface.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace Zikula\ModuleBundle;
+
+/**
+ * A module service to install, upgrade and remove modules.
+ */
+interface ModuleServiceInterface {
+ public function regenerateModuleList();
+ public function getAllModules();
+ public function getModule($id);
+ public function installModule($id);
+}
+
View
32 src/Zikula/Bundle/ModuleBundle/Resources/config/services.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" ?>
+
+<container xmlns="http://symfony.com/schema/dic/services"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
+
+ <parameters>
+ <parameter key="event_dispatcher.class">Zikula\ModuleBundle\ModuleAwareEventDispatcher</parameter>
+ <parameter key="debug.event_dispatcher.class">Zikula\ModuleBundle\ModuleAwareTraceableEventDispatcher</parameter>
+ </parameters>
+
+ <services>
+ <service id="zikula_modules.routing.loader" class="Zikula\ModuleBundle\ZikulaModuleRoutesLoader" public="false">
+ <tag name="routing.loader" />
+ <argument type="service" id="kernel" />
+ </service>
+
+ <service id="zikula.modules.storage" class="Zikula\ModuleBundle\ModuleService\DoctrineStorage" public="false">
+ <argument type="service" id="doctrine.orm.default_entity_manager" />
+ </service>
+
+ <service id="zikula.modules" class="Zikula\ModuleBundle\DefaultModuleService">
+ <argument type="service" id="zikula.modules.storage" />
+ <argument type="service" id="doctrine.orm.default_entity_manager" />
+ </service>
+
+ <service id="zikula.controller.resolver" class="Zikula\ModuleBundle\ModuleAwareControllerResolver" public="false">
+ <argument type="service" id="controller_resolver" />
+ <argument type="service" id="kernel" />
+ </service>
+ </services>
+</container>
View
0  src/Zikula/Bundle/ModuleBundle/Resources/public/js/test.js
No changes.
View
1  src/Zikula/Bundle/ModuleBundle/Resources/views/Default/index.html.twig
@@ -0,0 +1 @@
+Hello {{ name }}!
View
22 src/Zikula/Bundle/ModuleBundle/Resources/views/Module/edit.html.twig
@@ -0,0 +1,22 @@
+<h1>Module edit</h1>
+
+<form action="{{ path('module_update', { 'id': entity.id }) }}" method="post" {{ form_enctype(edit_form) }}>
+ {{ form_widget(edit_form) }}
+ <p>
+ <button type="submit">Edit</button>
+ </p>
+</form>
+
+<ul class="record_actions">
+ <li>
+ <a href="{{ path('module') }}">
+ Back to the list
+ </a>
+ </li>
+ <li>
+ <form action="{{ path('module_delete', { 'id': entity.id }) }}" method="post">
+ {{ form_widget(delete_form) }}
+ <button type="submit">Delete</button>
+ </form>
+ </li>
+</ul>
View
33 src/Zikula/Bundle/ModuleBundle/Resources/views/Module/index.html.twig
@@ -0,0 +1,33 @@
+ <h1>Module list</h1>
+
+ <table class="records_list">
+ <thead>
+ <tr>
+ <th>Id</th>
+ <th>Name</th>
+ <th>State</th>
+ <th>Version</th>
+ <th>Actions</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for entity in entities %}
+ <tr>
+ <td><a href="{{ path('module_show', { 'id': entity.id }) }}">{{ entity.id }}</a></td>
+ <td>{{ entity.name }}</td>
+ <td>{{ entity.state }}</td>
+ <td>{{ entity.version }}</td>
+ <td>
+ <ul>
+ <li>
+ <a href="{{ path('module_show', { 'id': entity.id }) }}">show</a>
+ </li>
+ <li>
+ <a href="{{ path('module_edit', { 'id': entity.id }) }}">edit</a>
+ </li>
+ </ul>
+ </td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
View
16 src/Zikula/Bundle/ModuleBundle/Resources/views/Module/new.html.twig
@@ -0,0 +1,16 @@
+<h1>Module creation</h1>
+
+<form action="{{ path('module_create') }}" method="post" {{ form_enctype(form) }}>
+ {{ form_widget(form) }}
+ <p>
+ <button type="submit">Create</button>
+ </p>
+</form>
+
+<ul class="record_actions">
+ <li>
+ <a href="{{ path('module') }}">
+ Back to the list
+ </a>
+ </li>
+</ul>
View
41 src/Zikula/Bundle/ModuleBundle/Resources/views/Module/show.html.twig
@@ -0,0 +1,41 @@
+<h1>Module</h1>
+
+<table class="record_properties">
+ <tbody>
+ <tr>
+ <th>Id</th>
+ <td>{{ entity.id }}</td>
+ </tr>
+ <tr>
+ <th>Class</th>
+ <td>{{ entity.name }}</td>
+ </tr>
+ <tr>
+ <th>State</th>
+ <td>{{ entity.state }}</td>
+ </tr>
+ <tr>
+ <th>Version</th>
+ <td>{{ entity.version }}</td>
+ </tr>
+ </tbody>
+</table>
+
+<ul class="record_actions">
+ <li>
+ <a href="{{ path('module') }}">
+ Back to the list
+ </a>
+ </li>
+ <li>
+ <a href="{{ path('module_edit', { 'id': entity.id }) }}">
+ Edit
+ </a>
+ </li>
+ <li>
+ <form action="{{ path('module_delete', { 'id': entity.id }) }}" method="post">
+ {{ form_widget(delete_form) }}
+ <button type="submit">Delete</button>
+ </form>
+ </li>
+</ul>
View
17 src/Zikula/Bundle/ModuleBundle/Tests/Controller/DefaultControllerTest.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Zikula\ModuleBundle\Tests\Controller;
+
+use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
+
+class DefaultControllerTest extends WebTestCase
+{
+ public function testIndex()
+ {
+ $client = static::createClient();
+
+ $crawler = $client->request('GET', '/hello/Fabien');
+
+ $this->assertTrue($crawler->filter('html:contains("Hello Fabien")')->count() > 0);
+ }
+}
View
54 src/Zikula/Bundle/ModuleBundle/Tests/Controller/ModuleControllerTest.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Zikula\ModuleBundle\Tests\Controller;
+
+use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
+
+class ModuleControllerTest extends WebTestCase
+{
+ /*
+ public function testCompleteScenario()
+ {
+ // Create a new client to browse the application
+ $client = static::createClient();
+
+ // Create a new entry in the database
+ $crawler = $client->request('GET', '/module/');
+ $this->assertTrue(200 === $client->getResponse()->getStatusCode());
+ $crawler = $client->click($crawler->selectLink('Create a new entry')->link());
+
+ // Fill in the form and submit it
+ $form = $crawler->selectButton('Create')->form(array(
+ 'module[field_name]' => 'Test',
+ // ... other fields to fill
+ ));
+
+ $client->submit($form);
+ $crawler = $client->followRedirect();
+
+ // Check data in the show view
+ $this->assertTrue($crawler->filter('td:contains("Test")')->count() > 0);
+
+ // Edit the entity
+ $crawler = $client->click($crawler->selectLink('Edit')->link());
+
+ $form = $crawler->selectButton('Edit')->form(array(
+ 'module[field_name]' => 'Foo',
+ // ... other fields to fill
+ ));
+
+ $client->submit($form);
+ $crawler = $client->followRedirect();
+
+ // Check the element contains an attribute with value equals "Foo"
+ $this->assertTrue($crawler->filter('[value="Foo"]')->count() > 0);
+
+ // Delete the entity
+ $client->submit($crawler->selectButton('Delete')->form());
+ $crawler = $client->followRedirect();
+
+ // Check the entity has been delete on the list
+ $this->assertNotRegExp('/Foo/', $client->getResponse()->getContent());
+ }
+ */
+}
View
157 src/Zikula/Bundle/ModuleBundle/ZikulaKernel.php
@@ -0,0 +1,157 @@
+<?php
+
+namespace Zikula\ModuleBundle;
+
+use Symfony\Component\HttpKernel\Kernel;
+use Symfony\Component\Config\ConfigCache;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+abstract class ZikulaKernel extends Kernel{
+ const TYPE_MODUES = 'modules';
+ const TYPE_THEMES = 'themes';
+
+ private $zikulaBundles;
+ private $moduleServiceIds;
+
+ public function boot()
+ {
+ $modules = array();
+
+ $this->loadModuleBundlesFromFilesystem();
+ $this->loadThemeBundlesFromFilesystem();
+
+ parent::boot();
+ }
+
+ public function registerModuleBundles(&$bundles)
+ {
+ if($this->zikulaBundles) {
+ foreach($this->zikulaBundles as $type => $bundleList) {
+ foreach($bundleList as $bundle) {
+ $bundles[] = $bundle;
+ }
+ }
+ }
+ }
+
+ public function getZiklaBundlesOfType($type)
+ {
+ return $this->zikulaBundles[$type];
+ }
+
+ public function isClassInModule($class)
+ {
+ foreach ($this->getBundles() as $bundle) {
+ if (0 === strpos($class, $bundle->getNamespace())) {
+ return $bundle instanceof ZikulaModule;
+ }
+ }
+
+ return false;
+ }
+
+ public function isClassInActiveModule($class)
+ {
+ foreach ($this->getBundles() as $bundle) {
+ if (0 === strpos($class, $bundle->getNamespace())) {
+ $modules = $this->container->get('zikula.modules')->getAllModules();
+ $modules = array_filter($modules, function($m) use($bundle) { return $m->getName() == $bundle->getName(); });
+ $module = array_shift($modules);
+
+ return $module != null && $module->getState() == Entity\Module::STATE_ACTIVE;
+ }
+ }
+
+ return false;
+ }
+
+ public function isModuleBundleActive(ZikulaModule $bundle)
+ {
+ $modules = $this->container->get('zikula.modules')->getAllModules();
+ $modules = array_filter($modules, function($m) use($bundle) { return $m->getName() == $bundle->getName(); });
+ $module = array_shift($modules);
+
+ return $module != null && $module->getState() == Entity\Module::STATE_ACTIVE;
+ }
+
+ public function getBundleByServiceId($id) {
+ if(!is_array($this->moduleServiceIds)) {
+ $file = $this->getCacheDir().'/'.$this->getContainerClass().'.modules';
+
+ if(file_exists($file)) {
+ $this->moduleServiceIds = unserialize(file_get_contents($file));
+ } else {
+ $this->moduleServiceIds = array();
+ }
+ }
+
+ foreach($this->moduleServiceIds as $bundleName => $ids) {
+ if(in_array($id, $ids)) {
+ return $this->getBundle($bundleName);
+ }
+ }
+
+ return null;
+ }
+
+ protected function getKernelParameters()
+ {
+ $base = parent::getKernelParameters();
+ $new = array();
+ $toClassName = function($object) { return get_class($object); };
+
+ foreach($this->zikulaBundles as $type => $bundles) {
+ $new['kernel.zikula.' . $type] = array_map($toClassName, $bundles);
+ }
+
+ return array_merge($base, $new);
+ }
+
+ protected function dumpContainer(ConfigCache $cache, ContainerBuilder $container, $class, $baseClass)
+ {
+ parent::dumpContainer($cache, $container, $class, $baseClass);
+
+ $data = array();
+
+ foreach($this->zikulaBundles as $type => $bundles) {
+ foreach($bundles as $bundle) {
+ $data[$bundle->getName()] = $bundle->getServiceIds();
+ }
+ }
+
+ $cache = new \Symfony\Component\Config\ConfigCache($this->getCacheDir().'/'.$class.'.modules', $this->debug);
+ $cache->write(serialize($data));
+ }
+
+ private function loadModuleBundlesFromFilesystem()
+ {
+ $dirs = \Symfony\Component\Finder\Finder::create()
+ ->directories()->in($this->rootDir . '/../modules')->depth(0);
+
+ foreach($dirs as $dir) {
+ $class = sprintf('%s\\%sModule', $dir->getFilename(), $dir->getFilename());
+
+ if(!is_subclass_of($class, 'Zikula\ModuleBundle\ZikulaModule')) {
+ throw new Exception\InvalidModuleStructureException($dir->getFilename(). ' # '. $class);
+ }
+
+ $this->zikulaBundles[self::TYPE_MODUES][] = new $class();
+ }
+ }
+
+ private function loadThemeBundlesFromFilesystem()
+ {
+ $dirs = \Symfony\Component\Finder\Finder::create()
+ ->directories()->in($this->rootDir . '/../themes')->depth(0);
+
+ foreach($dirs as $dir) {
+ $class = sprintf('%s\\%sTheme', $dir->getFilename(), $dir->getFilename());
+
+ if(!is_subclass_of($class, 'Zikula\ThemesBundle\ZikulaTheme')) {
+ throw new Exception\InvalidThemeStructureException($dir->getFilename());
+ }
+
+ $this->zikulaBundles[self::TYPE_THEMES][] = new $class();
+ }
+ }
+}
View
46 src/Zikula/Bundle/ModuleBundle/ZikulaModule.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Zikula\ModuleBundle;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+/**
+ *
+ */
+abstract class ZikulaModule extends \Symfony\Component\HttpKernel\Bundle\Bundle {
+
+ private $serviceIds = array();
+
+ public function __construct() {
+ $name = get_class($this);
+ $posNamespaceSeperator = strrpos($name, '\\');
+ $this->name = substr($name, $posNamespaceSeperator + 1);
+ }
+
+ public abstract function getVersion();
+
+ /**
+ * @return ModuleInstallerInterface
+ */
+ public abstract function createInstaller();
+
+ final public function build(ContainerBuilder $container)
+ {
+ // modules have to use DI Extensions
+ }
+
+ final public function getContainerExtension()
+ {
+ $ex = parent::getContainerExtension();
+
+ if($ex != null) {
+ $ex = new DependencyInjection\SandboxContainerExtension($ex, $this->serviceIds);
+ }
+
+ return $ex;
+ }
+
+ public function getServiceIds() {
+ return $this->serviceIds;
+ }
+}
View
14 src/Zikula/Bundle/ModuleBundle/ZikulaModuleBundle.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace Zikula\ModuleBundle;
+
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+class ZikulaModulesBundle extends Bundle
+{
+ public function build(ContainerBuilder $container)
+ {
+ $container->addCompilerPass(new DependencyInjection\Compiler\ControllerResolverCompilerPass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION);
+ }
+}
View
31 src/Zikula/Bundle/ModuleBundle/ZikulaModuleRoutesLoader.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace Zikula\ModuleBundle;
+
+/**
+ *
+ */
+class ZikulaModuleRoutesLoader extends \Symfony\Component\Config\Loader\Loader {
+
+ private $zikulaKernel;
+
+ public function __construct($zikulaKernel) {
+ $this->zikulaKernel = $zikulaKernel;
+ }
+
+ public function load($resource, $type = null) {
+ $collection = new \Symfony\Component\Routing\RouteCollection();
+
+ foreach($this->zikulaKernel->getZiklaBundlesOfType(ZikulaKernel::TYPE_MODUES) as $moduleBundle) {
+ $resource = '@' . $moduleBundle->getName() . '/Controller/';
+ $subCollection = $this->resolve($resource)->load($resource, 'annotation');
+ $collection->addCollection($subCollection, '/' . str_replace('Module', '', $moduleBundle->getName()) . '/');
+ }
+
+ return $collection;
+ }
+
+ public function supports($resource, $type = null) {
+ return $type == 'zikulaModules';
+ }
+}
View
15 src/Zikula/Bundle/ThemeBundle/Controller/DefaultController.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Zikula\ThemeBundle\Controller;
+
+use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
+
+class DefaultController extends Controller
+{
+ public function welcomeAction()
+ {
+ return $this->render('ZikulaThemesBundle:Default:welcome.html.twig');
+ }
+}
View
29 src/Zikula/Bundle/ThemeBundle/DependencyInjection/Configuration.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Zikula\ThemeBundle\DependencyInjection;
+
+use Symfony\Component\Config\Definition\Builder\TreeBuilder;
+use Symfony\Component\Config\Definition\ConfigurationInterface;
+
+/**
+ * This is the class that validates and merges configuration from your app/config files
+ *
+ * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
+ */
+class Configuration implements ConfigurationInterface
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function getConfigTreeBuilder()
+ {
+ $treeBuilder = new TreeBuilder();
+ $rootNode = $treeBuilder->root('zikula_themes');
+
+ // Here you should define the parameters that are allowed to
+ // configure your bundle. See the documentation linked above for
+ // more information on that topic.
+
+ return $treeBuilder;
+ }
+}
View
28 src/Zikula/Bundle/ThemeBundle/DependencyInjection/ZikulaThemesExtension.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Zikula\ThemeBundle\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\Config\FileLocator;
+use Symfony\Component\HttpKernel\DependencyInjection\Extension;
+use Symfony\Component\DependencyInjection\Loader;
+
+/**
+ * This is the class that loads and manages your bundle configuration
+ *
+ * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
+ */
+class ZikulaThemesExtension extends Extension
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function load(array $configs, ContainerBuilder $container)
+ {
+ $configuration = new Configuration();
+ $config = $this->processConfiguration($configuration, $configs);
+
+ $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
+ $loader->load('services.xml');
+ }
+}
View
38 src/Zikula/Bundle/ThemeBundle/EventListener/ThemeRenderer.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Zikula\ThemeBundle\EventListener;
+
+use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
+
+/**
+ *
+ */
+class ThemeRenderer {
+ /**
+ * @var EngineInterface
+ */
+ private $templating;
+
+ private $activeTheme;
+
+ public function __construct(EngineInterface $templating) {
+ $this->templating = $templating;
+ $this->activeTheme = 'BlueprintTheme';
+ }
+
+ public function onKernelResponse(\Symfony\Component\HttpKernel\Event\FilterResponseEvent $event) {
+ if($event->getRequestType() == \Symfony\Component\HttpKernel\HttpKernelInterface::MASTER_REQUEST) {
+ $response = $event->getResponse();
+ $request = $event->getRequest();
+
+ if(!$request->isXmlHttpRequest()
+ && strpos($response->getContent(), '</body>') === false
+ && !$response->isRedirection()
+ && 'html' === $request->getRequestFormat()
+ && (($response->headers->has('Content-Type') && false !== strpos($response->headers->get('Content-Type'), 'html')) || !$response->headers->has('Content-Type') )) {
+ $content = $this->templating->render($this->activeTheme . '::base.html.twig', array('content' => $response->getContent()));
+ $response->setContent($content);
+ }
+ }
+ }
+}
View
13 src/Zikula/Bundle/ThemeBundle/Resources/config/services.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" ?>
+
+<container xmlns="http://symfony.com/schema/dic/services"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
+
+ <services>
+ <service id="zikula.themes.listener" class="Zikula\ThemeBundle\EventListener\ThemeRenderer">
+ <tag name="kernel.event_listener" event="kernel.response" method="onKernelResponse" priority="255" />
+ <argument type="service" id="templating" />
+ </service>
+ </services>
+</container>
View
5 src/Zikula/Bundle/ThemeBundle/Resources/views/Default/welcome.html.twig
@@ -0,0 +1,5 @@
+<h1>Homepage</h1>
+
+<p>
+Located in ZikulaThemesBundle.
+</p>
View
17 src/Zikula/Bundle/ThemeBundle/Tests/Controller/DefaultControllerTest.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Zikula\ThemeBundle\Tests\Controller;
+
+use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
+
+class DefaultControllerTest extends WebTestCase
+{
+ public function testIndex()
+ {
+ $client = static::createClient();
+
+ $crawler = $client->request('GET', '/hello/Fabien');
+
+ $this->assertTrue($crawler->filter('html:contains("Hello Fabien")')->count() > 0);
+ }
+}
View
24 src/Zikula/Bundle/ThemeBundle/ZikulaTheme.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Zikula\ThemeBundle;
+
+/**
+ *
+ */
+abstract class ZikulaTheme extends \Symfony\Component\HttpKernel\Bundle\Bundle
+{
+
+ public function __construct()
+ {
+ $name = get_class($this);
+ $posNamespaceSeperator = strrpos($name, '\\');
+ $this->name = substr($name, $posNamespaceSeperator + 1);
+ }
+
+ public abstract function getVersion();
+
+ public function getServiceIds()
+ {
+ return array();
+ }
+}
View
9 src/Zikula/Bundle/ThemeBundle/ZikulaThemeBundle.php
@@ -0,0 +1,9 @@
+<?php
+
+namespace Zikula\ThemeBundle;
+
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+
+class ZikulaThemesBundle extends Bundle
+{
+}

No commit comments for this range

Something went wrong with that request. Please try again.