* * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ namespace Chill\MainBundle\DependencyInjection\Widget; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder; use Chill\MainBundle\DependencyInjection\Widget\AbstractWidgetsCompilerPass as WidgetsCompilerPass; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; /** * This trait allow to add automatic configuration for widget inside your config. * * Usage * ====== * * 1. Register widget factories * ---------------------------- * * Add widget factories, using `setWidgetFactories` * * Example : * * ``` * use Symfony\Component\DependencyInjection\ContainerBuilder; * use Chill\MainBundle\DependencyInjection\Widget\AddWidgetConfigurationTrait; * * class MyConfig * { * * use addWidgetConfigurationTrait; * * public function __construct(array $widgetFactories = array(), ContainerBuilder $container) * { * $this->setWidgetFactories($widgetFactories); * // will be used on next step * $this->container = $container; * } * * } * ``` * * * * 2. add widget config to your config * ----------------------------------- * * ``` * use Symfony\Component\DependencyInjection\ContainerBuilder; * use Chill\MainBundle\DependencyInjection\Widget\AddWidgetConfigurationTrait; * use Symfony\Component\Config\Definition\Builder\TreeBuilder; * * class MyConfig * { * * use addWidgetConfigurationTrait; * * private $container; * * public function __construct(array $widgetFactories = array(), ContainerBuilder $container) * { * $this->setWidgetFactories($widgetFactories); * $this->container; * } * * public function getConfigTreeBuilder() * { * $treeBuilder = new TreeBuilder(); * $root = $treeBuilder->root('my_root'); * * $root->children() * ->arrayNode('widgets') * ->canBeDisabled() * ->children() * ->append($this->addWidgetsConfiguration('homepage', $this->container)) * ->end() * ->end() * ; * * return $treeBuilder; * } * * } * ``` * * the above code will add to the config : * * ``` * widgets: * enabled: true * * # register widgets on place "homepage" * homepage: * order: ~ # Required * widget_alias: ~ # One of "person_list"; "add_person", Required * person_list: * # options for the person_list * ``` * * */ trait AddWidgetConfigurationTrait { /** * @param WidgetFactoryInterface[] */ private $widgetFactories; /** * * @param WidgetFactoryInterface[] $widgetFactories */ public function setWidgetFactories(array $widgetFactories) { $this->widgetFactories = $widgetFactories; } /** * @return WidgetFactoryInterface[] */ public function getWidgetFactories() { return $this->widgetFactories; } /** * add configuration nodes for the widget at the given place. * * @param type $place * @param ContainerBuilder $containerBuilder * @return type */ protected function addWidgetsConfiguration($place, ContainerBuilder $containerBuilder) { $treeBuilder = new TreeBuilder($place); $root = $treeBuilder->getRootNode($place) ->canBeUnset() ->info('register widgets on place "'.$place.'"'); // if no childen, return the root if (count(iterator_to_array($this->filterWidgetByPlace($place))) === 0) { return $root; } $prototypeChildren = $root->prototype('array')->children(); $prototypeChildren ->floatNode(WidgetsCompilerPass::WIDGET_CONFIG_ORDER) ->isRequired() ->info("the ordering of the widget. May be a number with decimal") ->example("10.58") ->end() ->scalarNode(WidgetsCompilerPass::WIDGET_CONFIG_ALIAS) // this node is scalar: when the configuration is build, the // tagged services are not available. But when the config reference // is build, the services are avaialble => we add the possible aliases // in the info. ->info("the widget alias (see your installed bundles config). " . "Possible values are (maybe incomplete) : ". \implode(", ", $this->getWidgetAliasesbyPlace($place, $containerBuilder))) ->isRequired() ->end() ; // adding the possible config on each widget under the widget_alias foreach ($this->filterWidgetByPlace($place) as $factory) { $builder = new TreeBuilder($factory->getWidgetAlias()); $widgetOptionsRoot = $builder->getRootNode($factory->getWidgetAlias()); $widgetOptionsRoot->canBeUnset() ->info(sprintf('the configuration for the widget "%s" (only required if this widget is set in widget_alias)', $factory->getWidgetAlias())); $factory->configureOptions($place, $widgetOptionsRoot->children()); $prototypeChildren->append($widgetOptionsRoot); } return $root; } /** * get all widget factories for the given place. * * @param string $place * @return \Generator a generator containing a widget factory */ protected function filterWidgetByPlace($place) { foreach($this->widgetFactories as $factory) { if (in_array($place, $factory->getAllowedPlaces())) { yield $factory; } } } /** * get the all possible aliases for the given place. This method * search within service tags and widget factories * * **Note** that services are not available when the config is build: the whole * aliases will be checked in compiler pass, or when the command * `config:dump-reference` is runned. * * @param type $place * @param ContainerBuilder $containerBuilder * @return type * @throws InvalidConfigurationException if a service's tag does not have the "alias" key */ protected function getWidgetAliasesbyPlace($place, ContainerBuilder $containerBuilder) { $result = array(); foreach ($this->filterWidgetByPlace($place) as $factory) { $result[] = $factory->getWidgetAlias(); } // append the aliases added without factory foreach ($containerBuilder ->findTaggedServiceIds(WidgetsCompilerPass::WIDGET_SERVICE_TAG_NAME) as $serviceId => $tags) { foreach ($tags as $tag) { // throw an error if no alias in definition if (!array_key_exists(WidgetsCompilerPass::WIDGET_SERVICE_TAG_ALIAS, $tag)) { throw new InvalidConfigurationException(sprintf( "The service with id %s does not have any %d key", $serviceId, WidgetsCompilerPass::WIDGET_SERVICE_TAG_ALIAS )); } // add the key to the possible results $result[] = $tag[WidgetsCompilerPass::WIDGET_SERVICE_TAG_ALIAS]; } } return $result; } }